* [RFC/PATCH] mmc: omap_hsmmc: introduce omap_hsmmc_prepare/complete
From: Venkatraman S @ 2012-11-06 15:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1350910768-8522-1-git-send-email-balbi@ti.com>
On Mon, Oct 22, 2012 at 6:29 PM, Felipe Balbi <balbi@ti.com> wrote:
> prepare() is supposed to prevent new children from
> being registered. On the MMC subsystem, children
> (new cards) registration starts with the card
> detect IRQ.
>
> Move card detect IRQ disabling to prepare() so that
> no new cards will be registered while we're trying
> to suspend.
>
> Likewise, move card detect IRQ enabling to complete()
> so we only try to register new children after our MMC
> IP is back up.
>
> Signed-off-by: Felipe Balbi <balbi@ti.com>
> ---
>
> Venkat, do you think the patch below makes sense ?
Except for the minor nit mentioned below, it works well.
I'll include it in my patch series.
>
> cheers
>
> drivers/mmc/host/omap_hsmmc.c | 45 ++++++++++++++++++++++++-------------------
> 1 file changed, 25 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> index 54bfd0c..4c8b41e 100644
> --- a/drivers/mmc/host/omap_hsmmc.c
> +++ b/drivers/mmc/host/omap_hsmmc.c
> @@ -2022,6 +2022,26 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
> }
>
> #ifdef CONFIG_PM
> +static int omap_hsmmc_prepare(struct device *dev)
> +{
> + struct omap_hsmmc_host *host = dev_get_drvdata(dev);
> +
> + if (host->pdata->suspend)
> + return host->pdata->suspend(dev, host->slot_id);
> +
> + return 0;
> +}
> +
> +static int omap_hsmmc_complete(struct device *dev)
The .complete() function prototype requires to return void.
This one emits a compilation warning.
^ permalink raw reply
* [GIT PULL] arm-soc: Xilinx zynq changes for v3.8
From: Olof Johansson @ 2012-11-06 15:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAHTX3dLdGie9gtxbPq_gb8BLao7YYohqQWZFg3LzyNHxu=imdg@mail.gmail.com>
On Mon, Nov 05, 2012 at 12:09:10PM +0100, Michal Simek wrote:
> Hi Arnd and Olof,
>
> please add these patches to your arm-soc trees. All these patches are
> for xilinx zynq arm platform.
> You shouldn't have any problem to merge it there are no conficts with v3.7-rc4.
>
> Please let me know if you find out any problem.
>
> Thanks,
> Michal
>
> The following changes since commit 8f0d8163b50e01f398b14bcd4dc039ac5ab18d64:
> Linus Torvalds (1):
> Linux 3.7-rc3
>
> are available in the git repository at:
>
> git://git.xilinx.com/linux-xlnx.git arm-next
Thanks, pulled into next/cleanup (local version in zynq/cleanup).
-Olof
^ permalink raw reply
* [PATCH 12/15] ARM: OMAP: timer: Add suspend-resume callbacks for clockevent device
From: Jon Hunter @ 2012-11-06 16:00 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <B5906170F1614E41A8A28DE3B8D121433EC045C5@DBDE01.ent.ti.com>
On 11/06/2012 01:32 AM, Bedia, Vaibhav wrote:
> Hi Jon,
>
> On Tue, Nov 06, 2012 at 02:34:05, Hunter, Jon wrote:
> [...]
>>> static struct clock_event_device clockevent_gpt = {
>>> .name = "gp_timer",
>>> .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
>>> @@ -142,6 +171,8 @@ static struct clock_event_device clockevent_gpt = {
>>> .rating = 300,
>>> .set_next_event = omap2_gp_timer_set_next_event,
>>> .set_mode = omap2_gp_timer_set_mode,
>>> + .suspend = omap_clkevt_suspend,
>>> + .resume = omap_clkevt_resume,
>>
>> So these suspend/resume callbacks are going to be called for all OMAP2+
>> and AMxxxx devices? I don't think we want that. AFAIK OMAP timers will
>> idle on their own when stopped and don't require this.
>>
>
> IMO instead of skipping the callback registration we could have checks in the
> suspend/resume callbacks to decide what to do.
>
> I'll check if the idling part is AM33xx specific. If not, based on the recent timer
> changes that you did, perhaps checking if the clockevent selected doesn't have the
> "ti,timer-alwon" capability will be good enough. What do you think?
Yes, I was thinking along the same lines. If I get chance I will try and
test your scenario on an OMAP3 too.
Cheers
Jon
^ permalink raw reply
* [PATCH 12/15] ARM: OMAP: timer: Add suspend-resume callbacks for clockevent device
From: Jon Hunter @ 2012-11-06 16:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <B5906170F1614E41A8A28DE3B8D121433EC04897@DBDE01.ent.ti.com>
On 11/06/2012 03:38 AM, Bedia, Vaibhav wrote:
> Hi Jon,
>
> On Tue, Nov 06, 2012 at 02:50:50, Hunter, Jon wrote:
> [...]
>>
>> Why is this? How is the dmtimer TIOCP_CFG register configured on AM33xx?
>> Is it using smart-idle?
>>
>
> Yes, it is set to smart-idle with wakeup capable mode. (this needs a fixup
> since this timer is not wakeup capable) but unfortunately this is not
> sufficient. On AM33xx there's no HW_AUTO mode magic so unless the IPs in
> PER domain are disabled by s/w, PER domain can't transition.
>
>>> The next one is that
>>> the clockevent doesn't generate any further interrupts once the
>>> system resumes. We need to restore the pre-suspend configuration.
>>> I haven't tried but I guess we could have used the save and restore
>>> of timer registers here.
>>
>> It would be interesting to try using an non-wakeup domain timer on
>> OMAP3/4 for clock events and seeing if suspend/resume works.
>>
>> Do you know what the exact problem here is? I understand that the timer
>> context could get lost, but exactly what is not getting restarted by the
>> kernel? For example, the only place we set the interrupt enable is
>> during the clock event init and so if the context is lost, then I could
>> see no more interrupts occurring. So is it enough to just restore the
>> interrupt enable register, do you really need to program the timer again?
>>
>
> Just restoring the interrupt enable register works. But since there's no logic
> retention I think a context save-restore would be better.
Ok, we may need to check the order in which events occur following
resume. The kernel will restart the clock-events and we just need to
make sure we do not restore the context after the clock-events has been
restarted.
Cheers
Jon
^ permalink raw reply
* 房事时间短原因?
From: fg-hijkl29@163.com @ 2012-11-06 16:09 UTC (permalink / raw)
To: linux-arm-kernel
An HTML attachment was scrubbed...
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20121107/34d7a754/attachment.html>
^ permalink raw reply
* [GIT PULL 2/6] DaVinci cleanup for v3.8
From: Olof Johansson @ 2012-11-06 16:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351620701-16516-2-git-send-email-nsekhar@ti.com>
On Tue, Oct 30, 2012 at 11:41:37PM +0530, Sekhar Nori wrote:
> The following changes since commit 6f0c0580b70c89094b3422ba81118c7b959c7556:
>
> Linux 3.7-rc2 (2012-10-20 12:11:32 -0700)
>
> are available in the git repository at:
>
> git://gitorious.org/linux-davinci/linux-davinci.git tags/davinci-for-v3.8/cleanup
Pulled, but would you mind moving your tree to a better git hosting service?
It takes _forever_ to do a pull from gitorious. Do you have a signed
PGP key so you can get a kernel.org account, per chance?
-Olof
>
> for you to fetch changes up to baad1b33f4d86d223d665fcefa79b4acc832a269:
>
> ARM: davinci: board-dm646x-evm.c: Remove unecessary semicolon (2012-10-26 13:59:01 +0530)
>
> ----------------------------------------------------------------
> These changes make minor clean-ups to remove some
> unused MUSB defines and an unnecessary semicolon.
>
> ----------------------------------------------------------------
> Constantine Shulyupin (1):
> ARM: davinci: usb: remove CONFIG_USB_MUSB_HOST etc
>
> Peter Senna Tschudin (1):
> ARM: davinci: board-dm646x-evm.c: Remove unecessary semicolon
>
> arch/arm/mach-davinci/board-dm646x-evm.c | 2 +-
> arch/arm/mach-davinci/usb.c | 6 ------
> 2 files changed, 1 insertion(+), 7 deletions(-)
^ permalink raw reply
* OMAP baseline test results for v3.7-rc1
From: Paul Walmsley @ 2012-11-06 16:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAORVsuUm4zKoSaZQ0D=gf3toKHWznjfNvQ+q4=jsgkRaJXygMg@mail.gmail.com>
On Tue, 6 Nov 2012, Jean Pihet wrote:
> On Tue, Nov 6, 2012 at 1:01 AM, Kevin Hilman
> <khilman@deeprootsystems.com> wrote:
>
> > FYI... I just ran across what appears to be the same bug on 3430/n900
> > during suspend/resume testing. With CPUidle enabled, this happens every
> > time.
> >
> > Reverting the I2C QoS patch makes it work again.
>
> That makes sense with CPU_IDLE enabled and suspend.
...
> For the records here are the patches that have been submitted to
> support the OMAP PM QoS:
> - func power states [1],
> - OMAP PM QoS support [2], which includes the latency figures update
> and which depends on [1],
> - the conversion of I2C to PM QoS [3],
> - the removal of the old no-op OMAP PM API [4], which depends on [3],
>
> The chicken-and-egg problem is clearly visible since [3] depends on [2].
Well it's definitely time for a revert, then. #3 should not have been
sent until #2 was merged. Will send this in a new thread, please ack it.
- Paul
^ permalink raw reply
* [PATCH] gpiolib: iron out include ladder mistakes
From: Linus Walleij @ 2012-11-06 16:21 UTC (permalink / raw)
To: linux-arm-kernel
From: Linus Walleij <linus.walleij@linaro.org>
The <*/gpio.h> includes are updated again: now we need to account
for the problem introduced by commit:
595679a8038584df7b9398bf34f61db3c038bfea
"gpiolib: fix up function prototypes etc"
Actually we need static inlines in include/asm-generic/gpio.h
as well since we may have GPIOLIB but not PINCTRL.
And we need to keep the static inlines in <linux/gpio.h>
but here for the !CONFIG_GENERIC_GPIO case, and then we
may as well throw in a few warnings like the other
prototypes there, if someone would have the bad taste
of compiling without GENERIC_GPIO even.
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
include/asm-generic/gpio.h | 13 +++++++++++++
include/linux/gpio.h | 7 +++----
2 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 50d995e..b546edb 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -67,6 +67,19 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
unsigned int pin_base, unsigned int npins);
void gpiochip_remove_pin_ranges(struct gpio_chip *chip);
+#else
+
+static inline int
+gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+ unsigned int pin_base, unsigned int npins)
+{
+}
+
+static inline void
+gpiochip_remove_pin_ranges(struct gpio_chip *chip)
+{
+}
+
#endif
/**
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 81bbfe5..7ba2762 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -231,21 +231,20 @@ static inline int irq_to_gpio(unsigned irq)
return -EINVAL;
}
-#ifdef CONFIG_PINCTRL
-
static inline int
gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
unsigned int pin_base, unsigned int npins)
{
+ WARN_ON(1);
+ return -EINVAL;
}
static inline void
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
{
+ WARN_ON(1);
}
-#endif /* CONFIG_PINCTRL */
-
#endif /* ! CONFIG_GENERIC_GPIO */
#endif /* __LINUX_GPIO_H */
--
1.7.11.3
^ permalink raw reply related
* [PATCH] i2c: omap: Move the remove constraint
From: Jean Pihet @ 2012-11-06 16:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1352195673-31559-1-git-send-email-shubhrajyoti@ti.com>
Hi Shubhrajyoti,
On Tue, Nov 6, 2012 at 10:54 AM, Shubhrajyoti D <shubhrajyoti@ti.com> wrote:
> Currently we just queue the transfer and release the
> qos constraints, however we donot wait for the transfer
> to complete to release the constraint. Move the remove
> constraint after the bus busy as we are sure that the
> transfers are completed by then.
>
> Signed-off-by: Shubhrajyoti D <shubhrajyoti@ti.com>
Good catch, the change definitely makes sense. Feel free to add:
Acked-by: Jean Pihet <j-pihet@ti.com>
Thanks,
Jean.
> ---
> drivers/i2c/busses/i2c-omap.c | 6 +++---
> 1 files changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
> index 94ff685..8b079d7 100644
> --- a/drivers/i2c/busses/i2c-omap.c
> +++ b/drivers/i2c/busses/i2c-omap.c
> @@ -655,13 +655,13 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
> break;
> }
>
> - if (dev->latency)
> - pm_qos_remove_request(&dev->pm_qos_request);
> -
> if (r == 0)
> r = num;
>
> omap_i2c_wait_for_bb(dev);
> +
> + if (dev->latency)
> + pm_qos_remove_request(&dev->pm_qos_request);
> out:
> pm_runtime_mark_last_busy(dev->dev);
> pm_runtime_put_autosuspend(dev->dev);
> --
> 1.7.5.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH 1/5] ARM: AT91SAM9G45: fix crypto peripherals irq issue due to sparse irq support.
From: Nicolas Royer @ 2012-11-06 16:31 UTC (permalink / raw)
To: linux-arm-kernel
Spare irq support introduced by commit "8fe82a5 ARM: at91: sparse irq support"
involves to add the NR_IRQS_LEGACY offset to irq number.
Signed-off-by: Nicolas Royer <nicolas@eukrea.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Eric B?nard <eric@eukrea.com>
Tested-by: Eric B?nard <eric@eukrea.com>
Cc: stable at vger.kernel.org # 3.6
---
arch/arm/mach-at91/at91sam9g45_devices.c | 12 ++++++------
1 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
index b159607..fcd233c 100644
--- a/arch/arm/mach-at91/at91sam9g45_devices.c
+++ b/arch/arm/mach-at91/at91sam9g45_devices.c
@@ -1841,8 +1841,8 @@ static struct resource sha_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = AT91SAM9G45_ID_AESTDESSHA,
- .end = AT91SAM9G45_ID_AESTDESSHA,
+ .start = NR_IRQS_LEGACY + AT91SAM9G45_ID_AESTDESSHA,
+ .end = NR_IRQS_LEGACY + AT91SAM9G45_ID_AESTDESSHA,
.flags = IORESOURCE_IRQ,
},
};
@@ -1874,8 +1874,8 @@ static struct resource tdes_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = AT91SAM9G45_ID_AESTDESSHA,
- .end = AT91SAM9G45_ID_AESTDESSHA,
+ .start = NR_IRQS_LEGACY + AT91SAM9G45_ID_AESTDESSHA,
+ .end = NR_IRQS_LEGACY + AT91SAM9G45_ID_AESTDESSHA,
.flags = IORESOURCE_IRQ,
},
};
@@ -1910,8 +1910,8 @@ static struct resource aes_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = AT91SAM9G45_ID_AESTDESSHA,
- .end = AT91SAM9G45_ID_AESTDESSHA,
+ .start = NR_IRQS_LEGACY + AT91SAM9G45_ID_AESTDESSHA,
+ .end = NR_IRQS_LEGACY + AT91SAM9G45_ID_AESTDESSHA,
.flags = IORESOURCE_IRQ,
},
};
--
1.7.6.5
^ permalink raw reply related
* [PATCH] Revert "ARM: OMAP: convert I2C driver to PM QoS for MPU latency constraints"
From: Paul Walmsley @ 2012-11-06 16:31 UTC (permalink / raw)
To: linux-arm-kernel
This reverts commit 3db11feffc1ad2ab9dea27789e6b5b3032827adc.
This commit causes I2C timeouts to appear on several OMAP3430/3530-based
boards:
http://marc.info/?l=linux-arm-kernel&m=135071372426971&w=2
http://marc.info/?l=linux-arm-kernel&m=135067558415214&w=2
http://marc.info/?l=linux-arm-kernel&m=135216013608196&w=2
and appears to have been sent for merging before one of its prerequisites
was merged:
http://marc.info/?l=linux-arm-kernel&m=135219411617621&w=2
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Cc: Jean Pihet <jean.pihet@newoldbits.com>
Cc: Kevin Hilman <khilman@ti.com>
Cc: Aaro Koskinen <aaro.koskinen@iki.fi>
Cc: Felipe Balbi <balbi@ti.com>
---
Intended for 3.7-rc.
arch/arm/plat-omap/i2c.c | 21 +++++++++++++++++++++
drivers/i2c/busses/i2c-omap.c | 32 ++++++++++++++------------------
include/linux/i2c-omap.h | 1 +
3 files changed, 36 insertions(+), 18 deletions(-)
diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c
index a5683a8..6013831 100644
--- a/arch/arm/plat-omap/i2c.c
+++ b/arch/arm/plat-omap/i2c.c
@@ -26,12 +26,14 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
+#include <linux/i2c-omap.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <mach/irqs.h>
#include <plat/i2c.h>
+#include <plat/omap-pm.h>
#include <plat/omap_device.h>
#define OMAP_I2C_SIZE 0x3f
@@ -127,6 +129,16 @@ static inline int omap1_i2c_add_bus(int bus_id)
#ifdef CONFIG_ARCH_OMAP2PLUS
+/*
+ * XXX This function is a temporary compatibility wrapper - only
+ * needed until the I2C driver can be converted to call
+ * omap_pm_set_max_dev_wakeup_lat() and handle a return code.
+ */
+static void omap_pm_set_max_mpu_wakeup_lat_compat(struct device *dev, long t)
+{
+ omap_pm_set_max_mpu_wakeup_lat(dev, t);
+}
+
static inline int omap2_i2c_add_bus(int bus_id)
{
int l;
@@ -158,6 +170,15 @@ static inline int omap2_i2c_add_bus(int bus_id)
dev_attr = (struct omap_i2c_dev_attr *)oh->dev_attr;
pdata->flags = dev_attr->flags;
+ /*
+ * When waiting for completion of a i2c transfer, we need to
+ * set a wake up latency constraint for the MPU. This is to
+ * ensure quick enough wakeup from idle, when transfer
+ * completes.
+ * Only omap3 has support for constraints
+ */
+ if (cpu_is_omap34xx())
+ pdata->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat_compat;
pdev = omap_device_build(name, bus_id, oh, pdata,
sizeof(struct omap_i2c_bus_platform_data),
NULL, 0, 0);
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index db31eae..0b02543 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -43,7 +43,6 @@
#include <linux/slab.h>
#include <linux/i2c-omap.h>
#include <linux/pm_runtime.h>
-#include <linux/pm_qos.h>
/* I2C controller revisions */
#define OMAP_I2C_OMAP1_REV_2 0x20
@@ -187,8 +186,9 @@ struct omap_i2c_dev {
int reg_shift; /* bit shift for I2C register addresses */
struct completion cmd_complete;
struct resource *ioarea;
- u32 latency; /* maximum MPU wkup latency */
- struct pm_qos_request pm_qos_request;
+ u32 latency; /* maximum mpu wkup latency */
+ void (*set_mpu_wkup_lat)(struct device *dev,
+ long latency);
u32 speed; /* Speed of bus in kHz */
u32 dtrev; /* extra revision from DT */
u32 flags;
@@ -494,7 +494,9 @@ static void omap_i2c_resize_fifo(struct omap_i2c_dev *dev, u8 size, bool is_rx)
dev->b_hw = 1; /* Enable hardware fixes */
/* calculate wakeup latency constraint for MPU */
- dev->latency = (1000000 * dev->threshold) / (1000 * dev->speed / 8);
+ if (dev->set_mpu_wkup_lat != NULL)
+ dev->latency = (1000000 * dev->threshold) /
+ (1000 * dev->speed / 8);
}
/*
@@ -629,16 +631,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
if (r < 0)
goto out;
- /*
- * When waiting for completion of a i2c transfer, we need to
- * set a wake up latency constraint for the MPU. This is to
- * ensure quick enough wakeup from idle, when transfer
- * completes.
- */
- if (dev->latency)
- pm_qos_add_request(&dev->pm_qos_request,
- PM_QOS_CPU_DMA_LATENCY,
- dev->latency);
+ if (dev->set_mpu_wkup_lat != NULL)
+ dev->set_mpu_wkup_lat(dev->dev, dev->latency);
for (i = 0; i < num; i++) {
r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
@@ -646,8 +640,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
break;
}
- if (dev->latency)
- pm_qos_remove_request(&dev->pm_qos_request);
+ if (dev->set_mpu_wkup_lat != NULL)
+ dev->set_mpu_wkup_lat(dev->dev, -1);
if (r == 0)
r = num;
@@ -1104,6 +1098,7 @@ omap_i2c_probe(struct platform_device *pdev)
} else if (pdata != NULL) {
dev->speed = pdata->clkrate;
dev->flags = pdata->flags;
+ dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
dev->dtrev = pdata->rev;
}
@@ -1159,8 +1154,9 @@ omap_i2c_probe(struct platform_device *pdev)
dev->b_hw = 1; /* Enable hardware fixes */
/* calculate wakeup latency constraint for MPU */
- dev->latency = (1000000 * dev->fifo_size) /
- (1000 * dev->speed / 8);
+ if (dev->set_mpu_wkup_lat != NULL)
+ dev->latency = (1000000 * dev->fifo_size) /
+ (1000 * dev->speed / 8);
}
/* reset ASAP, clearing any IRQs */
diff --git a/include/linux/i2c-omap.h b/include/linux/i2c-omap.h
index df804ba..92a0dc7 100644
--- a/include/linux/i2c-omap.h
+++ b/include/linux/i2c-omap.h
@@ -34,6 +34,7 @@ struct omap_i2c_bus_platform_data {
u32 clkrate;
u32 rev;
u32 flags;
+ void (*set_mpu_wkup_lat)(struct device *dev, long set);
};
#endif
--
1.7.10.4
^ permalink raw reply related
* [PATCH 2/5] ARM: AT91SAM9G45: same platform data structure for all crypto peripherals
From: Nicolas Royer @ 2012-11-06 16:32 UTC (permalink / raw)
To: linux-arm-kernel
Only AES use DMA in AT91SAM9G45 (TDES and SHA use PDC).
However latest Atmel TDES and SHA IP releases use DMA instead of PDC.
--> Atmel TDES and SHA drivers need DMA platform data for those IP releases.
Goal of this patch is to use the same platform data structure for all Atmel
crypto peripherals. This structure contains information about DMA interface.
Signed-off-by: Nicolas Royer <nicolas@eukrea.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Eric B?nard <eric@eukrea.com>
Tested-by: Eric B?nard <eric@eukrea.com>
---
arch/arm/mach-at91/at91sam9g45_devices.c | 8 ++++----
include/linux/platform_data/atmel-aes.h | 22 ----------------------
include/linux/platform_data/crypto-atmel.h | 22 ++++++++++++++++++++++
3 files changed, 26 insertions(+), 26 deletions(-)
delete mode 100644 include/linux/platform_data/atmel-aes.h
create mode 100644 include/linux/platform_data/crypto-atmel.h
diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
index fcd233c..a48dcb4 100644
--- a/arch/arm/mach-at91/at91sam9g45_devices.c
+++ b/arch/arm/mach-at91/at91sam9g45_devices.c
@@ -18,7 +18,7 @@
#include <linux/platform_device.h>
#include <linux/i2c-gpio.h>
#include <linux/atmel-mci.h>
-#include <linux/platform_data/atmel-aes.h>
+#include <linux/platform_data/crypto-atmel.h>
#include <linux/platform_data/at91_adc.h>
@@ -1900,7 +1900,7 @@ static void __init at91_add_device_tdes(void) {}
* -------------------------------------------------------------------- */
#if defined(CONFIG_CRYPTO_DEV_ATMEL_AES) || defined(CONFIG_CRYPTO_DEV_ATMEL_AES_MODULE)
-static struct aes_platform_data aes_data;
+static struct crypto_platform_data aes_data;
static u64 aes_dmamask = DMA_BIT_MASK(32);
static struct resource aes_resources[] = {
@@ -1931,9 +1931,9 @@ static struct platform_device at91sam9g45_aes_device = {
static void __init at91_add_device_aes(void)
{
struct at_dma_slave *atslave;
- struct aes_dma_data *alt_atslave;
+ struct crypto_dma_data *alt_atslave;
- alt_atslave = kzalloc(sizeof(struct aes_dma_data), GFP_KERNEL);
+ alt_atslave = kzalloc(sizeof(struct crypto_dma_data), GFP_KERNEL);
/* DMA TX slave channel configuration */
atslave = &alt_atslave->txdata;
diff --git a/include/linux/platform_data/atmel-aes.h b/include/linux/platform_data/atmel-aes.h
deleted file mode 100644
index ab68082..0000000
--- a/include/linux/platform_data/atmel-aes.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef __LINUX_ATMEL_AES_H
-#define __LINUX_ATMEL_AES_H
-
-#include <linux/platform_data/dma-atmel.h>
-
-/**
- * struct aes_dma_data - DMA data for AES
- */
-struct aes_dma_data {
- struct at_dma_slave txdata;
- struct at_dma_slave rxdata;
-};
-
-/**
- * struct aes_platform_data - board-specific AES configuration
- * @dma_slave: DMA slave interface to use in data transfers.
- */
-struct aes_platform_data {
- struct aes_dma_data *dma_slave;
-};
-
-#endif /* __LINUX_ATMEL_AES_H */
diff --git a/include/linux/platform_data/crypto-atmel.h b/include/linux/platform_data/crypto-atmel.h
new file mode 100644
index 0000000..b46e0d9
--- /dev/null
+++ b/include/linux/platform_data/crypto-atmel.h
@@ -0,0 +1,22 @@
+#ifndef __LINUX_CRYPTO_ATMEL_H
+#define __LINUX_CRYPTO_ATMEL_H
+
+#include <linux/platform_data/dma-atmel.h>
+
+/**
+ * struct crypto_dma_data - DMA data for AES/TDES/SHA
+ */
+struct crypto_dma_data {
+ struct at_dma_slave txdata;
+ struct at_dma_slave rxdata;
+};
+
+/**
+ * struct crypto_platform_data - board-specific AES/TDES/SHA configuration
+ * @dma_slave: DMA slave interface to use in data transfers.
+ */
+struct crypto_platform_data {
+ struct crypto_dma_data *dma_slave;
+};
+
+#endif /* __LINUX_CRYPTO_ATMEL_H */
--
1.7.6.5
^ permalink raw reply related
* [PATCH] Revert "ARM: OMAP: convert I2C driver to PM QoS for MPU latency constraints"
From: Jean Pihet @ 2012-11-06 16:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <alpine.DEB.2.00.1211061619461.20573@utopia.booyaka.com>
On Tue, Nov 6, 2012 at 5:31 PM, Paul Walmsley <paul@pwsan.com> wrote:
>
> This reverts commit 3db11feffc1ad2ab9dea27789e6b5b3032827adc.
> This commit causes I2C timeouts to appear on several OMAP3430/3530-based
> boards:
>
> http://marc.info/?l=linux-arm-kernel&m=135071372426971&w=2
> http://marc.info/?l=linux-arm-kernel&m=135067558415214&w=2
> http://marc.info/?l=linux-arm-kernel&m=135216013608196&w=2
>
> and appears to have been sent for merging before one of its prerequisites
> was merged:
>
> http://marc.info/?l=linux-arm-kernel&m=135219411617621&w=2
Indeed.
Acked-by: Jean Pihet <j-pihet@ti.com>
>
> Signed-off-by: Paul Walmsley <paul@pwsan.com>
> Cc: Jean Pihet <jean.pihet@newoldbits.com>
> Cc: Kevin Hilman <khilman@ti.com>
> Cc: Aaro Koskinen <aaro.koskinen@iki.fi>
> Cc: Felipe Balbi <balbi@ti.com>
> ---
>
> Intended for 3.7-rc.
>
> arch/arm/plat-omap/i2c.c | 21 +++++++++++++++++++++
> drivers/i2c/busses/i2c-omap.c | 32 ++++++++++++++------------------
> include/linux/i2c-omap.h | 1 +
> 3 files changed, 36 insertions(+), 18 deletions(-)
>
> diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c
> index a5683a8..6013831 100644
> --- a/arch/arm/plat-omap/i2c.c
> +++ b/arch/arm/plat-omap/i2c.c
> @@ -26,12 +26,14 @@
> #include <linux/kernel.h>
> #include <linux/platform_device.h>
> #include <linux/i2c.h>
> +#include <linux/i2c-omap.h>
> #include <linux/slab.h>
> #include <linux/err.h>
> #include <linux/clk.h>
>
> #include <mach/irqs.h>
> #include <plat/i2c.h>
> +#include <plat/omap-pm.h>
> #include <plat/omap_device.h>
>
> #define OMAP_I2C_SIZE 0x3f
> @@ -127,6 +129,16 @@ static inline int omap1_i2c_add_bus(int bus_id)
>
>
> #ifdef CONFIG_ARCH_OMAP2PLUS
> +/*
> + * XXX This function is a temporary compatibility wrapper - only
> + * needed until the I2C driver can be converted to call
> + * omap_pm_set_max_dev_wakeup_lat() and handle a return code.
> + */
> +static void omap_pm_set_max_mpu_wakeup_lat_compat(struct device *dev, long t)
> +{
> + omap_pm_set_max_mpu_wakeup_lat(dev, t);
> +}
> +
> static inline int omap2_i2c_add_bus(int bus_id)
> {
> int l;
> @@ -158,6 +170,15 @@ static inline int omap2_i2c_add_bus(int bus_id)
> dev_attr = (struct omap_i2c_dev_attr *)oh->dev_attr;
> pdata->flags = dev_attr->flags;
>
> + /*
> + * When waiting for completion of a i2c transfer, we need to
> + * set a wake up latency constraint for the MPU. This is to
> + * ensure quick enough wakeup from idle, when transfer
> + * completes.
> + * Only omap3 has support for constraints
> + */
> + if (cpu_is_omap34xx())
> + pdata->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat_compat;
> pdev = omap_device_build(name, bus_id, oh, pdata,
> sizeof(struct omap_i2c_bus_platform_data),
> NULL, 0, 0);
> diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
> index db31eae..0b02543 100644
> --- a/drivers/i2c/busses/i2c-omap.c
> +++ b/drivers/i2c/busses/i2c-omap.c
> @@ -43,7 +43,6 @@
> #include <linux/slab.h>
> #include <linux/i2c-omap.h>
> #include <linux/pm_runtime.h>
> -#include <linux/pm_qos.h>
>
> /* I2C controller revisions */
> #define OMAP_I2C_OMAP1_REV_2 0x20
> @@ -187,8 +186,9 @@ struct omap_i2c_dev {
> int reg_shift; /* bit shift for I2C register addresses */
> struct completion cmd_complete;
> struct resource *ioarea;
> - u32 latency; /* maximum MPU wkup latency */
> - struct pm_qos_request pm_qos_request;
> + u32 latency; /* maximum mpu wkup latency */
> + void (*set_mpu_wkup_lat)(struct device *dev,
> + long latency);
> u32 speed; /* Speed of bus in kHz */
> u32 dtrev; /* extra revision from DT */
> u32 flags;
> @@ -494,7 +494,9 @@ static void omap_i2c_resize_fifo(struct omap_i2c_dev *dev, u8 size, bool is_rx)
> dev->b_hw = 1; /* Enable hardware fixes */
>
> /* calculate wakeup latency constraint for MPU */
> - dev->latency = (1000000 * dev->threshold) / (1000 * dev->speed / 8);
> + if (dev->set_mpu_wkup_lat != NULL)
> + dev->latency = (1000000 * dev->threshold) /
> + (1000 * dev->speed / 8);
> }
>
> /*
> @@ -629,16 +631,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
> if (r < 0)
> goto out;
>
> - /*
> - * When waiting for completion of a i2c transfer, we need to
> - * set a wake up latency constraint for the MPU. This is to
> - * ensure quick enough wakeup from idle, when transfer
> - * completes.
> - */
> - if (dev->latency)
> - pm_qos_add_request(&dev->pm_qos_request,
> - PM_QOS_CPU_DMA_LATENCY,
> - dev->latency);
> + if (dev->set_mpu_wkup_lat != NULL)
> + dev->set_mpu_wkup_lat(dev->dev, dev->latency);
>
> for (i = 0; i < num; i++) {
> r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
> @@ -646,8 +640,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
> break;
> }
>
> - if (dev->latency)
> - pm_qos_remove_request(&dev->pm_qos_request);
> + if (dev->set_mpu_wkup_lat != NULL)
> + dev->set_mpu_wkup_lat(dev->dev, -1);
>
> if (r == 0)
> r = num;
> @@ -1104,6 +1098,7 @@ omap_i2c_probe(struct platform_device *pdev)
> } else if (pdata != NULL) {
> dev->speed = pdata->clkrate;
> dev->flags = pdata->flags;
> + dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
> dev->dtrev = pdata->rev;
> }
>
> @@ -1159,8 +1154,9 @@ omap_i2c_probe(struct platform_device *pdev)
> dev->b_hw = 1; /* Enable hardware fixes */
>
> /* calculate wakeup latency constraint for MPU */
> - dev->latency = (1000000 * dev->fifo_size) /
> - (1000 * dev->speed / 8);
> + if (dev->set_mpu_wkup_lat != NULL)
> + dev->latency = (1000000 * dev->fifo_size) /
> + (1000 * dev->speed / 8);
> }
>
> /* reset ASAP, clearing any IRQs */
> diff --git a/include/linux/i2c-omap.h b/include/linux/i2c-omap.h
> index df804ba..92a0dc7 100644
> --- a/include/linux/i2c-omap.h
> +++ b/include/linux/i2c-omap.h
> @@ -34,6 +34,7 @@ struct omap_i2c_bus_platform_data {
> u32 clkrate;
> u32 rev;
> u32 flags;
> + void (*set_mpu_wkup_lat)(struct device *dev, long set);
> };
>
> #endif
> --
> 1.7.10.4
^ permalink raw reply
* [PATCH 3/5] crypto: Atmel AES driver: add support for latest release of the IP (0x130)
From: Nicolas Royer @ 2012-11-06 16:32 UTC (permalink / raw)
To: linux-arm-kernel
Updates from previous IP release (0x120):
- add cfb64 support
- add DMA double input buffer support
Signed-off-by: Nicolas Royer <nicolas@eukrea.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Eric B?nard <eric@eukrea.com>
Tested-by: Eric B?nard <eric@eukrea.com>
---
drivers/crypto/atmel-aes.c | 470 +++++++++++++++++++++++++++++++++-----------
1 files changed, 352 insertions(+), 118 deletions(-)
diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c
index 8061336..3bce510 100644
--- a/drivers/crypto/atmel-aes.c
+++ b/drivers/crypto/atmel-aes.c
@@ -38,7 +38,7 @@
#include <crypto/aes.h>
#include <crypto/hash.h>
#include <crypto/internal/hash.h>
-#include <linux/platform_data/atmel-aes.h>
+#include <linux/platform_data/crypto-atmel.h>
#include "atmel-aes-regs.h"
#define CFB8_BLOCK_SIZE 1
@@ -47,7 +47,7 @@
#define CFB64_BLOCK_SIZE 8
/* AES flags */
-#define AES_FLAGS_MODE_MASK 0x01ff
+#define AES_FLAGS_MODE_MASK 0x03ff
#define AES_FLAGS_ENCRYPT BIT(0)
#define AES_FLAGS_CBC BIT(1)
#define AES_FLAGS_CFB BIT(2)
@@ -55,21 +55,26 @@
#define AES_FLAGS_CFB16 BIT(4)
#define AES_FLAGS_CFB32 BIT(5)
#define AES_FLAGS_CFB64 BIT(6)
-#define AES_FLAGS_OFB BIT(7)
-#define AES_FLAGS_CTR BIT(8)
+#define AES_FLAGS_CFB128 BIT(7)
+#define AES_FLAGS_OFB BIT(8)
+#define AES_FLAGS_CTR BIT(9)
#define AES_FLAGS_INIT BIT(16)
#define AES_FLAGS_DMA BIT(17)
#define AES_FLAGS_BUSY BIT(18)
+#define AES_FLAGS_FAST BIT(19)
-#define AES_FLAGS_DUALBUFF BIT(24)
-
-#define ATMEL_AES_QUEUE_LENGTH 1
-#define ATMEL_AES_CACHE_SIZE 0
+#define ATMEL_AES_QUEUE_LENGTH 50
#define ATMEL_AES_DMA_THRESHOLD 16
+struct atmel_aes_caps {
+ bool has_dualbuff;
+ bool has_cfb64;
+ u32 max_burst_size;
+};
+
struct atmel_aes_dev;
struct atmel_aes_ctx {
@@ -77,6 +82,8 @@ struct atmel_aes_ctx {
int keylen;
u32 key[AES_KEYSIZE_256 / sizeof(u32)];
+
+ u16 block_size;
};
struct atmel_aes_reqctx {
@@ -112,20 +119,27 @@ struct atmel_aes_dev {
struct scatterlist *in_sg;
unsigned int nb_in_sg;
-
+ size_t in_offset;
struct scatterlist *out_sg;
unsigned int nb_out_sg;
+ size_t out_offset;
size_t bufcnt;
+ size_t buflen;
+ size_t dma_size;
- u8 buf_in[ATMEL_AES_DMA_THRESHOLD] __aligned(sizeof(u32));
- int dma_in;
+ void *buf_in;
+ int dma_in;
+ dma_addr_t dma_addr_in;
struct atmel_aes_dma dma_lch_in;
- u8 buf_out[ATMEL_AES_DMA_THRESHOLD] __aligned(sizeof(u32));
- int dma_out;
+ void *buf_out;
+ int dma_out;
+ dma_addr_t dma_addr_out;
struct atmel_aes_dma dma_lch_out;
+ struct atmel_aes_caps caps;
+
u32 hw_version;
};
@@ -165,6 +179,37 @@ static int atmel_aes_sg_length(struct ablkcipher_request *req,
return sg_nb;
}
+static int atmel_aes_sg_copy(struct scatterlist **sg, size_t *offset,
+ void *buf, size_t buflen, size_t total, int out)
+{
+ unsigned int count, off = 0;
+
+ while (buflen && total) {
+ count = min((*sg)->length - *offset, total);
+ count = min(count, buflen);
+
+ if (!count)
+ return off;
+
+ scatterwalk_map_and_copy(buf + off, *sg, *offset, count, out);
+
+ off += count;
+ buflen -= count;
+ *offset += count;
+ total -= count;
+
+ if (*offset == (*sg)->length) {
+ *sg = sg_next(*sg);
+ if (*sg)
+ *offset = 0;
+ else
+ total = 0;
+ }
+ }
+
+ return off;
+}
+
static inline u32 atmel_aes_read(struct atmel_aes_dev *dd, u32 offset)
{
return readl_relaxed(dd->io_base + offset);
@@ -190,14 +235,6 @@ static void atmel_aes_write_n(struct atmel_aes_dev *dd, u32 offset,
atmel_aes_write(dd, offset, *value);
}
-static void atmel_aes_dualbuff_test(struct atmel_aes_dev *dd)
-{
- atmel_aes_write(dd, AES_MR, AES_MR_DUALBUFF);
-
- if (atmel_aes_read(dd, AES_MR) & AES_MR_DUALBUFF)
- dd->flags |= AES_FLAGS_DUALBUFF;
-}
-
static struct atmel_aes_dev *atmel_aes_find_dev(struct atmel_aes_ctx *ctx)
{
struct atmel_aes_dev *aes_dd = NULL;
@@ -225,7 +262,6 @@ static int atmel_aes_hw_init(struct atmel_aes_dev *dd)
if (!(dd->flags & AES_FLAGS_INIT)) {
atmel_aes_write(dd, AES_CR, AES_CR_SWRST);
- atmel_aes_dualbuff_test(dd);
dd->flags |= AES_FLAGS_INIT;
dd->err = 0;
}
@@ -233,11 +269,19 @@ static int atmel_aes_hw_init(struct atmel_aes_dev *dd)
return 0;
}
+static inline unsigned int atmel_aes_get_version(struct atmel_aes_dev *dd)
+{
+ return atmel_aes_read(dd, AES_HW_VERSION) & 0x00000fff;
+}
+
static void atmel_aes_hw_version_init(struct atmel_aes_dev *dd)
{
atmel_aes_hw_init(dd);
- dd->hw_version = atmel_aes_read(dd, AES_HW_VERSION);
+ dd->hw_version = atmel_aes_get_version(dd);
+
+ dev_info(dd->dev,
+ "version: 0x%x\n", dd->hw_version);
clk_disable_unprepare(dd->iclk);
}
@@ -260,50 +304,77 @@ static void atmel_aes_dma_callback(void *data)
tasklet_schedule(&dd->done_task);
}
-static int atmel_aes_crypt_dma(struct atmel_aes_dev *dd)
+static int atmel_aes_crypt_dma(struct atmel_aes_dev *dd,
+ dma_addr_t dma_addr_in, dma_addr_t dma_addr_out, int length)
{
+ struct scatterlist sg[2];
struct dma_async_tx_descriptor *in_desc, *out_desc;
- int nb_dma_sg_in, nb_dma_sg_out;
- dd->nb_in_sg = atmel_aes_sg_length(dd->req, dd->in_sg);
- if (!dd->nb_in_sg)
- goto exit_err;
+ dd->dma_size = length;
- nb_dma_sg_in = dma_map_sg(dd->dev, dd->in_sg, dd->nb_in_sg,
- DMA_TO_DEVICE);
- if (!nb_dma_sg_in)
- goto exit_err;
+ if (!(dd->flags & AES_FLAGS_FAST)) {
+ dma_sync_single_for_device(dd->dev, dma_addr_in, length,
+ DMA_TO_DEVICE);
+ }
- in_desc = dmaengine_prep_slave_sg(dd->dma_lch_in.chan, dd->in_sg,
- nb_dma_sg_in, DMA_MEM_TO_DEV,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (dd->flags & AES_FLAGS_CFB8) {
+ dd->dma_lch_in.dma_conf.dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dd->dma_lch_out.dma_conf.src_addr_width =
+ DMA_SLAVE_BUSWIDTH_1_BYTE;
+ } else if (dd->flags & AES_FLAGS_CFB16) {
+ dd->dma_lch_in.dma_conf.dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_2_BYTES;
+ dd->dma_lch_out.dma_conf.src_addr_width =
+ DMA_SLAVE_BUSWIDTH_2_BYTES;
+ } else {
+ dd->dma_lch_in.dma_conf.dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dd->dma_lch_out.dma_conf.src_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ }
- if (!in_desc)
- goto unmap_in;
+ if (dd->flags & (AES_FLAGS_CFB8 | AES_FLAGS_CFB16 |
+ AES_FLAGS_CFB32 | AES_FLAGS_CFB64)) {
+ dd->dma_lch_in.dma_conf.src_maxburst = 1;
+ dd->dma_lch_in.dma_conf.dst_maxburst = 1;
+ dd->dma_lch_out.dma_conf.src_maxburst = 1;
+ dd->dma_lch_out.dma_conf.dst_maxburst = 1;
+ } else {
+ dd->dma_lch_in.dma_conf.src_maxburst = dd->caps.max_burst_size;
+ dd->dma_lch_in.dma_conf.dst_maxburst = dd->caps.max_burst_size;
+ dd->dma_lch_out.dma_conf.src_maxburst = dd->caps.max_burst_size;
+ dd->dma_lch_out.dma_conf.dst_maxburst = dd->caps.max_burst_size;
+ }
- /* callback not needed */
+ dmaengine_slave_config(dd->dma_lch_in.chan, &dd->dma_lch_in.dma_conf);
+ dmaengine_slave_config(dd->dma_lch_out.chan, &dd->dma_lch_out.dma_conf);
- dd->nb_out_sg = atmel_aes_sg_length(dd->req, dd->out_sg);
- if (!dd->nb_out_sg)
- goto unmap_in;
+ dd->flags |= AES_FLAGS_DMA;
- nb_dma_sg_out = dma_map_sg(dd->dev, dd->out_sg, dd->nb_out_sg,
- DMA_FROM_DEVICE);
- if (!nb_dma_sg_out)
- goto unmap_out;
+ sg_init_table(&sg[0], 1);
+ sg_dma_address(&sg[0]) = dma_addr_in;
+ sg_dma_len(&sg[0]) = length;
- out_desc = dmaengine_prep_slave_sg(dd->dma_lch_out.chan, dd->out_sg,
- nb_dma_sg_out, DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ sg_init_table(&sg[1], 1);
+ sg_dma_address(&sg[1]) = dma_addr_out;
+ sg_dma_len(&sg[1]) = length;
+
+ in_desc = dmaengine_prep_slave_sg(dd->dma_lch_in.chan, &sg[0],
+ 1, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!in_desc)
+ return -EINVAL;
+ out_desc = dmaengine_prep_slave_sg(dd->dma_lch_out.chan, &sg[1],
+ 1, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!out_desc)
- goto unmap_out;
+ return -EINVAL;
out_desc->callback = atmel_aes_dma_callback;
out_desc->callback_param = dd;
- dd->total -= dd->req->nbytes;
-
dmaengine_submit(out_desc);
dma_async_issue_pending(dd->dma_lch_out.chan);
@@ -311,15 +382,6 @@ static int atmel_aes_crypt_dma(struct atmel_aes_dev *dd)
dma_async_issue_pending(dd->dma_lch_in.chan);
return 0;
-
-unmap_out:
- dma_unmap_sg(dd->dev, dd->out_sg, dd->nb_out_sg,
- DMA_FROM_DEVICE);
-unmap_in:
- dma_unmap_sg(dd->dev, dd->in_sg, dd->nb_in_sg,
- DMA_TO_DEVICE);
-exit_err:
- return -EINVAL;
}
static int atmel_aes_crypt_cpu_start(struct atmel_aes_dev *dd)
@@ -352,30 +414,66 @@ static int atmel_aes_crypt_cpu_start(struct atmel_aes_dev *dd)
static int atmel_aes_crypt_dma_start(struct atmel_aes_dev *dd)
{
- int err;
+ int err, fast = 0, in, out;
+ size_t count;
+ dma_addr_t addr_in, addr_out;
+
+ if ((!dd->in_offset) && (!dd->out_offset)) {
+ /* check for alignment */
+ in = IS_ALIGNED((u32)dd->in_sg->offset, sizeof(u32)) &&
+ IS_ALIGNED(dd->in_sg->length, dd->ctx->block_size);
+ out = IS_ALIGNED((u32)dd->out_sg->offset, sizeof(u32)) &&
+ IS_ALIGNED(dd->out_sg->length, dd->ctx->block_size);
+ fast = in && out;
+
+ if (sg_dma_len(dd->in_sg) != sg_dma_len(dd->out_sg))
+ fast = 0;
+ }
+
+
+ if (fast) {
+ count = min(dd->total, sg_dma_len(dd->in_sg));
+ count = min(count, sg_dma_len(dd->out_sg));
+
+ err = dma_map_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE);
+ if (!err) {
+ dev_err(dd->dev, "dma_map_sg() error\n");
+ return -EINVAL;
+ }
+
+ err = dma_map_sg(dd->dev, dd->out_sg, 1,
+ DMA_FROM_DEVICE);
+ if (!err) {
+ dev_err(dd->dev, "dma_map_sg() error\n");
+ dma_unmap_sg(dd->dev, dd->in_sg, 1,
+ DMA_TO_DEVICE);
+ return -EINVAL;
+ }
+
+ addr_in = sg_dma_address(dd->in_sg);
+ addr_out = sg_dma_address(dd->out_sg);
+
+ dd->flags |= AES_FLAGS_FAST;
- if (dd->flags & AES_FLAGS_CFB8) {
- dd->dma_lch_in.dma_conf.dst_addr_width =
- DMA_SLAVE_BUSWIDTH_1_BYTE;
- dd->dma_lch_out.dma_conf.src_addr_width =
- DMA_SLAVE_BUSWIDTH_1_BYTE;
- } else if (dd->flags & AES_FLAGS_CFB16) {
- dd->dma_lch_in.dma_conf.dst_addr_width =
- DMA_SLAVE_BUSWIDTH_2_BYTES;
- dd->dma_lch_out.dma_conf.src_addr_width =
- DMA_SLAVE_BUSWIDTH_2_BYTES;
} else {
- dd->dma_lch_in.dma_conf.dst_addr_width =
- DMA_SLAVE_BUSWIDTH_4_BYTES;
- dd->dma_lch_out.dma_conf.src_addr_width =
- DMA_SLAVE_BUSWIDTH_4_BYTES;
+ /* use cache buffers */
+ count = atmel_aes_sg_copy(&dd->in_sg, &dd->in_offset,
+ dd->buf_in, dd->buflen, dd->total, 0);
+
+ addr_in = dd->dma_addr_in;
+ addr_out = dd->dma_addr_out;
+
+ dd->flags &= ~AES_FLAGS_FAST;
}
- dmaengine_slave_config(dd->dma_lch_in.chan, &dd->dma_lch_in.dma_conf);
- dmaengine_slave_config(dd->dma_lch_out.chan, &dd->dma_lch_out.dma_conf);
+ dd->total -= count;
- dd->flags |= AES_FLAGS_DMA;
- err = atmel_aes_crypt_dma(dd);
+ err = atmel_aes_crypt_dma(dd, addr_in, addr_out, count);
+
+ if (err && (dd->flags & AES_FLAGS_FAST)) {
+ dma_unmap_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE);
+ dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_TO_DEVICE);
+ }
return err;
}
@@ -410,6 +508,8 @@ static int atmel_aes_write_ctrl(struct atmel_aes_dev *dd)
valmr |= AES_MR_CFBS_32b;
else if (dd->flags & AES_FLAGS_CFB64)
valmr |= AES_MR_CFBS_64b;
+ else if (dd->flags & AES_FLAGS_CFB128)
+ valmr |= AES_MR_CFBS_128b;
} else if (dd->flags & AES_FLAGS_OFB) {
valmr |= AES_MR_OPMOD_OFB;
} else if (dd->flags & AES_FLAGS_CTR) {
@@ -423,7 +523,7 @@ static int atmel_aes_write_ctrl(struct atmel_aes_dev *dd)
if (dd->total > ATMEL_AES_DMA_THRESHOLD) {
valmr |= AES_MR_SMOD_IDATAR0;
- if (dd->flags & AES_FLAGS_DUALBUFF)
+ if (dd->caps.has_dualbuff)
valmr |= AES_MR_DUALBUFF;
} else {
valmr |= AES_MR_SMOD_AUTO;
@@ -477,7 +577,9 @@ static int atmel_aes_handle_queue(struct atmel_aes_dev *dd,
/* assign new request to device */
dd->req = req;
dd->total = req->nbytes;
+ dd->in_offset = 0;
dd->in_sg = req->src;
+ dd->out_offset = 0;
dd->out_sg = req->dst;
rctx = ablkcipher_request_ctx(req);
@@ -506,18 +608,86 @@ static int atmel_aes_handle_queue(struct atmel_aes_dev *dd,
static int atmel_aes_crypt_dma_stop(struct atmel_aes_dev *dd)
{
int err = -EINVAL;
+ size_t count;
if (dd->flags & AES_FLAGS_DMA) {
- dma_unmap_sg(dd->dev, dd->out_sg,
- dd->nb_out_sg, DMA_FROM_DEVICE);
- dma_unmap_sg(dd->dev, dd->in_sg,
- dd->nb_in_sg, DMA_TO_DEVICE);
err = 0;
+ if (dd->flags & AES_FLAGS_FAST) {
+ dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_FROM_DEVICE);
+ dma_unmap_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE);
+ } else {
+ dma_sync_single_for_device(dd->dev, dd->dma_addr_out,
+ dd->dma_size, DMA_FROM_DEVICE);
+
+ /* copy data */
+ count = atmel_aes_sg_copy(&dd->out_sg, &dd->out_offset,
+ dd->buf_out, dd->buflen, dd->dma_size, 1);
+ if (count != dd->dma_size) {
+ err = -EINVAL;
+ pr_err("not all data converted: %u\n", count);
+ }
+ }
+ }
+
+ return err;
+}
+
+
+static int atmel_aes_buff_init(struct atmel_aes_dev *dd)
+{
+ int err = -ENOMEM;
+
+ dd->buf_in = (void *)__get_free_pages(GFP_KERNEL, 0);
+ dd->buf_out = (void *)__get_free_pages(GFP_KERNEL, 0);
+ dd->buflen = PAGE_SIZE;
+ dd->buflen &= ~(AES_BLOCK_SIZE - 1);
+
+ if (!dd->buf_in || !dd->buf_out) {
+ dev_err(dd->dev, "unable to alloc pages.\n");
+ goto err_alloc;
+ }
+
+ /* MAP here */
+ dd->dma_addr_in = dma_map_single(dd->dev, dd->buf_in,
+ dd->buflen, DMA_TO_DEVICE);
+ if (dma_mapping_error(dd->dev, dd->dma_addr_in)) {
+ dev_err(dd->dev, "dma %d bytes error\n", dd->buflen);
+ err = -EINVAL;
+ goto err_map_in;
+ }
+
+ dd->dma_addr_out = dma_map_single(dd->dev, dd->buf_out,
+ dd->buflen, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dd->dev, dd->dma_addr_out)) {
+ dev_err(dd->dev, "dma %d bytes error\n", dd->buflen);
+ err = -EINVAL;
+ goto err_map_out;
}
+ return 0;
+
+err_map_out:
+ dma_unmap_single(dd->dev, dd->dma_addr_in, dd->buflen,
+ DMA_TO_DEVICE);
+err_map_in:
+ free_page((unsigned long)dd->buf_out);
+ free_page((unsigned long)dd->buf_in);
+err_alloc:
+ if (err)
+ pr_err("error: %d\n", err);
return err;
}
+static void atmel_aes_buff_cleanup(struct atmel_aes_dev *dd)
+{
+ dma_unmap_single(dd->dev, dd->dma_addr_out, dd->buflen,
+ DMA_FROM_DEVICE);
+ dma_unmap_single(dd->dev, dd->dma_addr_in, dd->buflen,
+ DMA_TO_DEVICE);
+ free_page((unsigned long)dd->buf_out);
+ free_page((unsigned long)dd->buf_in);
+}
+
static int atmel_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
{
struct atmel_aes_ctx *ctx = crypto_ablkcipher_ctx(
@@ -525,9 +695,30 @@ static int atmel_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
struct atmel_aes_reqctx *rctx = ablkcipher_request_ctx(req);
struct atmel_aes_dev *dd;
- if (!IS_ALIGNED(req->nbytes, AES_BLOCK_SIZE)) {
- pr_err("request size is not exact amount of AES blocks\n");
- return -EINVAL;
+ if (mode & AES_FLAGS_CFB8) {
+ if (!IS_ALIGNED(req->nbytes, CFB8_BLOCK_SIZE)) {
+ pr_err("request size is not exact amount of CFB8 blocks\n");
+ return -EINVAL;
+ }
+ ctx->block_size = CFB8_BLOCK_SIZE;
+ } else if (mode & AES_FLAGS_CFB16) {
+ if (!IS_ALIGNED(req->nbytes, CFB16_BLOCK_SIZE)) {
+ pr_err("request size is not exact amount of CFB16 blocks\n");
+ return -EINVAL;
+ }
+ ctx->block_size = CFB16_BLOCK_SIZE;
+ } else if (mode & AES_FLAGS_CFB32) {
+ if (!IS_ALIGNED(req->nbytes, CFB32_BLOCK_SIZE)) {
+ pr_err("request size is not exact amount of CFB32 blocks\n");
+ return -EINVAL;
+ }
+ ctx->block_size = CFB32_BLOCK_SIZE;
+ } else {
+ if (!IS_ALIGNED(req->nbytes, AES_BLOCK_SIZE)) {
+ pr_err("request size is not exact amount of AES blocks\n");
+ return -EINVAL;
+ }
+ ctx->block_size = AES_BLOCK_SIZE;
}
dd = atmel_aes_find_dev(ctx);
@@ -551,14 +742,12 @@ static bool atmel_aes_filter(struct dma_chan *chan, void *slave)
}
}
-static int atmel_aes_dma_init(struct atmel_aes_dev *dd)
+static int atmel_aes_dma_init(struct atmel_aes_dev *dd,
+ struct crypto_platform_data *pdata)
{
int err = -ENOMEM;
- struct aes_platform_data *pdata;
dma_cap_mask_t mask_in, mask_out;
- pdata = dd->dev->platform_data;
-
if (pdata && pdata->dma_slave->txdata.dma_dev &&
pdata->dma_slave->rxdata.dma_dev) {
@@ -568,28 +757,38 @@ static int atmel_aes_dma_init(struct atmel_aes_dev *dd)
dd->dma_lch_in.chan = dma_request_channel(mask_in,
atmel_aes_filter, &pdata->dma_slave->rxdata);
+
if (!dd->dma_lch_in.chan)
goto err_dma_in;
dd->dma_lch_in.dma_conf.direction = DMA_MEM_TO_DEV;
dd->dma_lch_in.dma_conf.dst_addr = dd->phys_base +
AES_IDATAR(0);
- dd->dma_lch_in.dma_conf.src_maxburst = 1;
- dd->dma_lch_in.dma_conf.dst_maxburst = 1;
+ dd->dma_lch_in.dma_conf.src_maxburst = dd->caps.max_burst_size;
+ dd->dma_lch_in.dma_conf.src_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dd->dma_lch_in.dma_conf.dst_maxburst = dd->caps.max_burst_size;
+ dd->dma_lch_in.dma_conf.dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
dd->dma_lch_in.dma_conf.device_fc = false;
dma_cap_zero(mask_out);
dma_cap_set(DMA_SLAVE, mask_out);
dd->dma_lch_out.chan = dma_request_channel(mask_out,
atmel_aes_filter, &pdata->dma_slave->txdata);
+
if (!dd->dma_lch_out.chan)
goto err_dma_out;
dd->dma_lch_out.dma_conf.direction = DMA_DEV_TO_MEM;
dd->dma_lch_out.dma_conf.src_addr = dd->phys_base +
AES_ODATAR(0);
- dd->dma_lch_out.dma_conf.src_maxburst = 1;
- dd->dma_lch_out.dma_conf.dst_maxburst = 1;
+ dd->dma_lch_out.dma_conf.src_maxburst = dd->caps.max_burst_size;
+ dd->dma_lch_out.dma_conf.src_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dd->dma_lch_out.dma_conf.dst_maxburst = dd->caps.max_burst_size;
+ dd->dma_lch_out.dma_conf.dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
dd->dma_lch_out.dma_conf.device_fc = false;
return 0;
@@ -665,13 +864,13 @@ static int atmel_aes_ofb_decrypt(struct ablkcipher_request *req)
static int atmel_aes_cfb_encrypt(struct ablkcipher_request *req)
{
return atmel_aes_crypt(req,
- AES_FLAGS_ENCRYPT | AES_FLAGS_CFB);
+ AES_FLAGS_ENCRYPT | AES_FLAGS_CFB | AES_FLAGS_CFB128);
}
static int atmel_aes_cfb_decrypt(struct ablkcipher_request *req)
{
return atmel_aes_crypt(req,
- AES_FLAGS_CFB);
+ AES_FLAGS_CFB | AES_FLAGS_CFB128);
}
static int atmel_aes_cfb64_encrypt(struct ablkcipher_request *req)
@@ -753,7 +952,7 @@ static struct crypto_alg aes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_aes_ctx),
- .cra_alignmask = 0x0,
+ .cra_alignmask = 0xf,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_aes_cra_init,
@@ -773,7 +972,7 @@ static struct crypto_alg aes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_aes_ctx),
- .cra_alignmask = 0x0,
+ .cra_alignmask = 0xf,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_aes_cra_init,
@@ -794,7 +993,7 @@ static struct crypto_alg aes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_aes_ctx),
- .cra_alignmask = 0x0,
+ .cra_alignmask = 0xf,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_aes_cra_init,
@@ -815,7 +1014,7 @@ static struct crypto_alg aes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_aes_ctx),
- .cra_alignmask = 0x0,
+ .cra_alignmask = 0xf,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_aes_cra_init,
@@ -836,7 +1035,7 @@ static struct crypto_alg aes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = CFB32_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_aes_ctx),
- .cra_alignmask = 0x0,
+ .cra_alignmask = 0x3,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_aes_cra_init,
@@ -857,7 +1056,7 @@ static struct crypto_alg aes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = CFB16_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_aes_ctx),
- .cra_alignmask = 0x0,
+ .cra_alignmask = 0x1,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_aes_cra_init,
@@ -899,7 +1098,7 @@ static struct crypto_alg aes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_aes_ctx),
- .cra_alignmask = 0x0,
+ .cra_alignmask = 0xf,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_aes_cra_init,
@@ -915,15 +1114,14 @@ static struct crypto_alg aes_algs[] = {
},
};
-static struct crypto_alg aes_cfb64_alg[] = {
-{
+static struct crypto_alg aes_cfb64_alg = {
.cra_name = "cfb64(aes)",
.cra_driver_name = "atmel-cfb64-aes",
.cra_priority = 100,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = CFB64_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_aes_ctx),
- .cra_alignmask = 0x0,
+ .cra_alignmask = 0x7,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_aes_cra_init,
@@ -936,7 +1134,6 @@ static struct crypto_alg aes_cfb64_alg[] = {
.encrypt = atmel_aes_cfb64_encrypt,
.decrypt = atmel_aes_cfb64_decrypt,
}
-},
};
static void atmel_aes_queue_task(unsigned long data)
@@ -969,7 +1166,14 @@ static void atmel_aes_done_task(unsigned long data)
err = dd->err ? : err;
if (dd->total && !err) {
- err = atmel_aes_crypt_dma_start(dd);
+ if (dd->flags & AES_FLAGS_FAST) {
+ dd->in_sg = sg_next(dd->in_sg);
+ dd->out_sg = sg_next(dd->out_sg);
+ if (!dd->in_sg || !dd->out_sg)
+ err = -EINVAL;
+ }
+ if (!err)
+ err = atmel_aes_crypt_dma_start(dd);
if (!err)
return; /* DMA started. Not fininishing. */
}
@@ -1003,8 +1207,8 @@ static void atmel_aes_unregister_algs(struct atmel_aes_dev *dd)
for (i = 0; i < ARRAY_SIZE(aes_algs); i++)
crypto_unregister_alg(&aes_algs[i]);
- if (dd->hw_version >= 0x130)
- crypto_unregister_alg(&aes_cfb64_alg[0]);
+ if (dd->caps.has_cfb64)
+ crypto_unregister_alg(&aes_cfb64_alg);
}
static int atmel_aes_register_algs(struct atmel_aes_dev *dd)
@@ -1017,10 +1221,8 @@ static int atmel_aes_register_algs(struct atmel_aes_dev *dd)
goto err_aes_algs;
}
- atmel_aes_hw_version_init(dd);
-
- if (dd->hw_version >= 0x130) {
- err = crypto_register_alg(&aes_cfb64_alg[0]);
+ if (dd->caps.has_cfb64) {
+ err = crypto_register_alg(&aes_cfb64_alg);
if (err)
goto err_aes_cfb64_alg;
}
@@ -1036,10 +1238,32 @@ err_aes_algs:
return err;
}
+static void atmel_aes_get_cap(struct atmel_aes_dev *dd)
+{
+ dd->caps.has_dualbuff = 0;
+ dd->caps.has_cfb64 = 0;
+ dd->caps.max_burst_size = 1;
+
+ /* keep only major version number */
+ switch (dd->hw_version & 0xff0) {
+ case 0x130:
+ dd->caps.has_dualbuff = 1;
+ dd->caps.has_cfb64 = 1;
+ dd->caps.max_burst_size = 4;
+ break;
+ case 0x120:
+ break;
+ default:
+ dev_warn(dd->dev,
+ "Unmanaged aes version, set minimum capabilities\n");
+ break;
+ }
+}
+
static int __devinit atmel_aes_probe(struct platform_device *pdev)
{
struct atmel_aes_dev *aes_dd;
- struct aes_platform_data *pdata;
+ struct crypto_platform_data *pdata;
struct device *dev = &pdev->dev;
struct resource *aes_res;
unsigned long aes_phys_size;
@@ -1099,7 +1323,7 @@ static int __devinit atmel_aes_probe(struct platform_device *pdev)
}
/* Initializing the clock */
- aes_dd->iclk = clk_get(&pdev->dev, NULL);
+ aes_dd->iclk = clk_get(&pdev->dev, "aes_clk");
if (IS_ERR(aes_dd->iclk)) {
dev_err(dev, "clock intialization failed.\n");
err = PTR_ERR(aes_dd->iclk);
@@ -1113,7 +1337,15 @@ static int __devinit atmel_aes_probe(struct platform_device *pdev)
goto aes_io_err;
}
- err = atmel_aes_dma_init(aes_dd);
+ atmel_aes_hw_version_init(aes_dd);
+
+ atmel_aes_get_cap(aes_dd);
+
+ err = atmel_aes_buff_init(aes_dd);
+ if (err)
+ goto err_aes_buff;
+
+ err = atmel_aes_dma_init(aes_dd, pdata);
if (err)
goto err_aes_dma;
@@ -1135,6 +1367,8 @@ err_algs:
spin_unlock(&atmel_aes.lock);
atmel_aes_dma_cleanup(aes_dd);
err_aes_dma:
+ atmel_aes_buff_cleanup(aes_dd);
+err_aes_buff:
iounmap(aes_dd->io_base);
aes_io_err:
clk_put(aes_dd->iclk);
--
1.7.6.5
^ permalink raw reply related
* [PATCH 4/5] crypto: Atmel TDES driver: add support for latest release of the IP (0x700)
From: Nicolas Royer @ 2012-11-06 16:33 UTC (permalink / raw)
To: linux-arm-kernel
Update from previous IP release (0x600):
- add DMA support (previous IP release use PDC)
Signed-off-by: Nicolas Royer <nicolas@eukrea.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Eric B?nard <eric@eukrea.com>
Tested-by: Eric B?nard <eric@eukrea.com>
---
drivers/crypto/atmel-tdes-regs.h | 2 +
drivers/crypto/atmel-tdes.c | 394 +++++++++++++++++++++++++++++++++-----
2 files changed, 343 insertions(+), 53 deletions(-)
diff --git a/drivers/crypto/atmel-tdes-regs.h b/drivers/crypto/atmel-tdes-regs.h
index 5ac2a90..f86734d 100644
--- a/drivers/crypto/atmel-tdes-regs.h
+++ b/drivers/crypto/atmel-tdes-regs.h
@@ -69,6 +69,8 @@
#define TDES_XTEARNDR_XTEA_RNDS_MASK (0x3F << 0)
#define TDES_XTEARNDR_XTEA_RNDS_OFFSET 0
+#define TDES_HW_VERSION 0xFC
+
#define TDES_RPR 0x100
#define TDES_RCR 0x104
#define TDES_TPR 0x108
diff --git a/drivers/crypto/atmel-tdes.c b/drivers/crypto/atmel-tdes.c
index 7495f98..d2b81a9 100644
--- a/drivers/crypto/atmel-tdes.c
+++ b/drivers/crypto/atmel-tdes.c
@@ -38,29 +38,35 @@
#include <crypto/des.h>
#include <crypto/hash.h>
#include <crypto/internal/hash.h>
+#include <linux/platform_data/crypto-atmel.h>
#include "atmel-tdes-regs.h"
/* TDES flags */
-#define TDES_FLAGS_MODE_MASK 0x007f
+#define TDES_FLAGS_MODE_MASK 0x00ff
#define TDES_FLAGS_ENCRYPT BIT(0)
#define TDES_FLAGS_CBC BIT(1)
#define TDES_FLAGS_CFB BIT(2)
#define TDES_FLAGS_CFB8 BIT(3)
#define TDES_FLAGS_CFB16 BIT(4)
#define TDES_FLAGS_CFB32 BIT(5)
-#define TDES_FLAGS_OFB BIT(6)
+#define TDES_FLAGS_CFB64 BIT(6)
+#define TDES_FLAGS_OFB BIT(7)
#define TDES_FLAGS_INIT BIT(16)
#define TDES_FLAGS_FAST BIT(17)
#define TDES_FLAGS_BUSY BIT(18)
+#define TDES_FLAGS_DMA BIT(19)
-#define ATMEL_TDES_QUEUE_LENGTH 1
+#define ATMEL_TDES_QUEUE_LENGTH 50
#define CFB8_BLOCK_SIZE 1
#define CFB16_BLOCK_SIZE 2
#define CFB32_BLOCK_SIZE 4
-#define CFB64_BLOCK_SIZE 8
+struct atmel_tdes_caps {
+ bool has_dma;
+ u32 has_cfb_3keys;
+};
struct atmel_tdes_dev;
@@ -70,12 +76,19 @@ struct atmel_tdes_ctx {
int keylen;
u32 key[3*DES_KEY_SIZE / sizeof(u32)];
unsigned long flags;
+
+ u16 block_size;
};
struct atmel_tdes_reqctx {
unsigned long mode;
};
+struct atmel_tdes_dma {
+ struct dma_chan *chan;
+ struct dma_slave_config dma_conf;
+};
+
struct atmel_tdes_dev {
struct list_head list;
unsigned long phys_base;
@@ -99,8 +112,10 @@ struct atmel_tdes_dev {
size_t total;
struct scatterlist *in_sg;
+ unsigned int nb_in_sg;
size_t in_offset;
struct scatterlist *out_sg;
+ unsigned int nb_out_sg;
size_t out_offset;
size_t buflen;
@@ -109,10 +124,16 @@ struct atmel_tdes_dev {
void *buf_in;
int dma_in;
dma_addr_t dma_addr_in;
+ struct atmel_tdes_dma dma_lch_in;
void *buf_out;
int dma_out;
dma_addr_t dma_addr_out;
+ struct atmel_tdes_dma dma_lch_out;
+
+ struct atmel_tdes_caps caps;
+
+ u32 hw_version;
};
struct atmel_tdes_drv {
@@ -207,6 +228,31 @@ static int atmel_tdes_hw_init(struct atmel_tdes_dev *dd)
return 0;
}
+static inline unsigned int atmel_tdes_get_version(struct atmel_tdes_dev *dd)
+{
+ return atmel_tdes_read(dd, TDES_HW_VERSION) & 0x00000fff;
+}
+
+static void atmel_tdes_hw_version_init(struct atmel_tdes_dev *dd)
+{
+ atmel_tdes_hw_init(dd);
+
+ dd->hw_version = atmel_tdes_get_version(dd);
+
+ dev_info(dd->dev,
+ "version: 0x%x\n", dd->hw_version);
+
+ clk_disable_unprepare(dd->iclk);
+}
+
+static void atmel_tdes_dma_callback(void *data)
+{
+ struct atmel_tdes_dev *dd = data;
+
+ /* dma_lch_out - completed */
+ tasklet_schedule(&dd->done_task);
+}
+
static int atmel_tdes_write_ctrl(struct atmel_tdes_dev *dd)
{
int err;
@@ -217,7 +263,9 @@ static int atmel_tdes_write_ctrl(struct atmel_tdes_dev *dd)
if (err)
return err;
- atmel_tdes_write(dd, TDES_PTCR, TDES_PTCR_TXTDIS|TDES_PTCR_RXTDIS);
+ if (!dd->caps.has_dma)
+ atmel_tdes_write(dd, TDES_PTCR,
+ TDES_PTCR_TXTDIS | TDES_PTCR_RXTDIS);
/* MR register must be set before IV registers */
if (dd->ctx->keylen > (DES_KEY_SIZE << 1)) {
@@ -241,6 +289,8 @@ static int atmel_tdes_write_ctrl(struct atmel_tdes_dev *dd)
valmr |= TDES_MR_CFBS_16b;
else if (dd->flags & TDES_FLAGS_CFB32)
valmr |= TDES_MR_CFBS_32b;
+ else if (dd->flags & TDES_FLAGS_CFB64)
+ valmr |= TDES_MR_CFBS_64b;
} else if (dd->flags & TDES_FLAGS_OFB) {
valmr |= TDES_MR_OPMOD_OFB;
}
@@ -262,7 +312,7 @@ static int atmel_tdes_write_ctrl(struct atmel_tdes_dev *dd)
return 0;
}
-static int atmel_tdes_crypt_dma_stop(struct atmel_tdes_dev *dd)
+static int atmel_tdes_crypt_pdc_stop(struct atmel_tdes_dev *dd)
{
int err = 0;
size_t count;
@@ -288,7 +338,7 @@ static int atmel_tdes_crypt_dma_stop(struct atmel_tdes_dev *dd)
return err;
}
-static int atmel_tdes_dma_init(struct atmel_tdes_dev *dd)
+static int atmel_tdes_buff_init(struct atmel_tdes_dev *dd)
{
int err = -ENOMEM;
@@ -333,7 +383,7 @@ err_alloc:
return err;
}
-static void atmel_tdes_dma_cleanup(struct atmel_tdes_dev *dd)
+static void atmel_tdes_buff_cleanup(struct atmel_tdes_dev *dd)
{
dma_unmap_single(dd->dev, dd->dma_addr_out, dd->buflen,
DMA_FROM_DEVICE);
@@ -343,7 +393,7 @@ static void atmel_tdes_dma_cleanup(struct atmel_tdes_dev *dd)
free_page((unsigned long)dd->buf_in);
}
-static int atmel_tdes_crypt_dma(struct crypto_tfm *tfm, dma_addr_t dma_addr_in,
+static int atmel_tdes_crypt_pdc(struct crypto_tfm *tfm, dma_addr_t dma_addr_in,
dma_addr_t dma_addr_out, int length)
{
struct atmel_tdes_ctx *ctx = crypto_tfm_ctx(tfm);
@@ -379,7 +429,76 @@ static int atmel_tdes_crypt_dma(struct crypto_tfm *tfm, dma_addr_t dma_addr_in,
return 0;
}
-static int atmel_tdes_crypt_dma_start(struct atmel_tdes_dev *dd)
+static int atmel_tdes_crypt_dma(struct crypto_tfm *tfm, dma_addr_t dma_addr_in,
+ dma_addr_t dma_addr_out, int length)
+{
+ struct atmel_tdes_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct atmel_tdes_dev *dd = ctx->dd;
+ struct scatterlist sg[2];
+ struct dma_async_tx_descriptor *in_desc, *out_desc;
+
+ dd->dma_size = length;
+
+ if (!(dd->flags & TDES_FLAGS_FAST)) {
+ dma_sync_single_for_device(dd->dev, dma_addr_in, length,
+ DMA_TO_DEVICE);
+ }
+
+ if (dd->flags & TDES_FLAGS_CFB8) {
+ dd->dma_lch_in.dma_conf.dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dd->dma_lch_out.dma_conf.src_addr_width =
+ DMA_SLAVE_BUSWIDTH_1_BYTE;
+ } else if (dd->flags & TDES_FLAGS_CFB16) {
+ dd->dma_lch_in.dma_conf.dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_2_BYTES;
+ dd->dma_lch_out.dma_conf.src_addr_width =
+ DMA_SLAVE_BUSWIDTH_2_BYTES;
+ } else {
+ dd->dma_lch_in.dma_conf.dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dd->dma_lch_out.dma_conf.src_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ }
+
+ dmaengine_slave_config(dd->dma_lch_in.chan, &dd->dma_lch_in.dma_conf);
+ dmaengine_slave_config(dd->dma_lch_out.chan, &dd->dma_lch_out.dma_conf);
+
+ dd->flags |= TDES_FLAGS_DMA;
+
+ sg_init_table(&sg[0], 1);
+ sg_dma_address(&sg[0]) = dma_addr_in;
+ sg_dma_len(&sg[0]) = length;
+
+ sg_init_table(&sg[1], 1);
+ sg_dma_address(&sg[1]) = dma_addr_out;
+ sg_dma_len(&sg[1]) = length;
+
+ in_desc = dmaengine_prep_slave_sg(dd->dma_lch_in.chan, &sg[0],
+ 1, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!in_desc)
+ return -EINVAL;
+
+ out_desc = dmaengine_prep_slave_sg(dd->dma_lch_out.chan, &sg[1],
+ 1, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!out_desc)
+ return -EINVAL;
+
+ out_desc->callback = atmel_tdes_dma_callback;
+ out_desc->callback_param = dd;
+
+ dmaengine_submit(out_desc);
+ dma_async_issue_pending(dd->dma_lch_out.chan);
+
+ dmaengine_submit(in_desc);
+ dma_async_issue_pending(dd->dma_lch_in.chan);
+
+ return 0;
+}
+
+static int atmel_tdes_crypt_start(struct atmel_tdes_dev *dd)
{
struct crypto_tfm *tfm = crypto_ablkcipher_tfm(
crypto_ablkcipher_reqtfm(dd->req));
@@ -387,23 +506,23 @@ static int atmel_tdes_crypt_dma_start(struct atmel_tdes_dev *dd)
size_t count;
dma_addr_t addr_in, addr_out;
- if (sg_is_last(dd->in_sg) && sg_is_last(dd->out_sg)) {
+ if ((!dd->in_offset) && (!dd->out_offset)) {
/* check for alignment */
- in = IS_ALIGNED((u32)dd->in_sg->offset, sizeof(u32));
- out = IS_ALIGNED((u32)dd->out_sg->offset, sizeof(u32));
-
+ in = IS_ALIGNED((u32)dd->in_sg->offset, sizeof(u32)) &&
+ IS_ALIGNED(dd->in_sg->length, dd->ctx->block_size);
+ out = IS_ALIGNED((u32)dd->out_sg->offset, sizeof(u32)) &&
+ IS_ALIGNED(dd->out_sg->length, dd->ctx->block_size);
fast = in && out;
+
+ if (sg_dma_len(dd->in_sg) != sg_dma_len(dd->out_sg))
+ fast = 0;
}
+
if (fast) {
count = min(dd->total, sg_dma_len(dd->in_sg));
count = min(count, sg_dma_len(dd->out_sg));
- if (count != dd->total) {
- pr_err("request length != buffer length\n");
- return -EINVAL;
- }
-
err = dma_map_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE);
if (!err) {
dev_err(dd->dev, "dma_map_sg() error\n");
@@ -433,13 +552,16 @@ static int atmel_tdes_crypt_dma_start(struct atmel_tdes_dev *dd)
addr_out = dd->dma_addr_out;
dd->flags &= ~TDES_FLAGS_FAST;
-
}
dd->total -= count;
- err = atmel_tdes_crypt_dma(tfm, addr_in, addr_out, count);
- if (err) {
+ if (dd->caps.has_dma)
+ err = atmel_tdes_crypt_dma(tfm, addr_in, addr_out, count);
+ else
+ err = atmel_tdes_crypt_pdc(tfm, addr_in, addr_out, count);
+
+ if (err && (dd->flags & TDES_FLAGS_FAST)) {
dma_unmap_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE);
dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_TO_DEVICE);
}
@@ -447,7 +569,6 @@ static int atmel_tdes_crypt_dma_start(struct atmel_tdes_dev *dd)
return err;
}
-
static void atmel_tdes_finish_req(struct atmel_tdes_dev *dd, int err)
{
struct ablkcipher_request *req = dd->req;
@@ -506,7 +627,7 @@ static int atmel_tdes_handle_queue(struct atmel_tdes_dev *dd,
err = atmel_tdes_write_ctrl(dd);
if (!err)
- err = atmel_tdes_crypt_dma_start(dd);
+ err = atmel_tdes_crypt_start(dd);
if (err) {
/* des_task will not finish it, so do it here */
atmel_tdes_finish_req(dd, err);
@@ -516,41 +637,145 @@ static int atmel_tdes_handle_queue(struct atmel_tdes_dev *dd,
return ret;
}
+static int atmel_tdes_crypt_dma_stop(struct atmel_tdes_dev *dd)
+{
+ int err = -EINVAL;
+ size_t count;
+
+ if (dd->flags & TDES_FLAGS_DMA) {
+ err = 0;
+ if (dd->flags & TDES_FLAGS_FAST) {
+ dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_FROM_DEVICE);
+ dma_unmap_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE);
+ } else {
+ dma_sync_single_for_device(dd->dev, dd->dma_addr_out,
+ dd->dma_size, DMA_FROM_DEVICE);
+
+ /* copy data */
+ count = atmel_tdes_sg_copy(&dd->out_sg, &dd->out_offset,
+ dd->buf_out, dd->buflen, dd->dma_size, 1);
+ if (count != dd->dma_size) {
+ err = -EINVAL;
+ pr_err("not all data converted: %u\n", count);
+ }
+ }
+ }
+ return err;
+}
static int atmel_tdes_crypt(struct ablkcipher_request *req, unsigned long mode)
{
struct atmel_tdes_ctx *ctx = crypto_ablkcipher_ctx(
crypto_ablkcipher_reqtfm(req));
struct atmel_tdes_reqctx *rctx = ablkcipher_request_ctx(req);
- struct atmel_tdes_dev *dd;
if (mode & TDES_FLAGS_CFB8) {
if (!IS_ALIGNED(req->nbytes, CFB8_BLOCK_SIZE)) {
pr_err("request size is not exact amount of CFB8 blocks\n");
return -EINVAL;
}
+ ctx->block_size = CFB8_BLOCK_SIZE;
} else if (mode & TDES_FLAGS_CFB16) {
if (!IS_ALIGNED(req->nbytes, CFB16_BLOCK_SIZE)) {
pr_err("request size is not exact amount of CFB16 blocks\n");
return -EINVAL;
}
+ ctx->block_size = CFB16_BLOCK_SIZE;
} else if (mode & TDES_FLAGS_CFB32) {
if (!IS_ALIGNED(req->nbytes, CFB32_BLOCK_SIZE)) {
pr_err("request size is not exact amount of CFB32 blocks\n");
return -EINVAL;
}
- } else if (!IS_ALIGNED(req->nbytes, DES_BLOCK_SIZE)) {
- pr_err("request size is not exact amount of DES blocks\n");
- return -EINVAL;
+ ctx->block_size = CFB32_BLOCK_SIZE;
+ } else {
+ if (!IS_ALIGNED(req->nbytes, DES_BLOCK_SIZE)) {
+ pr_err("request size is not exact amount of DES blocks\n");
+ return -EINVAL;
+ }
+ ctx->block_size = DES_BLOCK_SIZE;
}
- dd = atmel_tdes_find_dev(ctx);
- if (!dd)
+ rctx->mode = mode;
+
+ return atmel_tdes_handle_queue(ctx->dd, req);
+}
+
+static bool atmel_tdes_filter(struct dma_chan *chan, void *slave)
+{
+ struct at_dma_slave *sl = slave;
+
+ if (sl && sl->dma_dev == chan->device->dev) {
+ chan->private = sl;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static int atmel_tdes_dma_init(struct atmel_tdes_dev *dd,
+ struct crypto_platform_data *pdata)
+{
+ int err = -ENOMEM;
+ dma_cap_mask_t mask_in, mask_out;
+
+ if (pdata && pdata->dma_slave->txdata.dma_dev &&
+ pdata->dma_slave->rxdata.dma_dev) {
+
+ /* Try to grab 2 DMA channels */
+ dma_cap_zero(mask_in);
+ dma_cap_set(DMA_SLAVE, mask_in);
+
+ dd->dma_lch_in.chan = dma_request_channel(mask_in,
+ atmel_tdes_filter, &pdata->dma_slave->rxdata);
+
+ if (!dd->dma_lch_in.chan)
+ goto err_dma_in;
+
+ dd->dma_lch_in.dma_conf.direction = DMA_MEM_TO_DEV;
+ dd->dma_lch_in.dma_conf.dst_addr = dd->phys_base +
+ TDES_IDATA1R;
+ dd->dma_lch_in.dma_conf.src_maxburst = 1;
+ dd->dma_lch_in.dma_conf.src_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dd->dma_lch_in.dma_conf.dst_maxburst = 1;
+ dd->dma_lch_in.dma_conf.dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dd->dma_lch_in.dma_conf.device_fc = false;
+
+ dma_cap_zero(mask_out);
+ dma_cap_set(DMA_SLAVE, mask_out);
+ dd->dma_lch_out.chan = dma_request_channel(mask_out,
+ atmel_tdes_filter, &pdata->dma_slave->txdata);
+
+ if (!dd->dma_lch_out.chan)
+ goto err_dma_out;
+
+ dd->dma_lch_out.dma_conf.direction = DMA_DEV_TO_MEM;
+ dd->dma_lch_out.dma_conf.src_addr = dd->phys_base +
+ TDES_ODATA1R;
+ dd->dma_lch_out.dma_conf.src_maxburst = 1;
+ dd->dma_lch_out.dma_conf.src_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dd->dma_lch_out.dma_conf.dst_maxburst = 1;
+ dd->dma_lch_out.dma_conf.dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dd->dma_lch_out.dma_conf.device_fc = false;
+
+ return 0;
+ } else {
return -ENODEV;
+ }
- rctx->mode = mode;
+err_dma_out:
+ dma_release_channel(dd->dma_lch_in.chan);
+err_dma_in:
+ return err;
+}
- return atmel_tdes_handle_queue(dd, req);
+static void atmel_tdes_dma_cleanup(struct atmel_tdes_dev *dd)
+{
+ dma_release_channel(dd->dma_lch_in.chan);
+ dma_release_channel(dd->dma_lch_out.chan);
}
static int atmel_des_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
@@ -590,7 +815,8 @@ static int atmel_tdes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
/*
* HW bug in cfb 3-keys mode.
*/
- if (strstr(alg_name, "cfb") && (keylen != 2*DES_KEY_SIZE)) {
+ if (!ctx->dd->caps.has_cfb_3keys && strstr(alg_name, "cfb")
+ && (keylen != 2*DES_KEY_SIZE)) {
crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
return -EINVAL;
} else if ((keylen != 2*DES_KEY_SIZE) && (keylen != 3*DES_KEY_SIZE)) {
@@ -678,8 +904,15 @@ static int atmel_tdes_ofb_decrypt(struct ablkcipher_request *req)
static int atmel_tdes_cra_init(struct crypto_tfm *tfm)
{
+ struct atmel_tdes_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct atmel_tdes_dev *dd;
+
tfm->crt_ablkcipher.reqsize = sizeof(struct atmel_tdes_reqctx);
+ dd = atmel_tdes_find_dev(ctx);
+ if (!dd)
+ return -ENODEV;
+
return 0;
}
@@ -695,7 +928,7 @@ static struct crypto_alg tdes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = DES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_tdes_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = 0x7,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_tdes_cra_init,
@@ -715,7 +948,7 @@ static struct crypto_alg tdes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = DES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_tdes_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = 0x7,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_tdes_cra_init,
@@ -736,7 +969,7 @@ static struct crypto_alg tdes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = DES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_tdes_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = 0x7,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_tdes_cra_init,
@@ -778,7 +1011,7 @@ static struct crypto_alg tdes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = CFB16_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_tdes_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = 0x1,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_tdes_cra_init,
@@ -799,7 +1032,7 @@ static struct crypto_alg tdes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = CFB32_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_tdes_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = 0x3,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_tdes_cra_init,
@@ -820,7 +1053,7 @@ static struct crypto_alg tdes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = DES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_tdes_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = 0x7,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_tdes_cra_init,
@@ -841,7 +1074,7 @@ static struct crypto_alg tdes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = DES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_tdes_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = 0x7,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_tdes_cra_init,
@@ -861,7 +1094,7 @@ static struct crypto_alg tdes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = DES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_tdes_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = 0x7,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_tdes_cra_init,
@@ -882,7 +1115,7 @@ static struct crypto_alg tdes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = DES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_tdes_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = 0x7,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_tdes_cra_init,
@@ -924,7 +1157,7 @@ static struct crypto_alg tdes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = CFB16_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_tdes_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = 0x1,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_tdes_cra_init,
@@ -945,7 +1178,7 @@ static struct crypto_alg tdes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = CFB32_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_tdes_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = 0x3,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_tdes_cra_init,
@@ -966,7 +1199,7 @@ static struct crypto_alg tdes_algs[] = {
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = DES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_tdes_ctx),
- .cra_alignmask = 0,
+ .cra_alignmask = 0x7,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = atmel_tdes_cra_init,
@@ -994,14 +1227,24 @@ static void atmel_tdes_done_task(unsigned long data)
struct atmel_tdes_dev *dd = (struct atmel_tdes_dev *) data;
int err;
- err = atmel_tdes_crypt_dma_stop(dd);
+ if (!(dd->flags & TDES_FLAGS_DMA))
+ err = atmel_tdes_crypt_pdc_stop(dd);
+ else
+ err = atmel_tdes_crypt_dma_stop(dd);
err = dd->err ? : err;
if (dd->total && !err) {
- err = atmel_tdes_crypt_dma_start(dd);
+ if (dd->flags & TDES_FLAGS_FAST) {
+ dd->in_sg = sg_next(dd->in_sg);
+ dd->out_sg = sg_next(dd->out_sg);
+ if (!dd->in_sg || !dd->out_sg)
+ err = -EINVAL;
+ }
if (!err)
- return;
+ err = atmel_tdes_crypt_start(dd);
+ if (!err)
+ return; /* DMA started. Not fininishing. */
}
atmel_tdes_finish_req(dd, err);
@@ -1053,9 +1296,31 @@ err_tdes_algs:
return err;
}
+static void atmel_tdes_get_cap(struct atmel_tdes_dev *dd)
+{
+
+ dd->caps.has_dma = 0;
+ dd->caps.has_cfb_3keys = 0;
+
+ /* keep only major version number */
+ switch (dd->hw_version & 0xf00) {
+ case 0x700:
+ dd->caps.has_dma = 1;
+ dd->caps.has_cfb_3keys = 1;
+ break;
+ case 0x600:
+ break;
+ default:
+ dev_warn(dd->dev,
+ "Unmanaged tdes version, set minimum capabilities\n");
+ break;
+ }
+}
+
static int __devinit atmel_tdes_probe(struct platform_device *pdev)
{
struct atmel_tdes_dev *tdes_dd;
+ struct crypto_platform_data *pdata;
struct device *dev = &pdev->dev;
struct resource *tdes_res;
unsigned long tdes_phys_size;
@@ -1109,7 +1374,7 @@ static int __devinit atmel_tdes_probe(struct platform_device *pdev)
}
/* Initializing the clock */
- tdes_dd->iclk = clk_get(&pdev->dev, NULL);
+ tdes_dd->iclk = clk_get(&pdev->dev, "tdes_clk");
if (IS_ERR(tdes_dd->iclk)) {
dev_err(dev, "clock intialization failed.\n");
err = PTR_ERR(tdes_dd->iclk);
@@ -1123,9 +1388,25 @@ static int __devinit atmel_tdes_probe(struct platform_device *pdev)
goto tdes_io_err;
}
- err = atmel_tdes_dma_init(tdes_dd);
+ atmel_tdes_hw_version_init(tdes_dd);
+
+ atmel_tdes_get_cap(tdes_dd);
+
+ err = atmel_tdes_buff_init(tdes_dd);
if (err)
- goto err_tdes_dma;
+ goto err_tdes_buff;
+
+ if (tdes_dd->caps.has_dma) {
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "platform data not available\n");
+ err = -ENXIO;
+ goto err_pdata;
+ }
+ err = atmel_tdes_dma_init(tdes_dd, pdata);
+ if (err)
+ goto err_tdes_dma;
+ }
spin_lock(&atmel_tdes.lock);
list_add_tail(&tdes_dd->list, &atmel_tdes.dev_list);
@@ -1143,8 +1424,12 @@ err_algs:
spin_lock(&atmel_tdes.lock);
list_del(&tdes_dd->list);
spin_unlock(&atmel_tdes.lock);
- atmel_tdes_dma_cleanup(tdes_dd);
+ if (tdes_dd->caps.has_dma)
+ atmel_tdes_dma_cleanup(tdes_dd);
err_tdes_dma:
+err_pdata:
+ atmel_tdes_buff_cleanup(tdes_dd);
+err_tdes_buff:
iounmap(tdes_dd->io_base);
tdes_io_err:
clk_put(tdes_dd->iclk);
@@ -1178,7 +1463,10 @@ static int __devexit atmel_tdes_remove(struct platform_device *pdev)
tasklet_kill(&tdes_dd->done_task);
tasklet_kill(&tdes_dd->queue_task);
- atmel_tdes_dma_cleanup(tdes_dd);
+ if (tdes_dd->caps.has_dma)
+ atmel_tdes_dma_cleanup(tdes_dd);
+
+ atmel_tdes_buff_cleanup(tdes_dd);
iounmap(tdes_dd->io_base);
--
1.7.6.5
^ permalink raw reply related
* [PATCH 5/5] crypto: Atmel SHA driver: add support for latest release of the IP (0x410)
From: Nicolas Royer @ 2012-11-06 16:33 UTC (permalink / raw)
To: linux-arm-kernel
Updates from IP release 0x320 to 0x400:
- add DMA support (previous IP revision use PDC)
- add DMA double input buffer support
- add SHA224 support
Update from IP release 0x400 to 0x410:
- add SHA384 and SHA512 support
Signed-off-by: Nicolas Royer <nicolas@eukrea.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Eric B?nard <eric@eukrea.com>
Tested-by: Eric B?nard <eric@eukrea.com>
---
drivers/crypto/Kconfig | 8 +-
drivers/crypto/atmel-sha-regs.h | 7 +-
drivers/crypto/atmel-sha.c | 586 ++++++++++++++++++++++++++++++++-------
3 files changed, 497 insertions(+), 104 deletions(-)
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 308c7fb..0f4076b 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -360,15 +360,17 @@ config CRYPTO_DEV_ATMEL_TDES
will be called atmel-tdes.
config CRYPTO_DEV_ATMEL_SHA
- tristate "Support for Atmel SHA1/SHA256 hw accelerator"
+ tristate "Support for Atmel SHA hw accelerator"
depends on ARCH_AT91
select CRYPTO_SHA1
select CRYPTO_SHA256
+ select CRYPTO_SHA512
select CRYPTO_ALGAPI
help
- Some Atmel processors have SHA1/SHA256 hw accelerator.
+ Some Atmel processors have SHA1/SHA224/SHA256/SHA384/SHA512
+ hw accelerator.
Select this if you want to use the Atmel module for
- SHA1/SHA256 algorithms.
+ SHA1/SHA224/SHA256/SHA384/SHA512 algorithms.
To compile this driver as a module, choose M here: the module
will be called atmel-sha.
diff --git a/drivers/crypto/atmel-sha-regs.h b/drivers/crypto/atmel-sha-regs.h
index dc53a20..83b2d74 100644
--- a/drivers/crypto/atmel-sha-regs.h
+++ b/drivers/crypto/atmel-sha-regs.h
@@ -14,10 +14,13 @@
#define SHA_MR_MODE_MANUAL 0x0
#define SHA_MR_MODE_AUTO 0x1
#define SHA_MR_MODE_PDC 0x2
-#define SHA_MR_DUALBUFF (1 << 3)
#define SHA_MR_PROCDLY (1 << 4)
#define SHA_MR_ALGO_SHA1 (0 << 8)
#define SHA_MR_ALGO_SHA256 (1 << 8)
+#define SHA_MR_ALGO_SHA384 (2 << 8)
+#define SHA_MR_ALGO_SHA512 (3 << 8)
+#define SHA_MR_ALGO_SHA224 (4 << 8)
+#define SHA_MR_DUALBUFF (1 << 16)
#define SHA_IER 0x10
#define SHA_IDR 0x14
@@ -33,6 +36,8 @@
#define SHA_ISR_URAT_MR (0x2 << 12)
#define SHA_ISR_URAT_WO (0x5 << 12)
+#define SHA_HW_VERSION 0xFC
+
#define SHA_TPR 0x108
#define SHA_TCR 0x10C
#define SHA_TNPR 0x118
diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c
index bcdf55f..408ea25 100644
--- a/drivers/crypto/atmel-sha.c
+++ b/drivers/crypto/atmel-sha.c
@@ -38,6 +38,7 @@
#include <crypto/sha.h>
#include <crypto/hash.h>
#include <crypto/internal/hash.h>
+#include <linux/platform_data/crypto-atmel.h>
#include "atmel-sha-regs.h"
/* SHA flags */
@@ -52,11 +53,12 @@
#define SHA_FLAGS_FINUP BIT(16)
#define SHA_FLAGS_SG BIT(17)
#define SHA_FLAGS_SHA1 BIT(18)
-#define SHA_FLAGS_SHA256 BIT(19)
-#define SHA_FLAGS_ERROR BIT(20)
-#define SHA_FLAGS_PAD BIT(21)
-
-#define SHA_FLAGS_DUALBUFF BIT(24)
+#define SHA_FLAGS_SHA224 BIT(19)
+#define SHA_FLAGS_SHA256 BIT(20)
+#define SHA_FLAGS_SHA384 BIT(21)
+#define SHA_FLAGS_SHA512 BIT(22)
+#define SHA_FLAGS_ERROR BIT(23)
+#define SHA_FLAGS_PAD BIT(24)
#define SHA_OP_UPDATE 1
#define SHA_OP_FINAL 2
@@ -65,6 +67,12 @@
#define ATMEL_SHA_DMA_THRESHOLD 56
+struct atmel_sha_caps {
+ bool has_dma;
+ bool has_dualbuff;
+ bool has_sha224;
+ bool has_sha_384_512;
+};
struct atmel_sha_dev;
@@ -73,8 +81,8 @@ struct atmel_sha_reqctx {
unsigned long flags;
unsigned long op;
- u8 digest[SHA256_DIGEST_SIZE] __aligned(sizeof(u32));
- size_t digcnt;
+ u8 digest[SHA512_DIGEST_SIZE] __aligned(sizeof(u32));
+ u64 digcnt[2];
size_t bufcnt;
size_t buflen;
dma_addr_t dma_addr;
@@ -84,6 +92,8 @@ struct atmel_sha_reqctx {
unsigned int offset; /* offset in current sg */
unsigned int total; /* total request */
+ size_t block_size;
+
u8 buffer[0] __aligned(sizeof(u32));
};
@@ -97,7 +107,12 @@ struct atmel_sha_ctx {
};
-#define ATMEL_SHA_QUEUE_LENGTH 1
+#define ATMEL_SHA_QUEUE_LENGTH 50
+
+struct atmel_sha_dma {
+ struct dma_chan *chan;
+ struct dma_slave_config dma_conf;
+};
struct atmel_sha_dev {
struct list_head list;
@@ -114,6 +129,12 @@ struct atmel_sha_dev {
unsigned long flags;
struct crypto_queue queue;
struct ahash_request *req;
+
+ struct atmel_sha_dma dma_lch_in;
+
+ struct atmel_sha_caps caps;
+
+ u32 hw_version;
};
struct atmel_sha_drv {
@@ -137,14 +158,6 @@ static inline void atmel_sha_write(struct atmel_sha_dev *dd,
writel_relaxed(value, dd->io_base + offset);
}
-static void atmel_sha_dualbuff_test(struct atmel_sha_dev *dd)
-{
- atmel_sha_write(dd, SHA_MR, SHA_MR_DUALBUFF);
-
- if (atmel_sha_read(dd, SHA_MR) & SHA_MR_DUALBUFF)
- dd->flags |= SHA_FLAGS_DUALBUFF;
-}
-
static size_t atmel_sha_append_sg(struct atmel_sha_reqctx *ctx)
{
size_t count;
@@ -176,31 +189,58 @@ static size_t atmel_sha_append_sg(struct atmel_sha_reqctx *ctx)
}
/*
- * The purpose of this padding is to ensure that the padded message
- * is a multiple of 512 bits. The bit "1" is appended at the end of
- * the message followed by "padlen-1" zero bits. Then a 64 bits block
- * equals to the message length in bits is appended.
+ * The purpose of this padding is to ensure that the padded message is a
+ * multiple of 512 bits (SHA1/SHA224/SHA256) or 1024 bits (SHA384/SHA512).
+ * The bit "1" is appended at the end of the message followed by
+ * "padlen-1" zero bits. Then a 64 bits block (SHA1/SHA224/SHA256) or
+ * 128 bits block (SHA384/SHA512) equals to the message length in bits
+ * is appended.
*
- * padlen is calculated as followed:
+ * For SHA1/SHA224/SHA256, padlen is calculated as followed:
* - if message length < 56 bytes then padlen = 56 - message length
* - else padlen = 64 + 56 - message length
+ *
+ * For SHA384/SHA512, padlen is calculated as followed:
+ * - if message length < 112 bytes then padlen = 112 - message length
+ * - else padlen = 128 + 112 - message length
*/
static void atmel_sha_fill_padding(struct atmel_sha_reqctx *ctx, int length)
{
unsigned int index, padlen;
- u64 bits;
- u64 size;
-
- bits = (ctx->bufcnt + ctx->digcnt + length) << 3;
- size = cpu_to_be64(bits);
-
- index = ctx->bufcnt & 0x3f;
- padlen = (index < 56) ? (56 - index) : ((64+56) - index);
- *(ctx->buffer + ctx->bufcnt) = 0x80;
- memset(ctx->buffer + ctx->bufcnt + 1, 0, padlen-1);
- memcpy(ctx->buffer + ctx->bufcnt + padlen, &size, 8);
- ctx->bufcnt += padlen + 8;
- ctx->flags |= SHA_FLAGS_PAD;
+ u64 bits[2];
+ u64 size[2];
+
+ size[0] = ctx->digcnt[0];
+ size[1] = ctx->digcnt[1];
+
+ size[0] += ctx->bufcnt;
+ if (size[0] < ctx->bufcnt)
+ size[1]++;
+
+ size[0] += length;
+ if (size[0] < length)
+ size[1]++;
+
+ bits[1] = cpu_to_be64(size[0] << 3);
+ bits[0] = cpu_to_be64(size[1] << 3 | size[0] >> 61);
+
+ if (ctx->flags & (SHA_FLAGS_SHA384 | SHA_FLAGS_SHA512)) {
+ index = ctx->bufcnt & 0x7f;
+ padlen = (index < 112) ? (112 - index) : ((128+112) - index);
+ *(ctx->buffer + ctx->bufcnt) = 0x80;
+ memset(ctx->buffer + ctx->bufcnt + 1, 0, padlen-1);
+ memcpy(ctx->buffer + ctx->bufcnt + padlen, bits, 16);
+ ctx->bufcnt += padlen + 16;
+ ctx->flags |= SHA_FLAGS_PAD;
+ } else {
+ index = ctx->bufcnt & 0x3f;
+ padlen = (index < 56) ? (56 - index) : ((64+56) - index);
+ *(ctx->buffer + ctx->bufcnt) = 0x80;
+ memset(ctx->buffer + ctx->bufcnt + 1, 0, padlen-1);
+ memcpy(ctx->buffer + ctx->bufcnt + padlen, &bits[1], 8);
+ ctx->bufcnt += padlen + 8;
+ ctx->flags |= SHA_FLAGS_PAD;
+ }
}
static int atmel_sha_init(struct ahash_request *req)
@@ -231,13 +271,35 @@ static int atmel_sha_init(struct ahash_request *req)
dev_dbg(dd->dev, "init: digest size: %d\n",
crypto_ahash_digestsize(tfm));
- if (crypto_ahash_digestsize(tfm) == SHA1_DIGEST_SIZE)
+ switch (crypto_ahash_digestsize(tfm)) {
+ case SHA1_DIGEST_SIZE:
ctx->flags |= SHA_FLAGS_SHA1;
- else if (crypto_ahash_digestsize(tfm) == SHA256_DIGEST_SIZE)
+ ctx->block_size = SHA1_BLOCK_SIZE;
+ break;
+ case SHA224_DIGEST_SIZE:
+ ctx->flags |= SHA_FLAGS_SHA224;
+ ctx->block_size = SHA224_BLOCK_SIZE;
+ break;
+ case SHA256_DIGEST_SIZE:
ctx->flags |= SHA_FLAGS_SHA256;
+ ctx->block_size = SHA256_BLOCK_SIZE;
+ break;
+ case SHA384_DIGEST_SIZE:
+ ctx->flags |= SHA_FLAGS_SHA384;
+ ctx->block_size = SHA384_BLOCK_SIZE;
+ break;
+ case SHA512_DIGEST_SIZE:
+ ctx->flags |= SHA_FLAGS_SHA512;
+ ctx->block_size = SHA512_BLOCK_SIZE;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
ctx->bufcnt = 0;
- ctx->digcnt = 0;
+ ctx->digcnt[0] = 0;
+ ctx->digcnt[1] = 0;
ctx->buflen = SHA_BUFFER_LEN;
return 0;
@@ -249,19 +311,28 @@ static void atmel_sha_write_ctrl(struct atmel_sha_dev *dd, int dma)
u32 valcr = 0, valmr = SHA_MR_MODE_AUTO;
if (likely(dma)) {
- atmel_sha_write(dd, SHA_IER, SHA_INT_TXBUFE);
+ if (!dd->caps.has_dma)
+ atmel_sha_write(dd, SHA_IER, SHA_INT_TXBUFE);
valmr = SHA_MR_MODE_PDC;
- if (dd->flags & SHA_FLAGS_DUALBUFF)
- valmr = SHA_MR_DUALBUFF;
+ if (dd->caps.has_dualbuff)
+ valmr |= SHA_MR_DUALBUFF;
} else {
atmel_sha_write(dd, SHA_IER, SHA_INT_DATARDY);
}
- if (ctx->flags & SHA_FLAGS_SHA256)
+ if (ctx->flags & SHA_FLAGS_SHA1)
+ valmr |= SHA_MR_ALGO_SHA1;
+ else if (ctx->flags & SHA_FLAGS_SHA224)
+ valmr |= SHA_MR_ALGO_SHA224;
+ else if (ctx->flags & SHA_FLAGS_SHA256)
valmr |= SHA_MR_ALGO_SHA256;
+ else if (ctx->flags & SHA_FLAGS_SHA384)
+ valmr |= SHA_MR_ALGO_SHA384;
+ else if (ctx->flags & SHA_FLAGS_SHA512)
+ valmr |= SHA_MR_ALGO_SHA512;
/* Setting CR_FIRST only for the first iteration */
- if (!ctx->digcnt)
+ if (!(ctx->digcnt[0] || ctx->digcnt[1]))
valcr = SHA_CR_FIRST;
atmel_sha_write(dd, SHA_CR, valcr);
@@ -275,13 +346,15 @@ static int atmel_sha_xmit_cpu(struct atmel_sha_dev *dd, const u8 *buf,
int count, len32;
const u32 *buffer = (const u32 *)buf;
- dev_dbg(dd->dev, "xmit_cpu: digcnt: %d, length: %d, final: %d\n",
- ctx->digcnt, length, final);
+ dev_dbg(dd->dev, "xmit_cpu: digcnt: 0x%llx 0x%llx, length: %d, final: %d\n",
+ ctx->digcnt[1], ctx->digcnt[0], length, final);
atmel_sha_write_ctrl(dd, 0);
/* should be non-zero before next lines to disable clocks later */
- ctx->digcnt += length;
+ ctx->digcnt[0] += length;
+ if (ctx->digcnt[0] < length)
+ ctx->digcnt[1]++;
if (final)
dd->flags |= SHA_FLAGS_FINAL; /* catch last interrupt */
@@ -302,8 +375,8 @@ static int atmel_sha_xmit_pdc(struct atmel_sha_dev *dd, dma_addr_t dma_addr1,
struct atmel_sha_reqctx *ctx = ahash_request_ctx(dd->req);
int len32;
- dev_dbg(dd->dev, "xmit_pdc: digcnt: %d, length: %d, final: %d\n",
- ctx->digcnt, length1, final);
+ dev_dbg(dd->dev, "xmit_pdc: digcnt: 0x%llx 0x%llx, length: %d, final: %d\n",
+ ctx->digcnt[1], ctx->digcnt[0], length1, final);
len32 = DIV_ROUND_UP(length1, sizeof(u32));
atmel_sha_write(dd, SHA_PTCR, SHA_PTCR_TXTDIS);
@@ -317,7 +390,9 @@ static int atmel_sha_xmit_pdc(struct atmel_sha_dev *dd, dma_addr_t dma_addr1,
atmel_sha_write_ctrl(dd, 1);
/* should be non-zero before next lines to disable clocks later */
- ctx->digcnt += length1;
+ ctx->digcnt[0] += length1;
+ if (ctx->digcnt[0] < length1)
+ ctx->digcnt[1]++;
if (final)
dd->flags |= SHA_FLAGS_FINAL; /* catch last interrupt */
@@ -330,6 +405,86 @@ static int atmel_sha_xmit_pdc(struct atmel_sha_dev *dd, dma_addr_t dma_addr1,
return -EINPROGRESS;
}
+static void atmel_sha_dma_callback(void *data)
+{
+ struct atmel_sha_dev *dd = data;
+
+ /* dma_lch_in - completed - wait DATRDY */
+ atmel_sha_write(dd, SHA_IER, SHA_INT_DATARDY);
+}
+
+static int atmel_sha_xmit_dma(struct atmel_sha_dev *dd, dma_addr_t dma_addr1,
+ size_t length1, dma_addr_t dma_addr2, size_t length2, int final)
+{
+ struct atmel_sha_reqctx *ctx = ahash_request_ctx(dd->req);
+ struct dma_async_tx_descriptor *in_desc;
+ struct scatterlist sg[2];
+
+ dev_dbg(dd->dev, "xmit_dma: digcnt: 0x%llx 0x%llx, length: %d, final: %d\n",
+ ctx->digcnt[1], ctx->digcnt[0], length1, final);
+
+ if (ctx->flags & (SHA_FLAGS_SHA1 | SHA_FLAGS_SHA224 |
+ SHA_FLAGS_SHA256)) {
+ dd->dma_lch_in.dma_conf.src_maxburst = 16;
+ dd->dma_lch_in.dma_conf.dst_maxburst = 16;
+ } else {
+ dd->dma_lch_in.dma_conf.src_maxburst = 32;
+ dd->dma_lch_in.dma_conf.dst_maxburst = 32;
+ }
+
+ dmaengine_slave_config(dd->dma_lch_in.chan, &dd->dma_lch_in.dma_conf);
+
+ if (length2) {
+ sg_init_table(sg, 2);
+ sg_dma_address(&sg[0]) = dma_addr1;
+ sg_dma_len(&sg[0]) = length1;
+ sg_dma_address(&sg[1]) = dma_addr2;
+ sg_dma_len(&sg[1]) = length2;
+ in_desc = dmaengine_prep_slave_sg(dd->dma_lch_in.chan, sg, 2,
+ DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ } else {
+ sg_init_table(sg, 1);
+ sg_dma_address(&sg[0]) = dma_addr1;
+ sg_dma_len(&sg[0]) = length1;
+ in_desc = dmaengine_prep_slave_sg(dd->dma_lch_in.chan, sg, 1,
+ DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ }
+ if (!in_desc)
+ return -EINVAL;
+
+ in_desc->callback = atmel_sha_dma_callback;
+ in_desc->callback_param = dd;
+
+ atmel_sha_write_ctrl(dd, 1);
+
+ /* should be non-zero before next lines to disable clocks later */
+ ctx->digcnt[0] += length1;
+ if (ctx->digcnt[0] < length1)
+ ctx->digcnt[1]++;
+
+ if (final)
+ dd->flags |= SHA_FLAGS_FINAL; /* catch last interrupt */
+
+ dd->flags |= SHA_FLAGS_DMA_ACTIVE;
+
+ /* Start DMA transfer */
+ dmaengine_submit(in_desc);
+ dma_async_issue_pending(dd->dma_lch_in.chan);
+
+ return -EINPROGRESS;
+}
+
+static int atmel_sha_xmit_start(struct atmel_sha_dev *dd, dma_addr_t dma_addr1,
+ size_t length1, dma_addr_t dma_addr2, size_t length2, int final)
+{
+ if (dd->caps.has_dma)
+ return atmel_sha_xmit_dma(dd, dma_addr1, length1,
+ dma_addr2, length2, final);
+ else
+ return atmel_sha_xmit_pdc(dd, dma_addr1, length1,
+ dma_addr2, length2, final);
+}
+
static int atmel_sha_update_cpu(struct atmel_sha_dev *dd)
{
struct atmel_sha_reqctx *ctx = ahash_request_ctx(dd->req);
@@ -337,7 +492,6 @@ static int atmel_sha_update_cpu(struct atmel_sha_dev *dd)
atmel_sha_append_sg(ctx);
atmel_sha_fill_padding(ctx, 0);
-
bufcnt = ctx->bufcnt;
ctx->bufcnt = 0;
@@ -349,17 +503,17 @@ static int atmel_sha_xmit_dma_map(struct atmel_sha_dev *dd,
size_t length, int final)
{
ctx->dma_addr = dma_map_single(dd->dev, ctx->buffer,
- ctx->buflen + SHA1_BLOCK_SIZE, DMA_TO_DEVICE);
+ ctx->buflen + ctx->block_size, DMA_TO_DEVICE);
if (dma_mapping_error(dd->dev, ctx->dma_addr)) {
dev_err(dd->dev, "dma %u bytes error\n", ctx->buflen +
- SHA1_BLOCK_SIZE);
+ ctx->block_size);
return -EINVAL;
}
ctx->flags &= ~SHA_FLAGS_SG;
/* next call does not fail... so no unmap in the case of error */
- return atmel_sha_xmit_pdc(dd, ctx->dma_addr, length, 0, 0, final);
+ return atmel_sha_xmit_start(dd, ctx->dma_addr, length, 0, 0, final);
}
static int atmel_sha_update_dma_slow(struct atmel_sha_dev *dd)
@@ -372,8 +526,8 @@ static int atmel_sha_update_dma_slow(struct atmel_sha_dev *dd)
final = (ctx->flags & SHA_FLAGS_FINUP) && !ctx->total;
- dev_dbg(dd->dev, "slow: bufcnt: %u, digcnt: %d, final: %d\n",
- ctx->bufcnt, ctx->digcnt, final);
+ dev_dbg(dd->dev, "slow: bufcnt: %u, digcnt: 0x%llx 0x%llx, final: %d\n",
+ ctx->bufcnt, ctx->digcnt[1], ctx->digcnt[0], final);
if (final)
atmel_sha_fill_padding(ctx, 0);
@@ -400,30 +554,25 @@ static int atmel_sha_update_dma_start(struct atmel_sha_dev *dd)
if (ctx->bufcnt || ctx->offset)
return atmel_sha_update_dma_slow(dd);
- dev_dbg(dd->dev, "fast: digcnt: %d, bufcnt: %u, total: %u\n",
- ctx->digcnt, ctx->bufcnt, ctx->total);
+ dev_dbg(dd->dev, "fast: digcnt: 0x%llx 0x%llx, bufcnt: %u, total: %u\n",
+ ctx->digcnt[1], ctx->digcnt[0], ctx->bufcnt, ctx->total);
sg = ctx->sg;
if (!IS_ALIGNED(sg->offset, sizeof(u32)))
return atmel_sha_update_dma_slow(dd);
- if (!sg_is_last(sg) && !IS_ALIGNED(sg->length, SHA1_BLOCK_SIZE))
- /* size is not SHA1_BLOCK_SIZE aligned */
+ if (!sg_is_last(sg) && !IS_ALIGNED(sg->length, ctx->block_size))
+ /* size is not ctx->block_size aligned */
return atmel_sha_update_dma_slow(dd);
length = min(ctx->total, sg->length);
if (sg_is_last(sg)) {
if (!(ctx->flags & SHA_FLAGS_FINUP)) {
- /* not last sg must be SHA1_BLOCK_SIZE aligned */
- tail = length & (SHA1_BLOCK_SIZE - 1);
+ /* not last sg must be ctx->block_size aligned */
+ tail = length & (ctx->block_size - 1);
length -= tail;
- if (length == 0) {
- /* offset where to start slow */
- ctx->offset = length;
- return atmel_sha_update_dma_slow(dd);
- }
}
}
@@ -434,7 +583,7 @@ static int atmel_sha_update_dma_start(struct atmel_sha_dev *dd)
/* Add padding */
if (final) {
- tail = length & (SHA1_BLOCK_SIZE - 1);
+ tail = length & (ctx->block_size - 1);
length -= tail;
ctx->total += tail;
ctx->offset = length; /* offset where to start slow */
@@ -445,10 +594,10 @@ static int atmel_sha_update_dma_start(struct atmel_sha_dev *dd)
atmel_sha_fill_padding(ctx, length);
ctx->dma_addr = dma_map_single(dd->dev, ctx->buffer,
- ctx->buflen + SHA1_BLOCK_SIZE, DMA_TO_DEVICE);
+ ctx->buflen + ctx->block_size, DMA_TO_DEVICE);
if (dma_mapping_error(dd->dev, ctx->dma_addr)) {
dev_err(dd->dev, "dma %u bytes error\n",
- ctx->buflen + SHA1_BLOCK_SIZE);
+ ctx->buflen + ctx->block_size);
return -EINVAL;
}
@@ -456,7 +605,7 @@ static int atmel_sha_update_dma_start(struct atmel_sha_dev *dd)
ctx->flags &= ~SHA_FLAGS_SG;
count = ctx->bufcnt;
ctx->bufcnt = 0;
- return atmel_sha_xmit_pdc(dd, ctx->dma_addr, count, 0,
+ return atmel_sha_xmit_start(dd, ctx->dma_addr, count, 0,
0, final);
} else {
ctx->sg = sg;
@@ -470,7 +619,7 @@ static int atmel_sha_update_dma_start(struct atmel_sha_dev *dd)
count = ctx->bufcnt;
ctx->bufcnt = 0;
- return atmel_sha_xmit_pdc(dd, sg_dma_address(ctx->sg),
+ return atmel_sha_xmit_start(dd, sg_dma_address(ctx->sg),
length, ctx->dma_addr, count, final);
}
}
@@ -483,7 +632,7 @@ static int atmel_sha_update_dma_start(struct atmel_sha_dev *dd)
ctx->flags |= SHA_FLAGS_SG;
/* next call does not fail... so no unmap in the case of error */
- return atmel_sha_xmit_pdc(dd, sg_dma_address(ctx->sg), length, 0,
+ return atmel_sha_xmit_start(dd, sg_dma_address(ctx->sg), length, 0,
0, final);
}
@@ -498,12 +647,13 @@ static int atmel_sha_update_dma_stop(struct atmel_sha_dev *dd)
if (ctx->sg)
ctx->offset = 0;
}
- if (ctx->flags & SHA_FLAGS_PAD)
+ if (ctx->flags & SHA_FLAGS_PAD) {
dma_unmap_single(dd->dev, ctx->dma_addr,
- ctx->buflen + SHA1_BLOCK_SIZE, DMA_TO_DEVICE);
+ ctx->buflen + ctx->block_size, DMA_TO_DEVICE);
+ }
} else {
dma_unmap_single(dd->dev, ctx->dma_addr, ctx->buflen +
- SHA1_BLOCK_SIZE, DMA_TO_DEVICE);
+ ctx->block_size, DMA_TO_DEVICE);
}
return 0;
@@ -515,8 +665,8 @@ static int atmel_sha_update_req(struct atmel_sha_dev *dd)
struct atmel_sha_reqctx *ctx = ahash_request_ctx(req);
int err;
- dev_dbg(dd->dev, "update_req: total: %u, digcnt: %d, finup: %d\n",
- ctx->total, ctx->digcnt, (ctx->flags & SHA_FLAGS_FINUP) != 0);
+ dev_dbg(dd->dev, "update_req: total: %u, digcnt: 0x%llx 0x%llx\n",
+ ctx->total, ctx->digcnt[1], ctx->digcnt[0]);
if (ctx->flags & SHA_FLAGS_CPU)
err = atmel_sha_update_cpu(dd);
@@ -524,8 +674,8 @@ static int atmel_sha_update_req(struct atmel_sha_dev *dd)
err = atmel_sha_update_dma_start(dd);
/* wait for dma completion before can take more data */
- dev_dbg(dd->dev, "update: err: %d, digcnt: %d\n",
- err, ctx->digcnt);
+ dev_dbg(dd->dev, "update: err: %d, digcnt: 0x%llx 0%llx\n",
+ err, ctx->digcnt[1], ctx->digcnt[0]);
return err;
}
@@ -562,12 +712,21 @@ static void atmel_sha_copy_hash(struct ahash_request *req)
u32 *hash = (u32 *)ctx->digest;
int i;
- if (likely(ctx->flags & SHA_FLAGS_SHA1))
+ if (ctx->flags & SHA_FLAGS_SHA1)
for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(u32); i++)
hash[i] = atmel_sha_read(ctx->dd, SHA_REG_DIGEST(i));
- else
+ else if (ctx->flags & SHA_FLAGS_SHA224)
+ for (i = 0; i < SHA224_DIGEST_SIZE / sizeof(u32); i++)
+ hash[i] = atmel_sha_read(ctx->dd, SHA_REG_DIGEST(i));
+ else if (ctx->flags & SHA_FLAGS_SHA256)
for (i = 0; i < SHA256_DIGEST_SIZE / sizeof(u32); i++)
hash[i] = atmel_sha_read(ctx->dd, SHA_REG_DIGEST(i));
+ else if (ctx->flags & SHA_FLAGS_SHA384)
+ for (i = 0; i < SHA384_DIGEST_SIZE / sizeof(u32); i++)
+ hash[i] = atmel_sha_read(ctx->dd, SHA_REG_DIGEST(i));
+ else
+ for (i = 0; i < SHA512_DIGEST_SIZE / sizeof(u32); i++)
+ hash[i] = atmel_sha_read(ctx->dd, SHA_REG_DIGEST(i));
}
static void atmel_sha_copy_ready_hash(struct ahash_request *req)
@@ -577,10 +736,16 @@ static void atmel_sha_copy_ready_hash(struct ahash_request *req)
if (!req->result)
return;
- if (likely(ctx->flags & SHA_FLAGS_SHA1))
+ if (ctx->flags & SHA_FLAGS_SHA1)
memcpy(req->result, ctx->digest, SHA1_DIGEST_SIZE);
- else
+ else if (ctx->flags & SHA_FLAGS_SHA224)
+ memcpy(req->result, ctx->digest, SHA224_DIGEST_SIZE);
+ else if (ctx->flags & SHA_FLAGS_SHA256)
memcpy(req->result, ctx->digest, SHA256_DIGEST_SIZE);
+ else if (ctx->flags & SHA_FLAGS_SHA384)
+ memcpy(req->result, ctx->digest, SHA384_DIGEST_SIZE);
+ else
+ memcpy(req->result, ctx->digest, SHA512_DIGEST_SIZE);
}
static int atmel_sha_finish(struct ahash_request *req)
@@ -589,11 +754,11 @@ static int atmel_sha_finish(struct ahash_request *req)
struct atmel_sha_dev *dd = ctx->dd;
int err = 0;
- if (ctx->digcnt)
+ if (ctx->digcnt[0] || ctx->digcnt[1])
atmel_sha_copy_ready_hash(req);
- dev_dbg(dd->dev, "digcnt: %d, bufcnt: %d\n", ctx->digcnt,
- ctx->bufcnt);
+ dev_dbg(dd->dev, "digcnt: 0x%llx 0x%llx, bufcnt: %d\n", ctx->digcnt[1],
+ ctx->digcnt[0], ctx->bufcnt);
return err;
}
@@ -628,9 +793,8 @@ static int atmel_sha_hw_init(struct atmel_sha_dev *dd)
{
clk_prepare_enable(dd->iclk);
- if (SHA_FLAGS_INIT & dd->flags) {
+ if (!(SHA_FLAGS_INIT & dd->flags)) {
atmel_sha_write(dd, SHA_CR, SHA_CR_SWRST);
- atmel_sha_dualbuff_test(dd);
dd->flags |= SHA_FLAGS_INIT;
dd->err = 0;
}
@@ -638,6 +802,23 @@ static int atmel_sha_hw_init(struct atmel_sha_dev *dd)
return 0;
}
+static inline unsigned int atmel_sha_get_version(struct atmel_sha_dev *dd)
+{
+ return atmel_sha_read(dd, SHA_HW_VERSION) & 0x00000fff;
+}
+
+static void atmel_sha_hw_version_init(struct atmel_sha_dev *dd)
+{
+ atmel_sha_hw_init(dd);
+
+ dd->hw_version = atmel_sha_get_version(dd);
+
+ dev_info(dd->dev,
+ "version: 0x%x\n", dd->hw_version);
+
+ clk_disable_unprepare(dd->iclk);
+}
+
static int atmel_sha_handle_queue(struct atmel_sha_dev *dd,
struct ahash_request *req)
{
@@ -682,10 +863,9 @@ static int atmel_sha_handle_queue(struct atmel_sha_dev *dd,
if (ctx->op == SHA_OP_UPDATE) {
err = atmel_sha_update_req(dd);
- if (err != -EINPROGRESS && (ctx->flags & SHA_FLAGS_FINUP)) {
+ if (err != -EINPROGRESS && (ctx->flags & SHA_FLAGS_FINUP))
/* no final() after finup() */
err = atmel_sha_final_req(dd);
- }
} else if (ctx->op == SHA_OP_FINAL) {
err = atmel_sha_final_req(dd);
}
@@ -808,7 +988,7 @@ static int atmel_sha_cra_init_alg(struct crypto_tfm *tfm, const char *alg_base)
}
crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
sizeof(struct atmel_sha_reqctx) +
- SHA_BUFFER_LEN + SHA256_BLOCK_SIZE);
+ SHA_BUFFER_LEN + SHA512_BLOCK_SIZE);
return 0;
}
@@ -826,7 +1006,7 @@ static void atmel_sha_cra_exit(struct crypto_tfm *tfm)
tctx->fallback = NULL;
}
-static struct ahash_alg sha_algs[] = {
+static struct ahash_alg sha_1_256_algs[] = {
{
.init = atmel_sha_init,
.update = atmel_sha_update,
@@ -875,6 +1055,79 @@ static struct ahash_alg sha_algs[] = {
},
};
+static struct ahash_alg sha_224_alg = {
+ .init = atmel_sha_init,
+ .update = atmel_sha_update,
+ .final = atmel_sha_final,
+ .finup = atmel_sha_finup,
+ .digest = atmel_sha_digest,
+ .halg = {
+ .digestsize = SHA224_DIGEST_SIZE,
+ .base = {
+ .cra_name = "sha224",
+ .cra_driver_name = "atmel-sha224",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA224_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct atmel_sha_ctx),
+ .cra_alignmask = 0,
+ .cra_module = THIS_MODULE,
+ .cra_init = atmel_sha_cra_init,
+ .cra_exit = atmel_sha_cra_exit,
+ }
+ }
+};
+
+static struct ahash_alg sha_384_512_algs[] = {
+{
+ .init = atmel_sha_init,
+ .update = atmel_sha_update,
+ .final = atmel_sha_final,
+ .finup = atmel_sha_finup,
+ .digest = atmel_sha_digest,
+ .halg = {
+ .digestsize = SHA384_DIGEST_SIZE,
+ .base = {
+ .cra_name = "sha384",
+ .cra_driver_name = "atmel-sha384",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA384_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct atmel_sha_ctx),
+ .cra_alignmask = 0x3,
+ .cra_module = THIS_MODULE,
+ .cra_init = atmel_sha_cra_init,
+ .cra_exit = atmel_sha_cra_exit,
+ }
+ }
+},
+{
+ .init = atmel_sha_init,
+ .update = atmel_sha_update,
+ .final = atmel_sha_final,
+ .finup = atmel_sha_finup,
+ .digest = atmel_sha_digest,
+ .halg = {
+ .digestsize = SHA512_DIGEST_SIZE,
+ .base = {
+ .cra_name = "sha512",
+ .cra_driver_name = "atmel-sha512",
+ .cra_priority = 100,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA512_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct atmel_sha_ctx),
+ .cra_alignmask = 0x3,
+ .cra_module = THIS_MODULE,
+ .cra_init = atmel_sha_cra_init,
+ .cra_exit = atmel_sha_cra_exit,
+ }
+ }
+},
+};
+
static void atmel_sha_done_task(unsigned long data)
{
struct atmel_sha_dev *dd = (struct atmel_sha_dev *)data;
@@ -941,32 +1194,142 @@ static void atmel_sha_unregister_algs(struct atmel_sha_dev *dd)
{
int i;
- for (i = 0; i < ARRAY_SIZE(sha_algs); i++)
- crypto_unregister_ahash(&sha_algs[i]);
+ for (i = 0; i < ARRAY_SIZE(sha_1_256_algs); i++)
+ crypto_unregister_ahash(&sha_1_256_algs[i]);
+
+ if (dd->caps.has_sha224)
+ crypto_unregister_ahash(&sha_224_alg);
+
+ if (dd->caps.has_sha_384_512) {
+ for (i = 0; i < ARRAY_SIZE(sha_384_512_algs); i++)
+ crypto_unregister_ahash(&sha_384_512_algs[i]);
+ }
}
static int atmel_sha_register_algs(struct atmel_sha_dev *dd)
{
int err, i, j;
- for (i = 0; i < ARRAY_SIZE(sha_algs); i++) {
- err = crypto_register_ahash(&sha_algs[i]);
+ for (i = 0; i < ARRAY_SIZE(sha_1_256_algs); i++) {
+ err = crypto_register_ahash(&sha_1_256_algs[i]);
if (err)
- goto err_sha_algs;
+ goto err_sha_1_256_algs;
+ }
+
+ if (dd->caps.has_sha224) {
+ err = crypto_register_ahash(&sha_224_alg);
+ if (err)
+ goto err_sha_224_algs;
+ }
+
+ if (dd->caps.has_sha_384_512) {
+ for (i = 0; i < ARRAY_SIZE(sha_384_512_algs); i++) {
+ err = crypto_register_ahash(&sha_384_512_algs[i]);
+ if (err)
+ goto err_sha_384_512_algs;
+ }
}
return 0;
-err_sha_algs:
+err_sha_384_512_algs:
+ for (j = 0; j < i; j++)
+ crypto_unregister_ahash(&sha_384_512_algs[j]);
+ crypto_unregister_ahash(&sha_224_alg);
+err_sha_224_algs:
+ i = ARRAY_SIZE(sha_1_256_algs);
+err_sha_1_256_algs:
for (j = 0; j < i; j++)
- crypto_unregister_ahash(&sha_algs[j]);
+ crypto_unregister_ahash(&sha_1_256_algs[j]);
return err;
}
+static bool atmel_sha_filter(struct dma_chan *chan, void *slave)
+{
+ struct at_dma_slave *sl = slave;
+
+ if (sl && sl->dma_dev == chan->device->dev) {
+ chan->private = sl;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static int atmel_sha_dma_init(struct atmel_sha_dev *dd,
+ struct crypto_platform_data *pdata)
+{
+ int err = -ENOMEM;
+ dma_cap_mask_t mask_in;
+
+ if (pdata && pdata->dma_slave->rxdata.dma_dev) {
+ /* Try to grab DMA channel */
+ dma_cap_zero(mask_in);
+ dma_cap_set(DMA_SLAVE, mask_in);
+
+ dd->dma_lch_in.chan = dma_request_channel(mask_in,
+ atmel_sha_filter, &pdata->dma_slave->rxdata);
+
+ if (!dd->dma_lch_in.chan)
+ return err;
+
+ dd->dma_lch_in.dma_conf.direction = DMA_MEM_TO_DEV;
+ dd->dma_lch_in.dma_conf.dst_addr = dd->phys_base +
+ SHA_REG_DIN(0);
+ dd->dma_lch_in.dma_conf.src_maxburst = 1;
+ dd->dma_lch_in.dma_conf.src_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dd->dma_lch_in.dma_conf.dst_maxburst = 1;
+ dd->dma_lch_in.dma_conf.dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dd->dma_lch_in.dma_conf.device_fc = false;
+
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static void atmel_sha_dma_cleanup(struct atmel_sha_dev *dd)
+{
+ dma_release_channel(dd->dma_lch_in.chan);
+}
+
+static void atmel_sha_get_cap(struct atmel_sha_dev *dd)
+{
+
+ dd->caps.has_dma = 0;
+ dd->caps.has_dualbuff = 0;
+ dd->caps.has_sha224 = 0;
+ dd->caps.has_sha_384_512 = 0;
+
+ /* keep only major version number */
+ switch (dd->hw_version & 0xff0) {
+ case 0x410:
+ dd->caps.has_dma = 1;
+ dd->caps.has_dualbuff = 1;
+ dd->caps.has_sha224 = 1;
+ dd->caps.has_sha_384_512 = 1;
+ break;
+ case 0x400:
+ dd->caps.has_dma = 1;
+ dd->caps.has_dualbuff = 1;
+ dd->caps.has_sha224 = 1;
+ break;
+ case 0x320:
+ break;
+ default:
+ dev_warn(dd->dev,
+ "Unmanaged sha version, set minimum capabilities\n");
+ break;
+ }
+}
+
static int __devinit atmel_sha_probe(struct platform_device *pdev)
{
struct atmel_sha_dev *sha_dd;
+ struct crypto_platform_data *pdata;
struct device *dev = &pdev->dev;
struct resource *sha_res;
unsigned long sha_phys_size;
@@ -1018,7 +1381,7 @@ static int __devinit atmel_sha_probe(struct platform_device *pdev)
}
/* Initializing the clock */
- sha_dd->iclk = clk_get(&pdev->dev, NULL);
+ sha_dd->iclk = clk_get(&pdev->dev, "sha_clk");
if (IS_ERR(sha_dd->iclk)) {
dev_err(dev, "clock intialization failed.\n");
err = PTR_ERR(sha_dd->iclk);
@@ -1032,6 +1395,22 @@ static int __devinit atmel_sha_probe(struct platform_device *pdev)
goto sha_io_err;
}
+ atmel_sha_hw_version_init(sha_dd);
+
+ atmel_sha_get_cap(sha_dd);
+
+ if (sha_dd->caps.has_dma) {
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "platform data not available\n");
+ err = -ENXIO;
+ goto err_pdata;
+ }
+ err = atmel_sha_dma_init(sha_dd, pdata);
+ if (err)
+ goto err_sha_dma;
+ }
+
spin_lock(&atmel_sha.lock);
list_add_tail(&sha_dd->list, &atmel_sha.dev_list);
spin_unlock(&atmel_sha.lock);
@@ -1048,6 +1427,10 @@ err_algs:
spin_lock(&atmel_sha.lock);
list_del(&sha_dd->list);
spin_unlock(&atmel_sha.lock);
+ if (sha_dd->caps.has_dma)
+ atmel_sha_dma_cleanup(sha_dd);
+err_sha_dma:
+err_pdata:
iounmap(sha_dd->io_base);
sha_io_err:
clk_put(sha_dd->iclk);
@@ -1078,6 +1461,9 @@ static int __devexit atmel_sha_remove(struct platform_device *pdev)
tasklet_kill(&sha_dd->done_task);
+ if (sha_dd->caps.has_dma)
+ atmel_sha_dma_cleanup(sha_dd);
+
iounmap(sha_dd->io_base);
clk_put(sha_dd->iclk);
@@ -1102,6 +1488,6 @@ static struct platform_driver atmel_sha_driver = {
module_platform_driver(atmel_sha_driver);
-MODULE_DESCRIPTION("Atmel SHA1/SHA256 hw acceleration support.");
+MODULE_DESCRIPTION("Atmel SHA (1/256/224/384/512) hw acceleration support.");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Nicolas Royer - Eukr?a Electromatique");
--
1.7.6.5
^ permalink raw reply related
* [PATCH v2] mmc: omap_hsmmc: introduce omap_hsmmc_prepare/complete
From: Felipe Balbi @ 2012-11-06 16:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAOyx1Lr1g=5RrOZdUiUcgWU0wjoNnpzH6Hr5JfqK8s0n5uoShw@mail.gmail.com>
prepare() is supposed to prevent new children from
being registered. On the MMC subsystem, children
(new cards) registration starts with the card
detect IRQ.
Move card detect IRQ disabling to prepare() so that
no new cards will be registered while we're trying
to suspend.
Likewise, move card detect IRQ enabling to complete()
so we only try to register new children after our MMC
IP is back up.
Signed-off-by: Felipe Balbi <balbi@ti.com>
---
Fixed ->complete() prototype.
drivers/mmc/host/omap_hsmmc.c | 43 +++++++++++++++++++++++--------------------
1 file changed, 23 insertions(+), 20 deletions(-)
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 54bfd0c..d2d6a56 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -2022,6 +2022,24 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
+static int omap_hsmmc_prepare(struct device *dev)
+{
+ struct omap_hsmmc_host *host = dev_get_drvdata(dev);
+
+ if (host->pdata->suspend)
+ return host->pdata->suspend(dev, host->slot_id);
+
+ return 0;
+}
+
+static void omap_hsmmc_complete(struct device *dev)
+{
+ struct omap_hsmmc_host *host = dev_get_drvdata(dev);
+
+ if (host->pdata->resume)
+ host->pdata->resume(dev, host->slot_id);
+}
+
static int omap_hsmmc_suspend(struct device *dev)
{
int ret = 0;
@@ -2035,23 +2053,10 @@ static int omap_hsmmc_suspend(struct device *dev)
pm_runtime_get_sync(host->dev);
host->suspended = 1;
- if (host->pdata->suspend) {
- ret = host->pdata->suspend(dev, host->slot_id);
- if (ret) {
- dev_dbg(dev, "Unable to handle MMC board"
- " level suspend\n");
- host->suspended = 0;
- return ret;
- }
- }
ret = mmc_suspend_host(host->mmc);
if (ret) {
host->suspended = 0;
- if (host->pdata->resume) {
- if (host->pdata->resume(dev, host->slot_id))
- dev_dbg(dev, "Unmask interrupt failed\n");
- }
goto err;
}
@@ -2088,12 +2093,6 @@ static int omap_hsmmc_resume(struct device *dev)
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))
omap_hsmmc_conf_bus_power(host);
- if (host->pdata->resume) {
- ret = host->pdata->resume(dev, host->slot_id);
- if (ret)
- dev_dbg(dev, "Unmask interrupt failed\n");
- }
-
omap_hsmmc_protect_card(host);
/* Notify the core to resume the host */
@@ -2109,8 +2108,10 @@ static int omap_hsmmc_resume(struct device *dev)
}
#else
+#define omap_hsmmc_prepare NULL
+#define omap_hsmmc_complete NULL
#define omap_hsmmc_suspend NULL
-#define omap_hsmmc_resume NULL
+#define omap_hsmmc_resume NULL
#endif
static int omap_hsmmc_runtime_suspend(struct device *dev)
@@ -2138,6 +2139,8 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
.suspend = omap_hsmmc_suspend,
.resume = omap_hsmmc_resume,
+ .prepare = omap_hsmmc_prepare,
+ .complete = omap_hsmmc_complete,
.runtime_suspend = omap_hsmmc_runtime_suspend,
.runtime_resume = omap_hsmmc_runtime_resume,
};
--
1.8.0
^ permalink raw reply related
* [PATCH v3 2/4] arm: bcm2835: convert to the irqchip infrastructure
From: Josh Cartwright @ 2012-11-06 16:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351462748-5224-3-git-send-email-thomas.petazzoni@free-electrons.com>
On Sun, Oct 28, 2012 at 11:19:06PM +0100, Thomas Petazzoni wrote:
> Register the irq controller driver in the main
> drivers/irqchip/irqchip.c file, and make sure that the initialization
> function of the driver sets handle_arch_irq() appropriately. This
> requires a bit of movement in the driver since the
> bcm2835_handle_irq() must move before the armctrl_of_init() function
> to avoid a forward declaration.
>
> On the arch/arm side, use irqchip_init() as the ->init_irq() callback,
> and remove the definition of ->handle_irq() since this is now done by
> the irq controller driver.
>
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Acked-by: Stephen Warren <swarren@wwwdotorg.org>
> Reviewed-by: Rob Herring <rob.herring@calxeda.com>
> ---
[..]
> diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c
> index dc670cc..62d1dad 100644
> --- a/drivers/irqchip/irq-bcm2835.c
> +++ b/drivers/irqchip/irq-bcm2835.c
[..]
> @@ -199,8 +197,8 @@ static void armctrl_handle_shortcut(int bank, struct pt_regs *regs,
> handle_IRQ(irq_linear_revmap(intc.domain, irq), regs);
> }
>
> -asmlinkage void __exception_irq_entry bcm2835_handle_irq(
> - struct pt_regs *regs)
> +static asmlinkage void __exception_irq_entry
> +bcm2835_handle_irq(struct pt_regs *regs)
> {
> u32 stat, irq;
>
> diff --git a/drivers/irqchip/irqchip.c b/drivers/irqchip/irqchip.c
> index 410f99f..e2496e4 100644
> --- a/drivers/irqchip/irqchip.c
> +++ b/drivers/irqchip/irqchip.c
> @@ -14,6 +14,12 @@
> #include "irqchip.h"
>
> static const struct of_device_id irqchip_of_match[] __initconst = {
> +#ifdef CONFIG_ARCH_BCM2835
> + {
> + .compatible = "brcm,bcm2835-armctrl-ic",
> + .data = bcm2835_irqchip_init,
> + },
> +#endif
> {},
> };
>
> diff --git a/drivers/irqchip/irqchip.h b/drivers/irqchip/irqchip.h
> index 1e7a5c2..1075537 100644
> --- a/drivers/irqchip/irqchip.h
> +++ b/drivers/irqchip/irqchip.h
> @@ -11,4 +11,6 @@
> #ifndef _IRQCHIP_H
> #define _IRQCHIP_H
>
> +int bcm2835_irqchip_init(struct device_node *node, struct device_node *parent);
> +
> #endif
Could it make sense here to kill the irqchip.h private export, and
instead rely on the linker to stitch together the builtin irqchip's
of_device_id tables?
Something like:
drivers/irqchip/irq-bcm2835.c:
static bcm2835_irqchip_init(struct device_node *node, struct device_node *parent)
{
/*...*/
}
static const struct of_device_id bcm2835_match[] __initconst = {
{ .compatible = "brcm,bcm2835-armctrl-ic", .data = bcm2835_irqchip_init, },
{},
};
DECLARE_IRQCHIP(bcm2835, bcm2835_match);
where include/linux/irqchip.h:
#define DECLARE_IRQCHIP(name,ids) \
static of_device_id * __irqchip_##name##_matches __used \
__attribute__((__section__(".init.irqchip"))) = ids
drivers/irqchip/irqchip.c:
void irqchip_init(void)
{
extern of_device_id *__irqchip_matches_start[], *__irqchip_matches_end[];
struct of_device_id **matchesp;
matchesp = __irqchip_matches_start;
while (matchesp < __irqchip_matches_end)
of_irq_init(*matchesp);
}
And a suitable entry in vmlinux.lds.h.
(Hopefully?) This would eliminate the need for the 'private' irqchip.h
declarations and the centrally maintained of_device_id table, which
sounds like a win for maintainability.
Thoughts?
Thanks,
Josh
^ permalink raw reply
* [PATCH 2/4] rtc: OMAP: Add system pm_power_off to rtc driver
From: Benoit Cousson @ 2012-11-06 16:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <331ABD5ECB02734CA317220B2BBEABC13EA64F14@DBDE01.ent.ti.com>
Hi Anil,
On 11/06/2012 06:07 AM, AnilKumar, Chimata wrote:
> On Mon, Nov 05, 2012 at 22:13:25, Cousson, Benoit wrote:
>> Hi Anil / Colin,
>>
>> On 11/05/2012 10:42 AM, AnilKumar Ch wrote:
>>> From: Colin Foe-Parker <colin.foeparker@logicpd.com>
>>>
>>> Add system power off control to rtc driver which is the in-charge
>>> of controlling the BeagleBone system power. The power_off routine
>>> can be hooked up to "pm_power_off" system call.
>>>
>>> System power off sequence:-
>>> * Set PMIC STATUS_OFF when PMIC_POWER_EN is pulled low
>>> * Enable PMIC_POWER_EN in rtc module
>>> * Set rtc ALARM2 time
>>> * Enable ALARM2 interrupt
>>>
>>> Added while (1); after the above steps to make sure that no other
>>> process acquire cpu. Otherwise we might see an unexpected behaviour
>>> because we are shutting down all the power rails of SoC except RTC.
>>>
>>> Signed-off-by: Colin Foe-Parker <colin.foeparker@logicpd.com>
>>> [anilkumar at ti.com: move poweroff additions to rtc driver]
>>> Signed-off-by: AnilKumar Ch <anilkumar@ti.com>
>>> ---
>>> Documentation/devicetree/bindings/rtc/rtc-omap.txt | 5 ++
>>> drivers/rtc/rtc-omap.c | 79 +++++++++++++++++++-
>>> 2 files changed, 83 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/rtc/rtc-omap.txt b/Documentation/devicetree/bindings/rtc/rtc-omap.txt
>>> index b47aa41..8d9f4f9 100644
>>> --- a/Documentation/devicetree/bindings/rtc/rtc-omap.txt
>>> +++ b/Documentation/devicetree/bindings/rtc/rtc-omap.txt
>>> @@ -6,6 +6,10 @@ Required properties:
>>> - interrupts: rtc timer, alarm interrupts in order
>>> - interrupt-parent: phandle for the interrupt controller
>>>
>>> +Optional properties:
>>> +- ti,system-power-controller: Telling whether or not rtc is controlling
>>> + the system power.
>>
>> I don't know how it is connected at board level, but I'm not sure the
>> binding is the proper one.
>
> Hi Benoit,
> ________________________________
> | ______ _______ |
> | | | | | |
> | |RTC | | | |
> | |PMIC | Line | | |
> | |PWR_EN|=======>|PWR_EN | |
> | |______| |_______| |
> | AM335x SoC TPS65217 |
> | |
> |________________________________|
> BeagleBone
>
> This is how RTC PMIC_PWR_EN is connected to PWR_EN of TPS65217 PMIC. Only when
> RTC pull low in PMIC_PWR_EN then PMIC will go to power off state provided TPS65217
> status should be changed to STATUS_OFF.
>
> ALARM2 event should be trigger to configure PMIC_PWR_EN properly then the "Line"
> driven low so that PMIC will go to shutdown mode.
Thanks for the nice diagram :-)
I'm wondering if we cannot abuse the gpio binding to describe that
connection instead of creating two custom attributes (PMIC + RTC).
Ideally we should do that without having to change the RTC to use the
gpiolib at all.
rtc: rtc at 44e3e000 {
compatible = "ti,da830-rtc";
reg = <0x44e3e000 0x1000>;
interrupts = <75, 76>;
ti,hwmods = "rtc";
/* expose the PWR_EN functionality of this RTC*/
gpio-controller;
#gpio-cells = <0>; /* assuming we can use 0 ??? */
};
...
tps: tps at 24 {
compatible = "ti,tps65217";
/*
* Enable the power enable feature from
* the input line if that attribute is there.
*/
gpio-power-en = <&rtc>; /* PWR_EN */
...
}
Any thought?
Regards,
Benoit
^ permalink raw reply
* [PATCH 1/7] I2c-nomadik: Fix the usage of wait_for_completion_timeout
From: Chuansheng Liu @ 2012-11-06 17:18 UTC (permalink / raw)
To: linux-arm-kernel
The return value of wait_for_completion_timeout() is always
>= 0 with unsigned int type.
So the condition "ret < 0" or "ret >= 0" is pointless.
Signed-off-by: liu chuansheng <chuansheng.liu@intel.com>
---
drivers/i2c/busses/i2c-nomadik.c | 14 --------------
1 files changed, 0 insertions(+), 14 deletions(-)
diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index 02c3115..8b2ffcf 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -435,13 +435,6 @@ static int read_i2c(struct nmk_i2c_dev *dev, u16 flags)
timeout = wait_for_completion_timeout(
&dev->xfer_complete, dev->adap.timeout);
- if (timeout < 0) {
- dev_err(&dev->adev->dev,
- "wait_for_completion_timeout "
- "returned %d waiting for event\n", timeout);
- status = timeout;
- }
-
if (timeout == 0) {
/* Controller timed out */
dev_err(&dev->adev->dev, "read from slave 0x%x timed out\n",
@@ -523,13 +516,6 @@ static int write_i2c(struct nmk_i2c_dev *dev, u16 flags)
timeout = wait_for_completion_timeout(
&dev->xfer_complete, dev->adap.timeout);
- if (timeout < 0) {
- dev_err(&dev->adev->dev,
- "wait_for_completion_timeout "
- "returned %d waiting for event\n", timeout);
- status = timeout;
- }
-
if (timeout == 0) {
/* Controller timed out */
dev_err(&dev->adev->dev, "write to slave 0x%x timed out\n",
--
1.7.0.4
^ permalink raw reply related
* arch/arm/kernel/smp_twd.c:370:3: warning: 'return' with a value, in function returning void
From: kbuild test robot @ 2012-11-06 17:23 UTC (permalink / raw)
To: linux-arm-kernel
tree: git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc.git next/soc
head: f60be0d5c1f2dd0188205f04a4a7b8778234fd9c
commit: 0336517b38c5fd643784eeee8f0d73efd48e3361 ARM: smp_twd: don't warn on no DT node
date: 6 days ago
config: make ARCH=arm imx_v6_v7_defconfig
All warnings:
arch/arm/kernel/smp_twd.c: In function 'twd_local_timer_of_register':
arch/arm/kernel/smp_twd.c:370:3: warning: 'return' with a value, in function returning void [enabled by default]
vim +370 +/return arch/arm/kernel/smp_twd.c
d8e03643 Marc Zyngier 2012-01-10 354
d8e03643 Marc Zyngier 2012-01-10 355 #ifdef CONFIG_OF
d8e03643 Marc Zyngier 2012-01-10 356 const static struct of_device_id twd_of_match[] __initconst = {
d8e03643 Marc Zyngier 2012-01-10 357 { .compatible = "arm,cortex-a9-twd-timer", },
d8e03643 Marc Zyngier 2012-01-10 358 { .compatible = "arm,cortex-a5-twd-timer", },
d8e03643 Marc Zyngier 2012-01-10 359 { .compatible = "arm,arm11mp-twd-timer", },
d8e03643 Marc Zyngier 2012-01-10 360 { },
d8e03643 Marc Zyngier 2012-01-10 361 };
d8e03643 Marc Zyngier 2012-01-10 362
d8e03643 Marc Zyngier 2012-01-10 363 void __init twd_local_timer_of_register(void)
d8e03643 Marc Zyngier 2012-01-10 364 {
d8e03643 Marc Zyngier 2012-01-10 365 struct device_node *np;
d8e03643 Marc Zyngier 2012-01-10 366 int err;
d8e03643 Marc Zyngier 2012-01-10 367
d8e03643 Marc Zyngier 2012-01-10 368 np = of_find_matching_node(NULL, twd_of_match);
0336517b Rob Herring 2012-10-25 369 if (!np)
0336517b Rob Herring 2012-10-25 @370 return -ENODEV;
d8e03643 Marc Zyngier 2012-01-10 371
d8e03643 Marc Zyngier 2012-01-10 372 twd_ppi = irq_of_parse_and_map(np, 0);
d8e03643 Marc Zyngier 2012-01-10 373 if (!twd_ppi) {
d8e03643 Marc Zyngier 2012-01-10 374 err = -EINVAL;
d8e03643 Marc Zyngier 2012-01-10 375 goto out;
d8e03643 Marc Zyngier 2012-01-10 376 }
d8e03643 Marc Zyngier 2012-01-10 377
d8e03643 Marc Zyngier 2012-01-10 378 twd_base = of_iomap(np, 0);
---
0-DAY kernel build testing backend Open Source Technology Center
Fengguang Wu, Yuanhan Liu Intel Corporation
^ permalink raw reply
* [PATCH V4 0/7] ARM: AM33XX: net: Add DT support to CPGMAC and MDIO driver
From: Mugunthan V N @ 2012-11-06 17:32 UTC (permalink / raw)
To: linux-arm-kernel
This patch-series adds support for,
[1/7]: Typo mistake in CPSW driver while invoking runtime_pm api's
[2/7]: Adds parent<->child relation between CPSW & MDIO module inside cpsw
driver, as in case of AM33XX, the resources are shared and common
register bit-field is provided to control module/clock enable/disable,
makes it difficult to handle common resource.
So the solution here is, to create parent<->child relation between them.
[3/7]: Add hwmod entry for MDIO module, required for MDIO driver.
[4/7]: cpsw: simplify the setup of the register pointers
[5/7]: Add DT device nodes for both CPSW and MDIO modules in am33xx.dtsi,
am335x-evm.dts and am335x-bone.dts file
[6/7]: Enable CPSW support to omap2plus_defconfig
[7/7]: cpsw: Kernel warn fix during suspend
This patch series has been created on top of net-next/master and tested
on BeagleBone platform for NFS boot and basic ping test cases.
Changes from V3:
* Removed unnecessary flags in Davinci MDIO Hwmod entry.
Mugunthan V N (4):
ARM: OMAP3+: hwmod: Add AM33XX HWMOD data for davinci_mdio module
arm/dts: am33xx: Add CPSW and MDIO module nodes for AM33XX
ARM: OMAP2+: omap2plus_defconfig: Enable CPSW support
net: cpsw: halt network stack before halting the device during
suspend
Richard Cochran (1):
cpsw: simplify the setup of the register pointers
Vaibhav Hiremath (2):
net: davinci_mdio: Fix typo mistake in calling runtime-pm api
net: cpsw: Add parent<->child relation support between cpsw and mdio
Documentation/devicetree/bindings/net/cpsw.txt | 34 ----
arch/arm/boot/dts/am335x-bone.dts | 8 +
arch/arm/boot/dts/am335x-evm.dts | 8 +
arch/arm/boot/dts/am33xx.dtsi | 42 +++++
arch/arm/configs/omap2plus_defconfig | 3 +
arch/arm/mach-omap2/omap_hwmod_33xx_data.c | 31 ++++
drivers/net/ethernet/ti/cpsw.c | 231 ++++++++++--------------
drivers/net/ethernet/ti/davinci_mdio.c | 2 +-
include/linux/platform_data/cpsw.h | 19 --
9 files changed, 192 insertions(+), 186 deletions(-)
^ permalink raw reply
* [PATCH V4 1/7] net: davinci_mdio: Fix typo mistake in calling runtime-pm api
From: Mugunthan V N @ 2012-11-06 17:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1352223159-27504-1-git-send-email-mugunthanvnm@ti.com>
From: Vaibhav Hiremath <hvaibhav@ti.com>
By mistake (most likely a copy-paste), instead of pm_runtime_get_sync()
api, driver is calling pm_runtime_put_sync() api in resume callback
function. The bug was introduced by commit id (ae2c07aaf74:
davinci_mdio: runtime PM support).
Now, the reason why it didn't impact functionality is, the patch has
been tested on AM335x-EVM and BeagleBone platform while submitting;
and in case of AM335x the MDIO driver doesn't control the module
enable/disable part, which is handled by CPSW driver.
Signed-off-by: Vaibhav Hiremath <hvaibhav@ti.com>
Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
Acked-by: Peter Korsgaard <jacmet@sunsite.dk>
Acked-by: Richard Cochran <richardcochran@gmail.com>
---
drivers/net/ethernet/ti/davinci_mdio.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c
index 51a96db..ae74280 100644
--- a/drivers/net/ethernet/ti/davinci_mdio.c
+++ b/drivers/net/ethernet/ti/davinci_mdio.c
@@ -465,7 +465,7 @@ static int davinci_mdio_resume(struct device *dev)
u32 ctrl;
spin_lock(&data->lock);
- pm_runtime_put_sync(data->dev);
+ pm_runtime_get_sync(data->dev);
/* restart the scan state machine */
ctrl = __raw_readl(&data->regs->control);
--
1.7.0.4
^ permalink raw reply related
* [PATCH V4 2/7] net: cpsw: Add parent<->child relation support between cpsw and mdio
From: Mugunthan V N @ 2012-11-06 17:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1352223159-27504-1-git-send-email-mugunthanvnm@ti.com>
From: Vaibhav Hiremath <hvaibhav@ti.com>
CPGMAC SubSystem consist of various sub-modules, like, mdio, cpdma,
cpsw, etc... These sub-modules are also used in some of Davinci family
of devices. Now based on requirement, use-case and available technology
nodes the integration of these sub-modules varies across devices.
So coming back to Linux net driver, currently separate and independent
platform devices & drivers for CPSW and MDIO is implemented. In case of
Davinci they both has separate control, from resources perspective,
like clock.
In case of AM33XX, the resources are shared and only one register
bit-field is provided to control module/clock enable/disable, makes it
difficult to handle common resource.
So the solution here implemented in this patch is,
Create parent<->child relationship between both the drivers, making
CPSW as a parent and MDIO as its child and enumerate all the child nodes
under CPSW module.
Both the drivers will function exactly the way it was operating before,
including runtime-pm functionality. No change is required in MDIO driver
(for that matter to any child driver).
As this is only supported during DT boot, the parent<->child relationship
is created and populated in DT execution flow. The only required change
is inside DTS file, making MDIO as a child to CPSW node.
Signed-off-by: Vaibhav Hiremath <hvaibhav@ti.com>
Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
Acked-by: Peter Korsgaard <jacmet@sunsite.dk>
Acked-by: Richard Cochran <richardcochran@gmail.com>
---
drivers/net/ethernet/ti/cpsw.c | 16 ++++++++++++++--
1 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 7654a62..7007aba 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -1144,7 +1144,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
}
data->mac_control = prop;
- for_each_child_of_node(node, slave_node) {
+ for_each_node_by_name(slave_node, "slave") {
struct cpsw_slave_data *slave_data = data->slave_data + i;
const char *phy_id = NULL;
const void *mac_addr = NULL;
@@ -1179,6 +1179,14 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
i++;
}
+ /*
+ * Populate all the child nodes here...
+ */
+ ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
+ /* We do not want to force this, as in some cases may not have child */
+ if (ret)
+ pr_warn("Doesn't have any child node\n");
+
return 0;
error_ret:
@@ -1212,6 +1220,11 @@ static int __devinit cpsw_probe(struct platform_device *pdev)
priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
priv->rx_packet_max = max(rx_packet_max, 128);
+ /*
+ * This may be required here for child devices.
+ */
+ pm_runtime_enable(&pdev->dev);
+
if (cpsw_probe_dt(&priv->data, pdev)) {
pr_err("cpsw: platform data missing\n");
ret = -ENODEV;
@@ -1238,7 +1251,6 @@ static int __devinit cpsw_probe(struct platform_device *pdev)
for (i = 0; i < data->slaves; i++)
priv->slaves[i].slave_num = i;
- pm_runtime_enable(&pdev->dev);
priv->clk = clk_get(&pdev->dev, "fck");
if (IS_ERR(priv->clk)) {
dev_err(&pdev->dev, "fck is not found\n");
--
1.7.0.4
^ permalink raw reply related
* [PATCH V4 3/7] ARM: OMAP3+: hwmod: Add AM33XX HWMOD data for davinci_mdio module
From: Mugunthan V N @ 2012-11-06 17:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1352223159-27504-1-git-send-email-mugunthanvnm@ti.com>
This patch adds hwmod entry for davinci MDIO module,
creating parent<->child relationship between CPSW and MDIO module.
This Parent-child relation is required in order to use common resources
like, clock, but still maintaining the logical separation between them.
CPGMAC SubSystem consist of various sub-modules, like, mdio, cpdma,
cpsw, etc... These sub-modules are also used in some of Davinci
family of devices, so separate and independent platform devices &
drivers for CPSW and MDIO is implemented.
In case of AM33XX, the resources are shared and common register
bit-field is provided to control module/clock enable/disable,
makes it difficult to handle common resources from both drivers.
So the solution is, create parent<->child relationship between
CPGMAC & MDIO modules.
Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
Signed-off-by: Vaibhav Hiremath <hvaibhav@ti.com>
Cc: Paul Walmsley <paul@pwsan.com>
Acked-by: Peter Korsgaard <jacmet@sunsite.dk>
Acked-by: Richard Cochran <richardcochran@gmail.com>
---
arch/arm/mach-omap2/omap_hwmod_33xx_data.c | 31 ++++++++++++++++++++++++++++
1 files changed, 31 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c
index 59d5c1c..3125835 100644
--- a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c
@@ -674,6 +674,7 @@ static struct omap_hwmod am33xx_cpgmac0_hwmod = {
.name = "cpgmac0",
.class = &am33xx_cpgmac0_hwmod_class,
.clkdm_name = "cpsw_125mhz_clkdm",
+ .flags = (HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY),
.mpu_irqs = am33xx_cpgmac0_irqs,
.main_clk = "cpsw_125mhz_gclk",
.prcm = {
@@ -685,6 +686,20 @@ static struct omap_hwmod am33xx_cpgmac0_hwmod = {
};
/*
+ * mdio class
+ */
+static struct omap_hwmod_class am33xx_mdio_hwmod_class = {
+ .name = "davinci_mdio",
+};
+
+static struct omap_hwmod am33xx_mdio_hwmod = {
+ .name = "davinci_mdio",
+ .class = &am33xx_mdio_hwmod_class,
+ .clkdm_name = "cpsw_125mhz_clkdm",
+ .main_clk = "cpsw_125mhz_gclk",
+};
+
+/*
* dcan class
*/
static struct omap_hwmod_class am33xx_dcan_hwmod_class = {
@@ -2501,6 +2516,21 @@ static struct omap_hwmod_ocp_if am33xx_l4_hs__cpgmac0 = {
.user = OCP_USER_MPU,
};
+struct omap_hwmod_addr_space am33xx_mdio_addr_space[] = {
+ {
+ .pa_start = 0x4A101000,
+ .pa_end = 0x4A101000 + SZ_256 - 1,
+ },
+ { }
+};
+
+struct omap_hwmod_ocp_if am33xx_cpgmac0__mdio = {
+ .master = &am33xx_cpgmac0_hwmod,
+ .slave = &am33xx_mdio_hwmod,
+ .addr = am33xx_mdio_addr_space,
+ .user = OCP_USER_MPU,
+};
+
static struct omap_hwmod_addr_space am33xx_elm_addr_space[] = {
{
.pa_start = 0x48080000,
@@ -3371,6 +3401,7 @@ static struct omap_hwmod_ocp_if *am33xx_hwmod_ocp_ifs[] __initdata = {
&am33xx_l3_main__tptc2,
&am33xx_l3_s__usbss,
&am33xx_l4_hs__cpgmac0,
+ &am33xx_cpgmac0__mdio,
NULL,
};
--
1.7.0.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox