* Re: [PATCH v3 03/11] rtc: Add MPC5121 Real time clock driver
From: Grant Likely @ 2010-02-10 2:39 UTC (permalink / raw)
To: Anatolij Gustschin, Alessandro Zummo
Cc: wd, rtc-linux, linuxppc-dev, dzu, Piotr Ziecik
In-Reply-To: <1265377377-29327-4-git-send-email-agust@denx.de>
On Fri, Feb 5, 2010 at 6:42 AM, Anatolij Gustschin <agust@denx.de> wrote:
> Add support for MPC5121 real time clock module.
>
> Signed-off-by: John Rigby <jcrigby@gmail.com>
> Signed-off-by: Piotr Ziecik <kosmo@semihalf.com>
> Signed-off-by: Wolfgang Denk <wd@denx.de>
> Signed-off-by: Anatolij Gustschin <agust@denx.de>
> Cc: <rtc-linux@googlegroups.com>
> Cc: Grant Likely <grant.likely@secretlab.ca>
> Cc: John Rigby <jcrigby@gmail.com>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
Alessandro, do you want me to carry this one in my powerpc tree along
with the rest of the 5121 patches, or do you want to carry it? There
aren't any commit ordering issues on this one.
g.
> ---
>
> Changes since v2:
> =A0- change commit message to describe what the patch is
> =A0- use __devinit/__devexit/__devexit_p for the probe/remove hooks
> =A0 and __devinitdata for the match table
> =A0- register device after it is completely set up
>
> Changes since v1 (as requested by Alessandro Zummo):
> =A0- Remove history from the driver file, the same history is in
> =A0 commit message
> =A0- implement alarm/irq interface using ->ops structure, don't
> =A0 use ops->ioctl() any more
> =A0- Clean up probe()
> =A0- replace printk() by dev_*()
> =A0- add arch dependency in Kconfig
> =A0- add requested include linux/init.h
> =A0- move MODULE_XXX to the end
> =A0- use rtc_valid_tm() when returning 'tm'
> =A0- use __init/__exit/__exit_p as this is not a hotpluggable device
>
> =A0drivers/rtc/Kconfig =A0 =A0 =A0 | =A0 10 ++
> =A0drivers/rtc/Makefile =A0 =A0 =A0| =A0 =A01 +
> =A0drivers/rtc/rtc-mpc5121.c | =A0387 +++++++++++++++++++++++++++++++++++=
++++++++++
> =A03 files changed, 398 insertions(+), 0 deletions(-)
> =A0create mode 100644 drivers/rtc/rtc-mpc5121.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 8167e9e..2bb8a8b 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -868,4 +868,14 @@ config RTC_DRV_MC13783
> =A0 =A0 =A0 =A0help
> =A0 =A0 =A0 =A0 =A0This enables support for the Freescale MC13783 PMIC RT=
C
>
> +config RTC_DRV_MPC5121
> + =A0 =A0 =A0 tristate "Freescale MPC5121 built-in RTC"
> + =A0 =A0 =A0 depends on PPC_MPC512x && RTC_CLASS
> + =A0 =A0 =A0 help
> + =A0 =A0 =A0 =A0 If you say yes here you will get support for the
> + =A0 =A0 =A0 =A0 built-in RTC MPC5121.
> +
> + =A0 =A0 =A0 =A0 This driver can also be built as a module. If so, the m=
odule
> + =A0 =A0 =A0 =A0 will be called rtc-mpc5121.
> +
> =A0endif # RTC_CLASS
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index e5160fd..b7148af 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -55,6 +55,7 @@ obj-$(CONFIG_RTC_DRV_MAX6900) +=3D rtc-max6900.o
> =A0obj-$(CONFIG_RTC_DRV_MAX6902) =A0+=3D rtc-max6902.o
> =A0obj-$(CONFIG_RTC_DRV_MC13783) =A0+=3D rtc-mc13783.o
> =A0obj-$(CONFIG_RTC_DRV_MSM6242) =A0+=3D rtc-msm6242.o
> +obj-$(CONFIG_RTC_DRV_MPC5121) =A0+=3D rtc-mpc5121.o
> =A0obj-$(CONFIG_RTC_DRV_MV) =A0 =A0 =A0 +=3D rtc-mv.o
> =A0obj-$(CONFIG_RTC_DRV_NUC900) =A0 +=3D rtc-nuc900.o
> =A0obj-$(CONFIG_RTC_DRV_OMAP) =A0 =A0 +=3D rtc-omap.o
> diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c
> new file mode 100644
> index 0000000..4313ca0
> --- /dev/null
> +++ b/drivers/rtc/rtc-mpc5121.c
> @@ -0,0 +1,387 @@
> +/*
> + * Real-time clock driver for MPC5121
> + *
> + * Copyright 2007, Domen Puncer <domen.puncer@telargo.com>
> + * Copyright 2008, Freescale Semiconductor, Inc. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/rtc.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/io.h>
> +
> +struct mpc5121_rtc_regs {
> + =A0 =A0 =A0 u8 set_time; =A0 =A0 =A0 =A0 =A0 =A0/* RTC + 0x00 */
> + =A0 =A0 =A0 u8 hour_set; =A0 =A0 =A0 =A0 =A0 =A0/* RTC + 0x01 */
> + =A0 =A0 =A0 u8 minute_set; =A0 =A0 =A0 =A0 =A0/* RTC + 0x02 */
> + =A0 =A0 =A0 u8 second_set; =A0 =A0 =A0 =A0 =A0/* RTC + 0x03 */
> +
> + =A0 =A0 =A0 u8 set_date; =A0 =A0 =A0 =A0 =A0 =A0/* RTC + 0x04 */
> + =A0 =A0 =A0 u8 month_set; =A0 =A0 =A0 =A0 =A0 /* RTC + 0x05 */
> + =A0 =A0 =A0 u8 weekday_set; =A0 =A0 =A0 =A0 /* RTC + 0x06 */
> + =A0 =A0 =A0 u8 date_set; =A0 =A0 =A0 =A0 =A0 =A0/* RTC + 0x07 */
> +
> + =A0 =A0 =A0 u8 write_sw; =A0 =A0 =A0 =A0 =A0 =A0/* RTC + 0x08 */
> + =A0 =A0 =A0 u8 sw_set; =A0 =A0 =A0 =A0 =A0 =A0 =A0/* RTC + 0x09 */
> + =A0 =A0 =A0 u16 year_set; =A0 =A0 =A0 =A0 =A0 /* RTC + 0x0a */
> +
> + =A0 =A0 =A0 u8 alm_enable; =A0 =A0 =A0 =A0 =A0/* RTC + 0x0c */
> + =A0 =A0 =A0 u8 alm_hour_set; =A0 =A0 =A0 =A0/* RTC + 0x0d */
> + =A0 =A0 =A0 u8 alm_min_set; =A0 =A0 =A0 =A0 /* RTC + 0x0e */
> + =A0 =A0 =A0 u8 int_enable; =A0 =A0 =A0 =A0 =A0/* RTC + 0x0f */
> +
> + =A0 =A0 =A0 u8 reserved1;
> + =A0 =A0 =A0 u8 hour; =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/* RTC + 0x11 */
> + =A0 =A0 =A0 u8 minute; =A0 =A0 =A0 =A0 =A0 =A0 =A0/* RTC + 0x12 */
> + =A0 =A0 =A0 u8 second; =A0 =A0 =A0 =A0 =A0 =A0 =A0/* RTC + 0x13 */
> +
> + =A0 =A0 =A0 u8 month; =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* RTC + 0x14 */
> + =A0 =A0 =A0 u8 wday_mday; =A0 =A0 =A0 =A0 =A0 /* RTC + 0x15 */
> + =A0 =A0 =A0 u16 year; =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* RTC + 0x16 */
> +
> + =A0 =A0 =A0 u8 int_alm; =A0 =A0 =A0 =A0 =A0 =A0 /* RTC + 0x18 */
> + =A0 =A0 =A0 u8 int_sw; =A0 =A0 =A0 =A0 =A0 =A0 =A0/* RTC + 0x19 */
> + =A0 =A0 =A0 u8 alm_status; =A0 =A0 =A0 =A0 =A0/* RTC + 0x1a */
> + =A0 =A0 =A0 u8 sw_minute; =A0 =A0 =A0 =A0 =A0 /* RTC + 0x1b */
> +
> + =A0 =A0 =A0 u8 bus_error_1; =A0 =A0 =A0 =A0 /* RTC + 0x1c */
> + =A0 =A0 =A0 u8 int_day; =A0 =A0 =A0 =A0 =A0 =A0 /* RTC + 0x1d */
> + =A0 =A0 =A0 u8 int_min; =A0 =A0 =A0 =A0 =A0 =A0 /* RTC + 0x1e */
> + =A0 =A0 =A0 u8 int_sec; =A0 =A0 =A0 =A0 =A0 =A0 /* RTC + 0x1f */
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* target_time:
> + =A0 =A0 =A0 =A0* =A0 =A0 =A0intended to be used for hibernation but hib=
ernation
> + =A0 =A0 =A0 =A0* =A0 =A0 =A0does not work on silicon rev 1.5 so use it =
for non-volatile
> + =A0 =A0 =A0 =A0* =A0 =A0 =A0storage of offset between the actual_time r=
egister and linux
> + =A0 =A0 =A0 =A0* =A0 =A0 =A0time
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 u32 target_time; =A0 =A0 =A0 =A0/* RTC + 0x20 */
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* actual_time:
> + =A0 =A0 =A0 =A0* =A0 =A0 =A0readonly time since VBAT_RTC was last conne=
cted
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 u32 actual_time; =A0 =A0 =A0 =A0/* RTC + 0x24 */
> + =A0 =A0 =A0 u32 keep_alive; =A0 =A0 =A0 =A0 /* RTC + 0x28 */
> +};
> +
> +struct mpc5121_rtc_data {
> + =A0 =A0 =A0 unsigned irq;
> + =A0 =A0 =A0 unsigned irq_periodic;
> + =A0 =A0 =A0 struct mpc5121_rtc_regs __iomem *regs;
> + =A0 =A0 =A0 struct rtc_device *rtc;
> + =A0 =A0 =A0 struct rtc_wkalrm wkalarm;
> +};
> +
> +/*
> + * Update second/minute/hour registers.
> + *
> + * This is just so alarm will work.
> + */
> +static void mpc5121_rtc_update_smh(struct mpc5121_rtc_regs __iomem *regs=
,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0stru=
ct rtc_time *tm)
> +{
> + =A0 =A0 =A0 out_8(®s->second_set, tm->tm_sec);
> + =A0 =A0 =A0 out_8(®s->minute_set, tm->tm_min);
> + =A0 =A0 =A0 out_8(®s->hour_set, tm->tm_hour);
> +
> + =A0 =A0 =A0 /* set time sequence */
> + =A0 =A0 =A0 out_8(®s->set_time, 0x1);
> + =A0 =A0 =A0 out_8(®s->set_time, 0x3);
> + =A0 =A0 =A0 out_8(®s->set_time, 0x1);
> + =A0 =A0 =A0 out_8(®s->set_time, 0x0);
> +}
> +
> +static int mpc5121_rtc_read_time(struct device *dev, struct rtc_time *tm=
)
> +{
> + =A0 =A0 =A0 struct mpc5121_rtc_data *rtc =3D dev_get_drvdata(dev);
> + =A0 =A0 =A0 struct mpc5121_rtc_regs __iomem *regs =3D rtc->regs;
> + =A0 =A0 =A0 unsigned long now;
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* linux time is actual_time plus the offset saved in tar=
get_time
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 now =3D in_be32(®s->actual_time) + in_be32(®s->target=
_time);
> +
> + =A0 =A0 =A0 rtc_time_to_tm(now, tm);
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* update second minute hour registers
> + =A0 =A0 =A0 =A0* so alarms will work
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 mpc5121_rtc_update_smh(regs, tm);
> +
> + =A0 =A0 =A0 return rtc_valid_tm(tm);
> +}
> +
> +static int mpc5121_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> + =A0 =A0 =A0 struct mpc5121_rtc_data *rtc =3D dev_get_drvdata(dev);
> + =A0 =A0 =A0 struct mpc5121_rtc_regs __iomem *regs =3D rtc->regs;
> + =A0 =A0 =A0 int ret;
> + =A0 =A0 =A0 unsigned long now;
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* The actual_time register is read only so we write the =
offset
> + =A0 =A0 =A0 =A0* between it and linux time to the target_time register.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 ret =3D rtc_tm_to_time(tm, &now);
> + =A0 =A0 =A0 if (ret =3D=3D 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_be32(®s->target_time, now - in_be32(=
®s->actual_time));
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* update second minute hour registers
> + =A0 =A0 =A0 =A0* so alarms will work
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 mpc5121_rtc_update_smh(regs, tm);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm =
*alarm)
> +{
> + =A0 =A0 =A0 struct mpc5121_rtc_data *rtc =3D dev_get_drvdata(dev);
> + =A0 =A0 =A0 struct mpc5121_rtc_regs __iomem *regs =3D rtc->regs;
> +
> + =A0 =A0 =A0 *alarm =3D rtc->wkalarm;
> +
> + =A0 =A0 =A0 alarm->pending =3D in_8(®s->alm_status);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int mpc5121_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *=
alarm)
> +{
> + =A0 =A0 =A0 struct mpc5121_rtc_data *rtc =3D dev_get_drvdata(dev);
> + =A0 =A0 =A0 struct mpc5121_rtc_regs __iomem *regs =3D rtc->regs;
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* the alarm has no seconds so deal with it
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 if (alarm->time.tm_sec) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 alarm->time.tm_sec =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 alarm->time.tm_min++;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (alarm->time.tm_min >=3D 60) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 alarm->time.tm_min =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 alarm->time.tm_hour++;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (alarm->time.tm_hour >=
=3D 24)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 alarm->time=
.tm_hour =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 alarm->time.tm_mday =3D -1;
> + =A0 =A0 =A0 alarm->time.tm_mon =3D -1;
> + =A0 =A0 =A0 alarm->time.tm_year =3D -1;
> +
> + =A0 =A0 =A0 out_8(®s->alm_min_set, alarm->time.tm_min);
> + =A0 =A0 =A0 out_8(®s->alm_hour_set, alarm->time.tm_hour);
> +
> + =A0 =A0 =A0 out_8(®s->alm_enable, alarm->enabled);
> +
> + =A0 =A0 =A0 rtc->wkalarm =3D *alarm;
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static irqreturn_t mpc5121_rtc_handler(int irq, void *dev)
> +{
> + =A0 =A0 =A0 struct mpc5121_rtc_data *rtc =3D dev_get_drvdata((struct de=
vice *)dev);
> + =A0 =A0 =A0 struct mpc5121_rtc_regs __iomem *regs =3D rtc->regs;
> +
> + =A0 =A0 =A0 if (in_8(®s->int_alm)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* acknowledge and clear status */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->int_alm, 1);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->alm_status, 1);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_=
AF);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return IRQ_HANDLED;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return IRQ_NONE;
> +}
> +
> +static irqreturn_t mpc5121_rtc_handler_upd(int irq, void *dev)
> +{
> + =A0 =A0 =A0 struct mpc5121_rtc_data *rtc =3D dev_get_drvdata((struct de=
vice *)dev);
> + =A0 =A0 =A0 struct mpc5121_rtc_regs __iomem *regs =3D rtc->regs;
> +
> + =A0 =A0 =A0 if (in_8(®s->int_sec) && (in_8(®s->int_enable) & 0x1)=
) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* acknowledge */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_8(®s->int_sec, 1);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_=
UF);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return IRQ_HANDLED;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return IRQ_NONE;
> +}
> +
> +static int mpc5121_rtc_alarm_irq_enable(struct device *dev,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 unsigned int enabled)
> +{
> + =A0 =A0 =A0 struct mpc5121_rtc_data *rtc =3D dev_get_drvdata(dev);
> + =A0 =A0 =A0 struct mpc5121_rtc_regs __iomem *regs =3D rtc->regs;
> + =A0 =A0 =A0 int val;
> +
> + =A0 =A0 =A0 if (enabled)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 val =3D 1;
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 val =3D 0;
> +
> + =A0 =A0 =A0 out_8(®s->alm_enable, val);
> + =A0 =A0 =A0 rtc->wkalarm.enabled =3D val;
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static int mpc5121_rtc_update_irq_enable(struct device *dev,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0unsigned int enabled)
> +{
> + =A0 =A0 =A0 struct mpc5121_rtc_data *rtc =3D dev_get_drvdata(dev);
> + =A0 =A0 =A0 struct mpc5121_rtc_regs __iomem *regs =3D rtc->regs;
> + =A0 =A0 =A0 int val;
> +
> + =A0 =A0 =A0 val =3D in_8(®s->int_enable);
> +
> + =A0 =A0 =A0 if (enabled)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 val =3D (val & ~0x8) | 0x1;
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 val &=3D ~0x1;
> +
> + =A0 =A0 =A0 out_8(®s->int_enable, val);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static const struct rtc_class_ops mpc5121_rtc_ops =3D {
> + =A0 =A0 =A0 .read_time =3D mpc5121_rtc_read_time,
> + =A0 =A0 =A0 .set_time =3D mpc5121_rtc_set_time,
> + =A0 =A0 =A0 .read_alarm =3D mpc5121_rtc_read_alarm,
> + =A0 =A0 =A0 .set_alarm =3D mpc5121_rtc_set_alarm,
> + =A0 =A0 =A0 .alarm_irq_enable =3D mpc5121_rtc_alarm_irq_enable,
> + =A0 =A0 =A0 .update_irq_enable =3D mpc5121_rtc_update_irq_enable,
> +};
> +
> +static int __devinit mpc5121_rtc_probe(struct of_device *op,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 const struct of_device_id *match)
> +{
> + =A0 =A0 =A0 struct mpc5121_rtc_data *rtc;
> + =A0 =A0 =A0 int err =3D 0;
> + =A0 =A0 =A0 u32 ka;
> +
> + =A0 =A0 =A0 rtc =3D kzalloc(sizeof(*rtc), GFP_KERNEL);
> + =A0 =A0 =A0 if (!rtc)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENOMEM;
> +
> + =A0 =A0 =A0 rtc->regs =3D of_iomap(op->node, 0);
> + =A0 =A0 =A0 if (!rtc->regs) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(&op->dev, "%s: couldn't map io spac=
e\n", __func__);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D -ENOSYS;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out_free;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 device_init_wakeup(&op->dev, 1);
> +
> + =A0 =A0 =A0 dev_set_drvdata(&op->dev, rtc);
> +
> + =A0 =A0 =A0 rtc->irq =3D irq_of_parse_and_map(op->node, 1);
> + =A0 =A0 =A0 err =3D request_irq(rtc->irq, mpc5121_rtc_handler, IRQF_DIS=
ABLED,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 "mpc5121-rtc", &op->dev);
> + =A0 =A0 =A0 if (err) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(&op->dev, "%s: could not request ir=
q: %i\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 __func__, rtc->irq);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out_dispose;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 rtc->irq_periodic =3D irq_of_parse_and_map(op->node, 0);
> + =A0 =A0 =A0 err =3D request_irq(rtc->irq_periodic, mpc5121_rtc_handler_=
upd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 IRQF_DISABL=
ED, "mpc5121-rtc_upd", &op->dev);
> + =A0 =A0 =A0 if (err) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(&op->dev, "%s: could not request ir=
q: %i\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 __func__, rtc->irq_periodic);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out_dispose2;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 ka =3D in_be32(&rtc->regs->keep_alive);
> + =A0 =A0 =A0 if (ka & 0x02) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_warn(&op->dev,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "mpc5121-rtc: Battery or os=
cillator failure!\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 out_be32(&rtc->regs->keep_alive, ka);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 rtc->rtc =3D rtc_device_register("mpc5121-rtc", &op->dev,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 &mpc5121_rtc_ops, THIS_MODULE);
> + =A0 =A0 =A0 if (IS_ERR(rtc->rtc)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 err =3D PTR_ERR(rtc->rtc);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out_free_irq;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return 0;
> +
> +out_free_irq:
> + =A0 =A0 =A0 free_irq(rtc->irq_periodic, &op->dev);
> +out_dispose2:
> + =A0 =A0 =A0 irq_dispose_mapping(rtc->irq_periodic);
> + =A0 =A0 =A0 free_irq(rtc->irq, &op->dev);
> +out_dispose:
> + =A0 =A0 =A0 irq_dispose_mapping(rtc->irq);
> + =A0 =A0 =A0 iounmap(rtc->regs);
> +out_free:
> + =A0 =A0 =A0 kfree(rtc);
> +
> + =A0 =A0 =A0 return err;
> +}
> +
> +static int __devexit mpc5121_rtc_remove(struct of_device *op)
> +{
> + =A0 =A0 =A0 struct mpc5121_rtc_data *rtc =3D dev_get_drvdata(&op->dev);
> + =A0 =A0 =A0 struct mpc5121_rtc_regs __iomem *regs =3D rtc->regs;
> +
> + =A0 =A0 =A0 /* disable interrupt, so there are no nasty surprises */
> + =A0 =A0 =A0 out_8(®s->alm_enable, 0);
> + =A0 =A0 =A0 out_8(®s->int_enable, in_8(®s->int_enable) & ~0x1);
> +
> + =A0 =A0 =A0 rtc_device_unregister(rtc->rtc);
> + =A0 =A0 =A0 iounmap(rtc->regs);
> + =A0 =A0 =A0 free_irq(rtc->irq, &op->dev);
> + =A0 =A0 =A0 free_irq(rtc->irq_periodic, &op->dev);
> + =A0 =A0 =A0 irq_dispose_mapping(rtc->irq);
> + =A0 =A0 =A0 irq_dispose_mapping(rtc->irq_periodic);
> + =A0 =A0 =A0 dev_set_drvdata(&op->dev, NULL);
> + =A0 =A0 =A0 kfree(rtc);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static struct of_device_id mpc5121_rtc_match[] __devinitdata =3D {
> + =A0 =A0 =A0 { .compatible =3D "fsl,mpc5121-rtc", },
> + =A0 =A0 =A0 {},
> +};
> +
> +static struct of_platform_driver mpc5121_rtc_driver =3D {
> + =A0 =A0 =A0 .owner =3D THIS_MODULE,
> + =A0 =A0 =A0 .name =3D "mpc5121-rtc",
> + =A0 =A0 =A0 .match_table =3D mpc5121_rtc_match,
> + =A0 =A0 =A0 .probe =3D mpc5121_rtc_probe,
> + =A0 =A0 =A0 .remove =3D __devexit_p(mpc5121_rtc_remove),
> +};
> +
> +static int __init mpc5121_rtc_init(void)
> +{
> + =A0 =A0 =A0 return of_register_platform_driver(&mpc5121_rtc_driver);
> +}
> +module_init(mpc5121_rtc_init);
> +
> +static void __exit mpc5121_rtc_exit(void)
> +{
> + =A0 =A0 =A0 of_unregister_platform_driver(&mpc5121_rtc_driver);
> +}
> +module_exit(mpc5121_rtc_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("John Rigby <jcrigby@gmail.com>");
> --
> 1.6.3.3
>
>
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: [PATCH v3 04/11] mtd: Add MPC5121 NAND Flash Controller driver
From: Grant Likely @ 2010-02-10 2:42 UTC (permalink / raw)
To: Anatolij Gustschin, David Woodhouse
Cc: wd, dzu, linuxppc-dev, linux-mtd, Piotr Ziecik
In-Reply-To: <1265377377-29327-5-git-send-email-agust@denx.de>
On Fri, Feb 5, 2010 at 6:42 AM, Anatolij Gustschin <agust@denx.de> wrote:
> Adds NAND Flash Controller driver for MPC5121 Revision 2.
> All device features, except hardware ECC and power management,
> are supported.
>
> Signed-off-by: Piotr Ziecik <kosmo@semihalf.com>
> Signed-off-by: Wolfgang Denk <wd@denx.de>
> Signed-off-by: Anatolij Gustschin <agust@denx.de>
> Cc: <linux-mtd@lists.infradead.org>
> Cc: Grant Likely <grant.likely@secretlab.ca>
> Cc: John Rigby <jcrigby@gmail.com>
On *very* brief review...
Acked-by: Grant Likely <grant.likely@secretlab.ca>
David, there are ordering issues on this patch with arch-specific
code. If the patch looks okay to you, then can I pick it up into my
tree?
Thanks,
g.
> ---
> Changes since v2:
> =A0- move the arch bits into separate patch
> =A0 (it is the next patch in this series now)
> =A0- use __devinit/__devexit/__devexit_p and __devinitdata
>
> Changes since v1:
> =A0- add logfile with changes since previous version
>
> Changes since the patch version submitted in May 2009:
>
> =A0- move mpc5121_nfc.h to the driver .c as there is only one user
> =A0- remove DRV_VERSION macro
> =A0- replace printk() by dev_*()
> =A0- drop unnecessary .suspend and .resume initializations
> =A0- remove duplicate .name/.owner settings
> =A0- fix mpc5121_nfc_init() to "return of_register_platform_driver(&mpc51=
21_nfc_driver);"
> =A0- move module_init() to just below the init function
> =A0- remove MODULE_VERSION
> =A0- use "mtd: Add MPC5121 NAND Flash Controller driver" as the subject,
> =A0 previously it was "mpc5121: Added NAND Flash Controller driver."
>
> =A0drivers/mtd/nand/Kconfig =A0 =A0 =A0 | =A0 =A07 +
> =A0drivers/mtd/nand/Makefile =A0 =A0 =A0| =A0 =A01 +
> =A0drivers/mtd/nand/mpc5121_nfc.c | =A0916 ++++++++++++++++++++++++++++++=
++++++++++
> =A03 files changed, 924 insertions(+), 0 deletions(-)
> =A0create mode 100644 drivers/mtd/nand/mpc5121_nfc.c
>
> diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
> index 677cd53..099f002 100644
> --- a/drivers/mtd/nand/Kconfig
> +++ b/drivers/mtd/nand/Kconfig
> @@ -442,6 +442,13 @@ config MTD_NAND_FSL_UPM
> =A0 =A0 =A0 =A0 =A0Enables support for NAND Flash chips wired onto Freesc=
ale PowerPC
> =A0 =A0 =A0 =A0 =A0processor localbus with User-Programmable Machine supp=
ort.
>
> +config MTD_NAND_MPC5121_NFC
> + =A0 =A0 =A0 tristate "MPC5121 built-in NAND Flash Controller support"
> + =A0 =A0 =A0 depends on PPC_MPC512x
> + =A0 =A0 =A0 help
> + =A0 =A0 =A0 =A0 This enables the driver for the NAND flash controller o=
n the
> + =A0 =A0 =A0 =A0 MPC5121 SoC.
> +
> =A0config MTD_NAND_MXC
> =A0 =A0 =A0 =A0tristate "MXC NAND support"
> =A0 =A0 =A0 =A0depends on ARCH_MX2 || ARCH_MX3
> diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
> index 1407bd1..d4ddf05 100644
> --- a/drivers/mtd/nand/Makefile
> +++ b/drivers/mtd/nand/Makefile
> @@ -42,5 +42,6 @@ obj-$(CONFIG_MTD_NAND_TXX9NDFMC) =A0 =A0 =A0+=3D txx9nd=
fmc.o
> =A0obj-$(CONFIG_MTD_NAND_W90P910) =A0 =A0 =A0 =A0 +=3D w90p910_nand.o
> =A0obj-$(CONFIG_MTD_NAND_NOMADIK) =A0 =A0 =A0 =A0 +=3D nomadik_nand.o
> =A0obj-$(CONFIG_MTD_NAND_BCM_UMI) =A0 =A0 =A0 =A0 +=3D bcm_umi_nand.o nan=
d_bcm_umi.o
> +obj-$(CONFIG_MTD_NAND_MPC5121_NFC) =A0 =A0 +=3D mpc5121_nfc.o
>
> =A0nand-objs :=3D nand_base.o nand_bbt.o
> diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nf=
c.c
> new file mode 100644
> index 0000000..191bf99
> --- /dev/null
> +++ b/drivers/mtd/nand/mpc5121_nfc.c
> @@ -0,0 +1,916 @@
> +/*
> + * Copyright 2004-2008 Freescale Semiconductor, Inc.
> + * Copyright 2009 Semihalf.
> + *
> + * Approved as OSADL project by a majority of OSADL members and funded
> + * by OSADL membership fees in 2009; =A0for details see www.osadl.org.
> + *
> + * Based on original driver from Freescale Semiconductor
> + * written by John Rigby <jrigby@freescale.com> on basis
> + * of drivers/mtd/nand/mxc_nand.c. Reworked and extended
> + * Piotr Ziecik <kosmo@semihalf.com>.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
> + * MA 02110-1301, USA.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/nand.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +
> +#include <asm/mpc5xxx.h>
> +
> +/* Addresses for NFC MAIN RAM BUFFER areas */
> +#define NFC_MAIN_AREA(n) =A0 =A0 =A0 ((n) * =A00x200)
> +
> +/* Addresses for NFC SPARE BUFFER areas */
> +#define NFC_SPARE_BUFFERS =A0 =A0 =A08
> +#define NFC_SPARE_LEN =A0 =A0 =A0 =A0 =A00x40
> +#define NFC_SPARE_AREA(n) =A0 =A0 =A0(0x1000 + ((n) * NFC_SPARE_LEN))
> +
> +/* MPC5121 NFC registers */
> +#define NFC_BUF_ADDR =A0 =A0 =A0 =A0 =A0 0x1E04
> +#define NFC_FLASH_ADDR =A0 =A0 =A0 =A0 0x1E06
> +#define NFC_FLASH_CMD =A0 =A0 =A0 =A0 =A00x1E08
> +#define NFC_CONFIG =A0 =A0 =A0 =A0 =A0 =A0 0x1E0A
> +#define NFC_ECC_STATUS1 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x1E0C
> +#define NFC_ECC_STATUS2 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x1E0E
> +#define NFC_SPAS =A0 =A0 =A0 =A0 =A0 =A0 =A0 0x1E10
> +#define NFC_WRPROT =A0 =A0 =A0 =A0 =A0 =A0 0x1E12
> +#define NFC_NF_WRPRST =A0 =A0 =A0 =A0 =A00x1E18
> +#define NFC_CONFIG1 =A0 =A0 =A0 =A0 =A0 =A00x1E1A
> +#define NFC_CONFIG2 =A0 =A0 =A0 =A0 =A0 =A00x1E1C
> +#define NFC_UNLOCKSTART_BLK0 =A0 0x1E20
> +#define NFC_UNLOCKEND_BLK0 =A0 =A0 0x1E22
> +#define NFC_UNLOCKSTART_BLK1 =A0 0x1E24
> +#define NFC_UNLOCKEND_BLK1 =A0 =A0 0x1E26
> +#define NFC_UNLOCKSTART_BLK2 =A0 0x1E28
> +#define NFC_UNLOCKEND_BLK2 =A0 =A0 0x1E2A
> +#define NFC_UNLOCKSTART_BLK3 =A0 0x1E2C
> +#define NFC_UNLOCKEND_BLK3 =A0 =A0 0x1E2E
> +
> +/* Bit Definitions: NFC_BUF_ADDR */
> +#define NFC_RBA_MASK =A0 =A0 =A0 =A0 =A0 (7 << 0)
> +#define NFC_ACTIVE_CS_SHIFT =A0 =A05
> +#define NFC_ACTIVE_CS_MASK =A0 =A0 (3 << NFC_ACTIVE_CS_SHIFT)
> +
> +/* Bit Definitions: NFC_CONFIG */
> +#define NFC_BLS_UNLOCKED =A0 =A0 =A0 (1 << 1)
> +
> +/* Bit Definitions: NFC_CONFIG1 */
> +#define NFC_ECC_4BIT =A0 =A0 =A0 =A0 =A0 (1 << 0)
> +#define NFC_FULL_PAGE_DMA =A0 =A0 =A0(1 << 1)
> +#define NFC_SPARE_ONLY =A0 =A0 =A0 =A0 (1 << 2)
> +#define NFC_ECC_ENABLE =A0 =A0 =A0 =A0 (1 << 3)
> +#define NFC_INT_MASK =A0 =A0 =A0 =A0 =A0 (1 << 4)
> +#define NFC_BIG_ENDIAN =A0 =A0 =A0 =A0 (1 << 5)
> +#define NFC_RESET =A0 =A0 =A0 =A0 =A0 =A0 =A0(1 << 6)
> +#define NFC_CE =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1 << 7)
> +#define NFC_ONE_CYCLE =A0 =A0 =A0 =A0 =A0(1 << 8)
> +#define NFC_PPB_32 =A0 =A0 =A0 =A0 =A0 =A0 (0 << 9)
> +#define NFC_PPB_64 =A0 =A0 =A0 =A0 =A0 =A0 (1 << 9)
> +#define NFC_PPB_128 =A0 =A0 =A0 =A0 =A0 =A0(2 << 9)
> +#define NFC_PPB_256 =A0 =A0 =A0 =A0 =A0 =A0(3 << 9)
> +#define NFC_PPB_MASK =A0 =A0 =A0 =A0 =A0 (3 << 9)
> +#define NFC_FULL_PAGE_INT =A0 =A0 =A0(1 << 11)
> +
> +/* Bit Definitions: NFC_CONFIG2 */
> +#define NFC_COMMAND =A0 =A0 =A0 =A0 =A0 =A0(1 << 0)
> +#define NFC_ADDRESS =A0 =A0 =A0 =A0 =A0 =A0(1 << 1)
> +#define NFC_INPUT =A0 =A0 =A0 =A0 =A0 =A0 =A0(1 << 2)
> +#define NFC_OUTPUT =A0 =A0 =A0 =A0 =A0 =A0 (1 << 3)
> +#define NFC_ID =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (1 << 4)
> +#define NFC_STATUS =A0 =A0 =A0 =A0 =A0 =A0 (1 << 5)
> +#define NFC_CMD_FAIL =A0 =A0 =A0 =A0 =A0 (1 << 15)
> +#define NFC_INT =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(1 << 15)
> +
> +/* Bit Definitions: NFC_WRPROT */
> +#define NFC_WPC_LOCK_TIGHT =A0 =A0 (1 << 0)
> +#define NFC_WPC_LOCK =A0 =A0 =A0 =A0 =A0 (1 << 1)
> +#define NFC_WPC_UNLOCK =A0 =A0 =A0 =A0 (1 << 2)
> +
> +#define =A0 =A0 =A0 =A0DRV_NAME =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"mpc5121_=
nfc"
> +
> +/* Timeouts */
> +#define NFC_RESET_TIMEOUT =A0 =A0 =A01000 =A0 =A0 =A0 =A0 =A0 =A0/* 1 ms=
*/
> +#define NFC_TIMEOUT =A0 =A0 =A0 =A0 =A0 =A0(HZ / 10) =A0 =A0 =A0 /* 1/10=
s */
> +
> +struct mpc5121_nfc_prv {
> + =A0 =A0 =A0 struct mtd_info =A0 =A0 =A0 =A0 mtd;
> + =A0 =A0 =A0 struct nand_chip =A0 =A0 =A0 =A0chip;
> + =A0 =A0 =A0 int =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 irq;
> + =A0 =A0 =A0 void __iomem =A0 =A0 =A0 =A0 =A0 =A0*regs;
> + =A0 =A0 =A0 struct clk =A0 =A0 =A0 =A0 =A0 =A0 =A0*clk;
> + =A0 =A0 =A0 wait_queue_head_t =A0 =A0 =A0 irq_waitq;
> + =A0 =A0 =A0 uint =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0column;
> + =A0 =A0 =A0 int =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 spareonly;
> + =A0 =A0 =A0 void __iomem =A0 =A0 =A0 =A0 =A0 =A0*csreg;
> + =A0 =A0 =A0 struct device =A0 =A0 =A0 =A0 =A0 *dev;
> +};
> +
> +static void mpc5121_nfc_done(struct mtd_info *mtd);
> +
> +#ifdef CONFIG_MTD_PARTITIONS
> +static const char *mpc5121_nfc_pprobes[] =3D { "cmdlinepart", NULL };
> +#endif
> +
> +/* Read NFC register */
> +static inline u16 nfc_read(struct mtd_info *mtd, uint reg)
> +{
> + =A0 =A0 =A0 struct nand_chip *chip =3D mtd->priv;
> + =A0 =A0 =A0 struct mpc5121_nfc_prv *prv =3D chip->priv;
> +
> + =A0 =A0 =A0 return in_be16(prv->regs + reg);
> +}
> +
> +/* Write NFC register */
> +static inline void nfc_write(struct mtd_info *mtd, uint reg, u16 val)
> +{
> + =A0 =A0 =A0 struct nand_chip *chip =3D mtd->priv;
> + =A0 =A0 =A0 struct mpc5121_nfc_prv *prv =3D chip->priv;
> +
> + =A0 =A0 =A0 out_be16(prv->regs + reg, val);
> +}
> +
> +/* Set bits in NFC register */
> +static inline void nfc_set(struct mtd_info *mtd, uint reg, u16 bits)
> +{
> + =A0 =A0 =A0 nfc_write(mtd, reg, nfc_read(mtd, reg) | bits);
> +}
> +
> +/* Clear bits in NFC register */
> +static inline void nfc_clear(struct mtd_info *mtd, uint reg, u16 bits)
> +{
> + =A0 =A0 =A0 nfc_write(mtd, reg, nfc_read(mtd, reg) & ~bits);
> +}
> +
> +/* Invoke address cycle */
> +static inline void mpc5121_nfc_send_addr(struct mtd_info *mtd, u16 addr)
> +{
> + =A0 =A0 =A0 nfc_write(mtd, NFC_FLASH_ADDR, addr);
> + =A0 =A0 =A0 nfc_write(mtd, NFC_CONFIG2, NFC_ADDRESS);
> + =A0 =A0 =A0 mpc5121_nfc_done(mtd);
> +}
> +
> +/* Invoke command cycle */
> +static inline void mpc5121_nfc_send_cmd(struct mtd_info *mtd, u16 cmd)
> +{
> + =A0 =A0 =A0 nfc_write(mtd, NFC_FLASH_CMD, cmd);
> + =A0 =A0 =A0 nfc_write(mtd, NFC_CONFIG2, NFC_COMMAND);
> + =A0 =A0 =A0 mpc5121_nfc_done(mtd);
> +}
> +
> +/* Send data from NFC buffers to NAND flash */
> +static inline void mpc5121_nfc_send_prog_page(struct mtd_info *mtd)
> +{
> + =A0 =A0 =A0 nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
> + =A0 =A0 =A0 nfc_write(mtd, NFC_CONFIG2, NFC_INPUT);
> + =A0 =A0 =A0 mpc5121_nfc_done(mtd);
> +}
> +
> +/* Receive data from NAND flash */
> +static inline void mpc5121_nfc_send_read_page(struct mtd_info *mtd)
> +{
> + =A0 =A0 =A0 nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
> + =A0 =A0 =A0 nfc_write(mtd, NFC_CONFIG2, NFC_OUTPUT);
> + =A0 =A0 =A0 mpc5121_nfc_done(mtd);
> +}
> +
> +/* Receive ID from NAND flash */
> +static inline void mpc5121_nfc_send_read_id(struct mtd_info *mtd)
> +{
> + =A0 =A0 =A0 nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
> + =A0 =A0 =A0 nfc_write(mtd, NFC_CONFIG2, NFC_ID);
> + =A0 =A0 =A0 mpc5121_nfc_done(mtd);
> +}
> +
> +/* Receive status from NAND flash */
> +static inline void mpc5121_nfc_send_read_status(struct mtd_info *mtd)
> +{
> + =A0 =A0 =A0 nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
> + =A0 =A0 =A0 nfc_write(mtd, NFC_CONFIG2, NFC_STATUS);
> + =A0 =A0 =A0 mpc5121_nfc_done(mtd);
> +}
> +
> +/* NFC interrupt handler */
> +static irqreturn_t mpc5121_nfc_irq(int irq, void *data)
> +{
> + =A0 =A0 =A0 struct mtd_info *mtd =3D data;
> + =A0 =A0 =A0 struct nand_chip *chip =3D mtd->priv;
> + =A0 =A0 =A0 struct mpc5121_nfc_prv *prv =3D chip->priv;
> +
> + =A0 =A0 =A0 nfc_set(mtd, NFC_CONFIG1, NFC_INT_MASK);
> + =A0 =A0 =A0 wake_up(&prv->irq_waitq);
> +
> + =A0 =A0 =A0 return IRQ_HANDLED;
> +}
> +
> +/* Wait for operation complete */
> +static void mpc5121_nfc_done(struct mtd_info *mtd)
> +{
> + =A0 =A0 =A0 struct nand_chip *chip =3D mtd->priv;
> + =A0 =A0 =A0 struct mpc5121_nfc_prv *prv =3D chip->priv;
> + =A0 =A0 =A0 int rv;
> +
> + =A0 =A0 =A0 if ((nfc_read(mtd, NFC_CONFIG2) & NFC_INT) =3D=3D 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 nfc_clear(mtd, NFC_CONFIG1, NFC_INT_MASK);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rv =3D wait_event_timeout(prv->irq_waitq,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (nfc_read(mtd, NFC_CONFIG2)=
& NFC_INT), NFC_TIMEOUT);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!rv)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_warn(prv->dev,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "Timeout wh=
ile waiting for interrupt.\n");
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 nfc_clear(mtd, NFC_CONFIG2, NFC_INT);
> +}
> +
> +/* Do address cycle(s) */
> +static void mpc5121_nfc_addr_cycle(struct mtd_info *mtd, int column, int=
page)
> +{
> + =A0 =A0 =A0 struct nand_chip *chip =3D mtd->priv;
> + =A0 =A0 =A0 u32 pagemask =3D chip->pagemask;
> +
> + =A0 =A0 =A0 if (column !=3D -1) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc5121_nfc_send_addr(mtd, column);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (mtd->writesize > 512)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc5121_nfc_send_addr(mtd, =
column >> 8);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 if (page !=3D -1) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 do {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc5121_nfc_send_addr(mtd, =
page & 0xFF);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 page >>=3D 8;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pagemask >>=3D 8;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } while (pagemask);
> + =A0 =A0 =A0 }
> +}
> +
> +/* Control chip select signals */
> +static void mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip)
> +{
> + =A0 =A0 =A0 if (chip < 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 nfc_clear(mtd, NFC_CONFIG1, NFC_CE);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 nfc_clear(mtd, NFC_BUF_ADDR, NFC_ACTIVE_CS_MASK);
> + =A0 =A0 =A0 nfc_set(mtd, NFC_BUF_ADDR, (chip << NFC_ACTIVE_CS_SHIFT) &
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 NFC_ACTIVE_CS_MASK);
> + =A0 =A0 =A0 nfc_set(mtd, NFC_CONFIG1, NFC_CE);
> +}
> +
> +/* Init external chip select logic on ADS5121 board */
> +static int ads5121_chipselect_init(struct mtd_info *mtd)
> +{
> + =A0 =A0 =A0 struct nand_chip *chip =3D mtd->priv;
> + =A0 =A0 =A0 struct mpc5121_nfc_prv *prv =3D chip->priv;
> + =A0 =A0 =A0 struct device_node *dn;
> +
> + =A0 =A0 =A0 dn =3D of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-=
cpld");
> + =A0 =A0 =A0 if (dn) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 prv->csreg =3D of_iomap(dn, 0);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 of_node_put(dn);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!prv->csreg)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENOMEM;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* CPLD Register 9 controls NAND /CE Lines =
*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 prv->csreg +=3D 9;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return -EINVAL;
> +}
> +
> +/* Control chips select signal on ADS5121 board */
> +static void ads5121_select_chip(struct mtd_info *mtd, int chip)
> +{
> + =A0 =A0 =A0 struct nand_chip *nand =3D mtd->priv;
> + =A0 =A0 =A0 struct mpc5121_nfc_prv *prv =3D nand->priv;
> + =A0 =A0 =A0 u8 v;
> +
> + =A0 =A0 =A0 v =3D in_8(prv->csreg);
> + =A0 =A0 =A0 v |=3D 0x0F;
> +
> + =A0 =A0 =A0 if (chip >=3D 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc5121_nfc_select_chip(mtd, 0);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 v &=3D ~(1 << chip);
> + =A0 =A0 =A0 } else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc5121_nfc_select_chip(mtd, -1);
> +
> + =A0 =A0 =A0 out_8(prv->csreg, v);
> +}
> +
> +/* Read NAND Ready/Busy signal */
> +static int mpc5121_nfc_dev_ready(struct mtd_info *mtd)
> +{
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* NFC handles ready/busy signal internally. Therefore, t=
his function
> + =A0 =A0 =A0 =A0* always returns status as ready.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 return 1;
> +}
> +
> +/* Write command to NAND flash */
> +static void mpc5121_nfc_command(struct mtd_info *mtd, unsigned command,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int column, int page)
> +{
> + =A0 =A0 =A0 struct nand_chip *chip =3D mtd->priv;
> + =A0 =A0 =A0 struct mpc5121_nfc_prv *prv =3D chip->priv;
> +
> + =A0 =A0 =A0 prv->column =3D (column >=3D 0) ? column : 0;
> + =A0 =A0 =A0 prv->spareonly =3D 0;
> +
> + =A0 =A0 =A0 switch (command) {
> + =A0 =A0 =A0 case NAND_CMD_PAGEPROG:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc5121_nfc_send_prog_page(mtd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* NFC does not support sub-page reads and writes,
> + =A0 =A0 =A0 =A0* so emulate them using full page transfers.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 case NAND_CMD_READ0:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 column =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case NAND_CMD_READ1:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 prv->column +=3D 256;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 command =3D NAND_CMD_READ0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 column =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case NAND_CMD_READOOB:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 prv->spareonly =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 command =3D NAND_CMD_READ0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 column =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case NAND_CMD_SEQIN:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc5121_nfc_command(mtd, NAND_CMD_READ0, co=
lumn, page);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 column =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case NAND_CMD_ERASE1:
> + =A0 =A0 =A0 case NAND_CMD_ERASE2:
> + =A0 =A0 =A0 case NAND_CMD_READID:
> + =A0 =A0 =A0 case NAND_CMD_STATUS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 mpc5121_nfc_send_cmd(mtd, command);
> + =A0 =A0 =A0 mpc5121_nfc_addr_cycle(mtd, column, page);
> +
> + =A0 =A0 =A0 switch (command) {
> + =A0 =A0 =A0 case NAND_CMD_READ0:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (mtd->writesize > 512)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc5121_nfc_send_cmd(mtd, N=
AND_CMD_READSTART);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc5121_nfc_send_read_page(mtd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case NAND_CMD_READID:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc5121_nfc_send_read_id(mtd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case NAND_CMD_STATUS:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc5121_nfc_send_read_status(mtd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (chip->options & NAND_BUSWIDTH_16)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 prv->column =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 prv->column =3D 0;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 }
> +}
> +
> +/* Copy data from/to NFC spare buffers. */
> +static void mpc5121_nfc_copy_spare(struct mtd_info *mtd, uint offset,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 u8 *buffer, uint size, int wr)
> +{
> + =A0 =A0 =A0 struct nand_chip *nand =3D mtd->priv;
> + =A0 =A0 =A0 struct mpc5121_nfc_prv *prv =3D nand->priv;
> + =A0 =A0 =A0 uint o, s, sbsize, blksize;
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* NAND spare area is available through NFC spare buffers=
.
> + =A0 =A0 =A0 =A0* The NFC divides spare area into (page_size / 512) chun=
ks.
> + =A0 =A0 =A0 =A0* Each chunk is placed into separate spare memory area, =
using
> + =A0 =A0 =A0 =A0* first (spare_size / num_of_chunks) bytes of the buffer=
.
> + =A0 =A0 =A0 =A0*
> + =A0 =A0 =A0 =A0* For NAND device in which the spare area is not divided=
fully
> + =A0 =A0 =A0 =A0* by the number of chunks, number of used bytes in each =
spare
> + =A0 =A0 =A0 =A0* buffer is rounded down to the nearest even number of b=
ytes,
> + =A0 =A0 =A0 =A0* and all remaining bytes are added to the last used spa=
re area.
> + =A0 =A0 =A0 =A0*
> + =A0 =A0 =A0 =A0* For more information read section 26.6.10 of MPC5121e
> + =A0 =A0 =A0 =A0* Microcontroller Reference Manual, Rev. 3.
> + =A0 =A0 =A0 =A0*/
> +
> + =A0 =A0 =A0 /* Calculate number of valid bytes in each spare buffer */
> + =A0 =A0 =A0 sbsize =3D (mtd->oobsize / (mtd->writesize / 512)) & ~1;
> +
> + =A0 =A0 =A0 while (size) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Calculate spare buffer number */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D offset / sbsize;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (s > NFC_SPARE_BUFFERS - 1)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 s =3D NFC_SPARE_BUFFERS - 1=
;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* Calculate offset to requested data blo=
ck in selected spare
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* buffer and its size.
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 o =3D offset - (s * sbsize);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 blksize =3D min(sbsize - o, size);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (wr)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 memcpy_toio(prv->regs + NFC=
_SPARE_AREA(s) + o,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 buffer, blksize);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 memcpy_fromio(buffer,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 prv->regs +=
NFC_SPARE_AREA(s) + o, blksize);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 buffer +=3D blksize;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 offset +=3D blksize;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 size -=3D blksize;
> + =A0 =A0 =A0 };
> +}
> +
> +/* Copy data from/to NFC main and spare buffers */
> +static void mpc5121_nfc_buf_copy(struct mtd_info *mtd, u_char *buf, int =
len,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int wr=
)
> +{
> + =A0 =A0 =A0 struct nand_chip *chip =3D mtd->priv;
> + =A0 =A0 =A0 struct mpc5121_nfc_prv *prv =3D chip->priv;
> + =A0 =A0 =A0 uint c =3D prv->column;
> + =A0 =A0 =A0 uint l;
> +
> + =A0 =A0 =A0 /* Handle spare area access */
> + =A0 =A0 =A0 if (prv->spareonly || c >=3D mtd->writesize) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Calculate offset from beginning of spare=
area */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (c >=3D mtd->writesize)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 c -=3D mtd->writesize;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 prv->column +=3D len;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc5121_nfc_copy_spare(mtd, c, buf, len, wr=
);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* Handle main area access - limit copy length to prevent
> + =A0 =A0 =A0 =A0* crossing main/spare boundary.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 l =3D min((uint)len, mtd->writesize - c);
> + =A0 =A0 =A0 prv->column +=3D l;
> +
> + =A0 =A0 =A0 if (wr)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 memcpy_toio(prv->regs + NFC_MAIN_AREA(0) + =
c, buf, l);
> + =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 memcpy_fromio(buf, prv->regs + NFC_MAIN_ARE=
A(0) + c, l);
> +
> + =A0 =A0 =A0 /* Handle crossing main/spare boundary */
> + =A0 =A0 =A0 if (l !=3D len) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 buf +=3D l;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 len -=3D l;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc5121_nfc_buf_copy(mtd, buf, len, wr);
> + =A0 =A0 =A0 }
> +}
> +
> +/* Read data from NFC buffers */
> +static void mpc5121_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int =
len)
> +{
> + =A0 =A0 =A0 mpc5121_nfc_buf_copy(mtd, buf, len, 0);
> +}
> +
> +/* Write data to NFC buffers */
> +static void mpc5121_nfc_write_buf(struct mtd_info *mtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 const u_char *buf, int len)
> +{
> + =A0 =A0 =A0 mpc5121_nfc_buf_copy(mtd, (u_char *)buf, len, 1);
> +}
> +
> +/* Compare buffer with NAND flash */
> +static int mpc5121_nfc_verify_buf(struct mtd_info *mtd,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 const u_char *buf, int len)
> +{
> + =A0 =A0 =A0 u_char tmp[256];
> + =A0 =A0 =A0 uint bsize;
> +
> + =A0 =A0 =A0 while (len) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 bsize =3D min(len, 256);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc5121_nfc_read_buf(mtd, tmp, bsize);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (memcmp(buf, tmp, bsize))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 1;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 buf +=3D bsize;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 len -=3D bsize;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +/* Read byte from NFC buffers */
> +static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd)
> +{
> + =A0 =A0 =A0 u8 tmp;
> +
> + =A0 =A0 =A0 mpc5121_nfc_read_buf(mtd, &tmp, sizeof(tmp));
> +
> + =A0 =A0 =A0 return tmp;
> +}
> +
> +/* Read word from NFC buffers */
> +static u16 mpc5121_nfc_read_word(struct mtd_info *mtd)
> +{
> + =A0 =A0 =A0 u16 tmp;
> +
> + =A0 =A0 =A0 mpc5121_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp));
> +
> + =A0 =A0 =A0 return tmp;
> +}
> +
> +/*
> + * Read NFC configuration from Reset Config Word
> + *
> + * NFC is configured during reset in basis of information stored
> + * in Reset Config Word. There is no other way to set NAND block
> + * size, spare size and bus width.
> + */
> +static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
> +{
> + =A0 =A0 =A0 struct nand_chip *chip =3D mtd->priv;
> + =A0 =A0 =A0 struct mpc5121_nfc_prv *prv =3D chip->priv;
> + =A0 =A0 =A0 struct mpc512x_reset_module *rm;
> + =A0 =A0 =A0 struct device_node *rmnode;
> + =A0 =A0 =A0 uint rcw_pagesize =3D 0;
> + =A0 =A0 =A0 uint rcw_sparesize =3D 0;
> + =A0 =A0 =A0 uint rcw_width;
> + =A0 =A0 =A0 uint rcwh;
> + =A0 =A0 =A0 uint romloc, ps;
> +
> + =A0 =A0 =A0 rmnode =3D of_find_compatible_node(NULL, NULL, "fsl,mpc5121=
-reset");
> + =A0 =A0 =A0 if (!rmnode) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(prv->dev, "Missing 'fsl,mpc5121-res=
et' "
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 "node in device tree!\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENODEV;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 rm =3D of_iomap(rmnode, 0);
> + =A0 =A0 =A0 if (!rm) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(prv->dev, "Error mapping reset modu=
le node!\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EBUSY;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 rcwh =3D in_be32(&rm->rcwhr);
> +
> + =A0 =A0 =A0 /* Bit 6: NFC bus width */
> + =A0 =A0 =A0 rcw_width =3D ((rcwh >> 6) & 0x1) ? 2 : 1;
> +
> + =A0 =A0 =A0 /* Bit 7: NFC Page/Spare size */
> + =A0 =A0 =A0 ps =3D (rcwh >> 7) & 0x1;
> +
> + =A0 =A0 =A0 /* Bits [22:21]: ROM Location */
> + =A0 =A0 =A0 romloc =3D (rcwh >> 21) & 0x3;
> +
> + =A0 =A0 =A0 /* Decode RCW bits */
> + =A0 =A0 =A0 switch ((ps << 2) | romloc) {
> + =A0 =A0 =A0 case 0x00:
> + =A0 =A0 =A0 case 0x01:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rcw_pagesize =3D 512;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rcw_sparesize =3D 16;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case 0x02:
> + =A0 =A0 =A0 case 0x03:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rcw_pagesize =3D 4096;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rcw_sparesize =3D 128;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case 0x04:
> + =A0 =A0 =A0 case 0x05:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rcw_pagesize =3D 2048;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rcw_sparesize =3D 64;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 case 0x06:
> + =A0 =A0 =A0 case 0x07:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rcw_pagesize =3D 4096;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rcw_sparesize =3D 218;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 mtd->writesize =3D rcw_pagesize;
> + =A0 =A0 =A0 mtd->oobsize =3D rcw_sparesize;
> + =A0 =A0 =A0 if (rcw_width =3D=3D 2)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 chip->options |=3D NAND_BUSWIDTH_16;
> +
> + =A0 =A0 =A0 dev_notice(prv->dev, "Configured for "
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "%u-bit NAN=
D, page size %u "
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "with %u sp=
are.\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 rcw_width *=
8, rcw_pagesize,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 rcw_sparesi=
ze);
> + =A0 =A0 =A0 iounmap(rm);
> + =A0 =A0 =A0 of_node_put(rmnode);
> + =A0 =A0 =A0 return 0;
> +}
> +
> +/* Free driver resources */
> +static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd)
> +{
> + =A0 =A0 =A0 struct nand_chip *chip =3D mtd->priv;
> + =A0 =A0 =A0 struct mpc5121_nfc_prv *prv =3D chip->priv;
> +
> + =A0 =A0 =A0 if (prv->clk) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 clk_disable(prv->clk);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 clk_put(prv->clk);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 if (prv->csreg)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 iounmap(prv->csreg);
> +}
> +
> +static int __devinit mpc5121_nfc_probe(struct of_device *op,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 const struct of_device_id *match)
> +{
> + =A0 =A0 =A0 struct device_node *rootnode, *dn =3D op->node;
> + =A0 =A0 =A0 struct device *dev =3D &op->dev;
> + =A0 =A0 =A0 struct mpc5121_nfc_prv *prv;
> + =A0 =A0 =A0 struct resource res;
> + =A0 =A0 =A0 struct mtd_info *mtd;
> +#ifdef CONFIG_MTD_PARTITIONS
> + =A0 =A0 =A0 struct mtd_partition *parts;
> +#endif
> + =A0 =A0 =A0 struct nand_chip *chip;
> + =A0 =A0 =A0 unsigned long regs_paddr, regs_size;
> + =A0 =A0 =A0 const uint *chips_no;
> + =A0 =A0 =A0 int resettime =3D 0;
> + =A0 =A0 =A0 int retval =3D 0;
> + =A0 =A0 =A0 int rev, len;
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* Check SoC revision. This driver supports only NFC
> + =A0 =A0 =A0 =A0* in MPC5121 revision 2.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 rev =3D (mfspr(SPRN_SVR) >> 4) & 0xF;
> + =A0 =A0 =A0 if (rev !=3D 2) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "SoC revision %u is not suppor=
ted!\n", rev);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENXIO;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 prv =3D devm_kzalloc(dev, sizeof(*prv), GFP_KERNEL);
> + =A0 =A0 =A0 if (!prv) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Memory exhausted!\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENOMEM;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 mtd =3D &prv->mtd;
> + =A0 =A0 =A0 chip =3D &prv->chip;
> +
> + =A0 =A0 =A0 mtd->priv =3D chip;
> + =A0 =A0 =A0 chip->priv =3D prv;
> + =A0 =A0 =A0 prv->dev =3D dev;
> +
> + =A0 =A0 =A0 /* Read NFC configuration from Reset Config Word */
> + =A0 =A0 =A0 retval =3D mpc5121_nfc_read_hw_config(mtd);
> + =A0 =A0 =A0 if (retval) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Unable to read NFC config!\n"=
);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return retval;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 prv->irq =3D irq_of_parse_and_map(dn, 0);
> + =A0 =A0 =A0 if (prv->irq =3D=3D NO_IRQ) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Error mapping IRQ!\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 retval =3D of_address_to_resource(dn, 0, &res);
> + =A0 =A0 =A0 if (retval) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Error parsing memory region!\=
n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return retval;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 chips_no =3D of_get_property(dn, "chips", &len);
> + =A0 =A0 =A0 if (!chips_no || len !=3D sizeof(*chips_no)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Invalid/missing 'chips' prope=
rty!\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 regs_paddr =3D res.start;
> + =A0 =A0 =A0 regs_size =3D res.end - res.start + 1;
> +
> + =A0 =A0 =A0 if (!devm_request_mem_region(dev, regs_paddr, regs_size, DR=
V_NAME)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Error requesting memory regio=
n!\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EBUSY;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 prv->regs =3D devm_ioremap(dev, regs_paddr, regs_size);
> + =A0 =A0 =A0 if (!prv->regs) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Error mapping memory region!\=
n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENOMEM;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 mtd->name =3D "MPC5121 NAND";
> + =A0 =A0 =A0 chip->dev_ready =3D mpc5121_nfc_dev_ready;
> + =A0 =A0 =A0 chip->cmdfunc =3D mpc5121_nfc_command;
> + =A0 =A0 =A0 chip->read_byte =3D mpc5121_nfc_read_byte;
> + =A0 =A0 =A0 chip->read_word =3D mpc5121_nfc_read_word;
> + =A0 =A0 =A0 chip->read_buf =3D mpc5121_nfc_read_buf;
> + =A0 =A0 =A0 chip->write_buf =3D mpc5121_nfc_write_buf;
> + =A0 =A0 =A0 chip->verify_buf =3D mpc5121_nfc_verify_buf;
> + =A0 =A0 =A0 chip->select_chip =3D mpc5121_nfc_select_chip;
> + =A0 =A0 =A0 chip->options =3D NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT;
> + =A0 =A0 =A0 chip->ecc.mode =3D NAND_ECC_SOFT;
> +
> + =A0 =A0 =A0 /* Support external chip-select logic on ADS5121 board */
> + =A0 =A0 =A0 rootnode =3D of_find_node_by_path("/");
> + =A0 =A0 =A0 if (of_device_is_compatible(rootnode, "fsl,mpc5121ads")) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval =3D ads5121_chipselect_init(mtd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (retval) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Chipselect in=
it error!\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 of_node_put(rootnode);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return retval;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 chip->select_chip =3D ads5121_select_chip;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 of_node_put(rootnode);
> +
> + =A0 =A0 =A0 /* Enable NFC clock */
> + =A0 =A0 =A0 prv->clk =3D clk_get(dev, "nfc_clk");
> + =A0 =A0 =A0 if (!prv->clk) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Unable to acquire NFC clock!\=
n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval =3D -ENODEV;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto error;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 clk_enable(prv->clk);
> +
> + =A0 =A0 =A0 /* Reset NAND Flash controller */
> + =A0 =A0 =A0 nfc_set(mtd, NFC_CONFIG1, NFC_RESET);
> + =A0 =A0 =A0 while (nfc_read(mtd, NFC_CONFIG1) & NFC_RESET) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (resettime++ >=3D NFC_RESET_TIMEOUT) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Timeout while=
resetting NFC!\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval =3D -EINVAL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto error;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 udelay(1);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /* Enable write to NFC memory */
> + =A0 =A0 =A0 nfc_write(mtd, NFC_CONFIG, NFC_BLS_UNLOCKED);
> +
> + =A0 =A0 =A0 /* Enable write to all NAND pages */
> + =A0 =A0 =A0 nfc_write(mtd, NFC_UNLOCKSTART_BLK0, 0x0000);
> + =A0 =A0 =A0 nfc_write(mtd, NFC_UNLOCKEND_BLK0, 0xFFFF);
> + =A0 =A0 =A0 nfc_write(mtd, NFC_WRPROT, NFC_WPC_UNLOCK);
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* Setup NFC:
> + =A0 =A0 =A0 =A0* =A0 =A0 =A0- Big Endian transfers,
> + =A0 =A0 =A0 =A0* =A0 =A0 =A0- Interrupt after full page read/write.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 nfc_write(mtd, NFC_CONFIG1, NFC_BIG_ENDIAN | NFC_INT_MASK |
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 NFC_FULL_PAGE_INT);
> +
> + =A0 =A0 =A0 /* Set spare area size */
> + =A0 =A0 =A0 nfc_write(mtd, NFC_SPAS, mtd->oobsize >> 1);
> +
> + =A0 =A0 =A0 init_waitqueue_head(&prv->irq_waitq);
> + =A0 =A0 =A0 retval =3D devm_request_irq(dev, prv->irq, &mpc5121_nfc_irq=
, 0, DRV_NAME,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mtd);
> + =A0 =A0 =A0 if (retval) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Error requesting IRQ!\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto error;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /* Detect NAND chips */
> + =A0 =A0 =A0 if (nand_scan(mtd, *chips_no)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "NAND Flash not found !\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 devm_free_irq(dev, prv->irq, mtd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval =3D -ENXIO;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto error;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /* Set erase block size */
> + =A0 =A0 =A0 switch (mtd->erasesize / mtd->writesize) {
> + =A0 =A0 =A0 case 32:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 nfc_set(mtd, NFC_CONFIG1, NFC_PPB_32);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case 64:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 nfc_set(mtd, NFC_CONFIG1, NFC_PPB_64);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case 128:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 nfc_set(mtd, NFC_CONFIG1, NFC_PPB_128);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 case 256:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 nfc_set(mtd, NFC_CONFIG1, NFC_PPB_256);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> +
> + =A0 =A0 =A0 default:
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Unsupported NAND flash!\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 devm_free_irq(dev, prv->irq, mtd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval =3D -ENXIO;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto error;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 dev_set_drvdata(dev, mtd);
> +
> + =A0 =A0 =A0 /* Register device in MTD */
> +#ifdef CONFIG_MTD_PARTITIONS
> + =A0 =A0 =A0 retval =3D parse_mtd_partitions(mtd, mpc5121_nfc_pprobes, &=
parts, 0);
> +#ifdef CONFIG_MTD_OF_PARTS
> + =A0 =A0 =A0 if (retval =3D=3D 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval =3D of_mtd_parse_partitions(dev, dn,=
&parts);
> +#endif
> + =A0 =A0 =A0 if (retval < 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Error parsing MTD partitions!=
\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 devm_free_irq(dev, prv->irq, mtd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval =3D -EINVAL;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto error;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 if (retval > 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval =3D add_mtd_partitions(mtd, parts, r=
etval);
> + =A0 =A0 =A0 else
> +#endif
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 retval =3D add_mtd_device(mtd);
> +
> + =A0 =A0 =A0 if (retval) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Error adding MTD device!\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 devm_free_irq(dev, prv->irq, mtd);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto error;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return 0;
> +error:
> + =A0 =A0 =A0 mpc5121_nfc_free(dev, mtd);
> + =A0 =A0 =A0 return retval;
> +}
> +
> +static int __devexit mpc5121_nfc_remove(struct of_device *op)
> +{
> + =A0 =A0 =A0 struct device *dev =3D &op->dev;
> + =A0 =A0 =A0 struct mtd_info *mtd =3D dev_get_drvdata(dev);
> + =A0 =A0 =A0 struct nand_chip *chip =3D mtd->priv;
> + =A0 =A0 =A0 struct mpc5121_nfc_prv *prv =3D chip->priv;
> +
> + =A0 =A0 =A0 nand_release(mtd);
> + =A0 =A0 =A0 devm_free_irq(dev, prv->irq, mtd);
> + =A0 =A0 =A0 mpc5121_nfc_free(dev, mtd);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static struct of_device_id mpc5121_nfc_match[] __devinitdata =3D {
> + =A0 =A0 =A0 { .compatible =3D "fsl,mpc5121-nfc", },
> + =A0 =A0 =A0 {},
> +};
> +
> +static struct of_platform_driver mpc5121_nfc_driver =3D {
> + =A0 =A0 =A0 .match_table =A0 =A0=3D mpc5121_nfc_match,
> + =A0 =A0 =A0 .probe =A0 =A0 =A0 =A0 =A0=3D mpc5121_nfc_probe,
> + =A0 =A0 =A0 .remove =A0 =A0 =A0 =A0 =3D __devexit_p(mpc5121_nfc_remove)=
,
> + =A0 =A0 =A0 .driver =A0 =A0 =A0 =A0 =3D {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .name =A0 =3D DRV_NAME,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .owner =A0=3D THIS_MODULE,
> + =A0 =A0 =A0 },
> +};
> +
> +static int __init mpc5121_nfc_init(void)
> +{
> + =A0 =A0 =A0 return of_register_platform_driver(&mpc5121_nfc_driver);
> +}
> +
> +module_init(mpc5121_nfc_init);
> +
> +static void __exit mpc5121_nfc_cleanup(void)
> +{
> + =A0 =A0 =A0 of_unregister_platform_driver(&mpc5121_nfc_driver);
> +}
> +
> +module_exit(mpc5121_nfc_cleanup);
> +
> +MODULE_AUTHOR("Freescale Semiconductor, Inc.");
> +MODULE_DESCRIPTION("MPC5121 NAND MTD driver");
> +MODULE_LICENSE("GPL");
> --
> 1.6.3.3
>
>
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* Re: [PATCH v3 06/11] dma: Add MPC512x DMA driver
From: Grant Likely @ 2010-02-10 2:44 UTC (permalink / raw)
To: Anatolij Gustschin; +Cc: wd, dzu, linuxppc-dev, Dan Williams, Piotr Ziecik
In-Reply-To: <1265377377-29327-7-git-send-email-agust@denx.de>
On Fri, Feb 5, 2010 at 6:42 AM, Anatolij Gustschin <agust@denx.de> wrote:
> From: Piotr Ziecik <kosmo@semihalf.com>
>
> Adds initial version of MPC512x DMA driver.
> Only memory to memory transfers are currenly supported.
>
> Signed-off-by: Piotr Ziecik <kosmo@semihalf.com>
> Signed-off-by: Wolfgang Denk <wd@denx.de>
> Signed-off-by: Anatolij Gustschin <agust@denx.de>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Cc: Grant Likely <grant.likely@secretlab.ca>
> Cc: John Rigby <jcrigby@gmail.com>
on brief review...
Acked-by: Grant Likely <grant.likely@secretlab.ca>
> ---
> No changes since v2
>
> Changes since v1:
> =A0- move content of the mpc512x.h into the drivers .c file as
> =A0 it is only used by this DMA driver
> =A0- use __devinit/__devexit/__devexit_p as requested
> =A0- add unregistration of the dma device
> =A0- remove meaningless comment
>
> Changes since patch version submitted in May 2009:
> =A0- don't use wildcards in compatible property, use "fsl,mpc5121-dma"
> =A0- don't add "fsl,mpc5121-dma" compatible to of_bus_ids[] as the
> =A0 dma device is part of IMMR
>
> =A0drivers/dma/Kconfig =A0 =A0 =A0 | =A0 =A07 +
> =A0drivers/dma/Makefile =A0 =A0 =A0| =A0 =A01 +
> =A0drivers/dma/mpc512x_dma.c | =A0800 +++++++++++++++++++++++++++++++++++=
++++++++++
> =A03 files changed, 808 insertions(+), 0 deletions(-)
> =A0create mode 100644 drivers/dma/mpc512x_dma.c
>
> diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
> index e02d74b..ac67a53 100644
> --- a/drivers/dma/Kconfig
> +++ b/drivers/dma/Kconfig
> @@ -69,6 +69,13 @@ config FSL_DMA
> =A0 =A0 =A0 =A0 =A0The Elo is the DMA controller on some 82xx and 83xx pa=
rts, and the
> =A0 =A0 =A0 =A0 =A0Elo Plus is the DMA controller on 85xx and 86xx parts.
>
> +config MPC512X_DMA
> + =A0 =A0 =A0 tristate "Freescale MPC512x built-in DMA engine support"
> + =A0 =A0 =A0 depends on PPC_MPC512x
> + =A0 =A0 =A0 select DMA_ENGINE
> + =A0 =A0 =A0 ---help---
> + =A0 =A0 =A0 =A0 Enable support for the Freescale MPC512x built-in DMA e=
ngine.
> +
> =A0config MV_XOR
> =A0 =A0 =A0 =A0bool "Marvell XOR engine support"
> =A0 =A0 =A0 =A0depends on PLAT_ORION
> diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
> index 807053d..4696bcf 100644
> --- a/drivers/dma/Makefile
> +++ b/drivers/dma/Makefile
> @@ -4,6 +4,7 @@ obj-$(CONFIG_DMATEST) +=3D dmatest.o
> =A0obj-$(CONFIG_INTEL_IOATDMA) +=3D ioat/
> =A0obj-$(CONFIG_INTEL_IOP_ADMA) +=3D iop-adma.o
> =A0obj-$(CONFIG_FSL_DMA) +=3D fsldma.o
> +obj-$(CONFIG_MPC512X_DMA) +=3D mpc512x_dma.o
> =A0obj-$(CONFIG_MV_XOR) +=3D mv_xor.o
> =A0obj-$(CONFIG_DW_DMAC) +=3D dw_dmac.o
> =A0obj-$(CONFIG_AT_HDMAC) +=3D at_hdmac.o
> diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c
> new file mode 100644
> index 0000000..3fdf1f4
> --- /dev/null
> +++ b/drivers/dma/mpc512x_dma.c
> @@ -0,0 +1,800 @@
> +/*
> + * Copyright (C) Freescale Semicondutor, Inc. 2007, 2008.
> + * Copyright (C) Semihalf 2009
> + *
> + * Written by Piotr Ziecik <kosmo@semihalf.com>. Hardware description
> + * (defines, structures and comments) was taken from MPC5121 DMA driver
> + * written by Hongjun Chen <hong-jun.chen@freescale.com>.
> + *
> + * Approved as OSADL project by a majority of OSADL members and funded
> + * by OSADL membership fees in 2009; =A0for details see www.osadl.org.
> + *
> + * This program is free software; you can redistribute it and/or modify =
it
> + * under the terms of the GNU General Public License as published by the=
Free
> + * Software Foundation; either version 2 of the License, or (at your opt=
ion)
> + * any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but W=
ITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. =A0See the GNU General Public Licen=
se for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License alo=
ng with
> + * this program; if not, write to the Free Software Foundation, Inc., 59
> + * Temple Place - Suite 330, Boston, MA =A002111-1307, USA.
> + *
> + * The full GNU General Public License is included in this distribution =
in the
> + * file called COPYING.
> + */
> +
> +/*
> + * This is initial version of MPC5121 DMA driver. Only memory to memory
> + * transfers are supported (tested using dmatest module).
> + */
> +
> +#include <linux/module.h>
> +#include <linux/dmaengine.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +
> +#include <linux/random.h>
> +
> +/* Number of DMA Transfer descriptors allocated per channel */
> +#define MPC_DMA_DESCRIPTORS =A0 =A064
> +
> +/* Macro definitions */
> +#define MPC_DMA_CHANNELS =A0 =A0 =A0 64
> +#define MPC_DMA_TCD_OFFSET =A0 =A0 0x1000
> +
> +/* Arbitration mode of group and channel */
> +#define MPC_DMA_DMACR_EDCG =A0 =A0 (1 << 31)
> +#define MPC_DMA_DMACR_ERGA =A0 =A0 (1 << 3)
> +#define MPC_DMA_DMACR_ERCA =A0 =A0 (1 << 2)
> +
> +/* Error codes */
> +#define MPC_DMA_DMAES_VLD =A0 =A0 =A0(1 << 31)
> +#define MPC_DMA_DMAES_GPE =A0 =A0 =A0(1 << 15)
> +#define MPC_DMA_DMAES_CPE =A0 =A0 =A0(1 << 14)
> +#define MPC_DMA_DMAES_ERRCHN(err) \
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (((err) >> =
8) & 0x3f)
> +#define MPC_DMA_DMAES_SAE =A0 =A0 =A0(1 << 7)
> +#define MPC_DMA_DMAES_SOE =A0 =A0 =A0(1 << 6)
> +#define MPC_DMA_DMAES_DAE =A0 =A0 =A0(1 << 5)
> +#define MPC_DMA_DMAES_DOE =A0 =A0 =A0(1 << 4)
> +#define MPC_DMA_DMAES_NCE =A0 =A0 =A0(1 << 3)
> +#define MPC_DMA_DMAES_SGE =A0 =A0 =A0(1 << 2)
> +#define MPC_DMA_DMAES_SBE =A0 =A0 =A0(1 << 1)
> +#define MPC_DMA_DMAES_DBE =A0 =A0 =A0(1 << 0)
> +
> +#define MPC_DMA_TSIZE_1 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x00
> +#define MPC_DMA_TSIZE_2 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x01
> +#define MPC_DMA_TSIZE_4 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x02
> +#define MPC_DMA_TSIZE_16 =A0 =A0 =A0 0x04
> +#define MPC_DMA_TSIZE_32 =A0 =A0 =A0 0x05
> +
> +/* MPC5121 DMA engine registers */
> +struct __attribute__ ((__packed__)) mpc_dma_regs {
> + =A0 =A0 =A0 /* 0x00 */
> + =A0 =A0 =A0 u32 dmacr; =A0 =A0 =A0 =A0 =A0 =A0 =A0/* DMA control regist=
er */
> + =A0 =A0 =A0 u32 dmaes; =A0 =A0 =A0 =A0 =A0 =A0 =A0/* DMA error status *=
/
> + =A0 =A0 =A0 /* 0x08 */
> + =A0 =A0 =A0 u32 dmaerqh; =A0 =A0 =A0 =A0 =A0 =A0/* DMA enable request h=
igh(channels 63~32) */
> + =A0 =A0 =A0 u32 dmaerql; =A0 =A0 =A0 =A0 =A0 =A0/* DMA enable request l=
ow(channels 31~0) */
> + =A0 =A0 =A0 u32 dmaeeih; =A0 =A0 =A0 =A0 =A0 =A0/* DMA enable error int=
errupt high(ch63~32) */
> + =A0 =A0 =A0 u32 dmaeeil; =A0 =A0 =A0 =A0 =A0 =A0/* DMA enable error int=
errupt low(ch31~0) */
> + =A0 =A0 =A0 /* 0x18 */
> + =A0 =A0 =A0 u8 dmaserq; =A0 =A0 =A0 =A0 =A0 =A0 /* DMA set enable reque=
st */
> + =A0 =A0 =A0 u8 dmacerq; =A0 =A0 =A0 =A0 =A0 =A0 /* DMA clear enable req=
uest */
> + =A0 =A0 =A0 u8 dmaseei; =A0 =A0 =A0 =A0 =A0 =A0 /* DMA set enable error=
interrupt */
> + =A0 =A0 =A0 u8 dmaceei; =A0 =A0 =A0 =A0 =A0 =A0 /* DMA clear enable err=
or interrupt */
> + =A0 =A0 =A0 /* 0x1c */
> + =A0 =A0 =A0 u8 dmacint; =A0 =A0 =A0 =A0 =A0 =A0 /* DMA clear interrupt =
request */
> + =A0 =A0 =A0 u8 dmacerr; =A0 =A0 =A0 =A0 =A0 =A0 /* DMA clear error */
> + =A0 =A0 =A0 u8 dmassrt; =A0 =A0 =A0 =A0 =A0 =A0 /* DMA set start bit */
> + =A0 =A0 =A0 u8 dmacdne; =A0 =A0 =A0 =A0 =A0 =A0 /* DMA clear DONE statu=
s bit */
> + =A0 =A0 =A0 /* 0x20 */
> + =A0 =A0 =A0 u32 dmainth; =A0 =A0 =A0 =A0 =A0 =A0/* DMA interrupt reques=
t high(ch63~32) */
> + =A0 =A0 =A0 u32 dmaintl; =A0 =A0 =A0 =A0 =A0 =A0/* DMA interrupt reques=
t low(ch31~0) */
> + =A0 =A0 =A0 u32 dmaerrh; =A0 =A0 =A0 =A0 =A0 =A0/* DMA error high(ch63~=
32) */
> + =A0 =A0 =A0 u32 dmaerrl; =A0 =A0 =A0 =A0 =A0 =A0/* DMA error low(ch31~0=
) */
> + =A0 =A0 =A0 /* 0x30 */
> + =A0 =A0 =A0 u32 dmahrsh; =A0 =A0 =A0 =A0 =A0 =A0/* DMA hw request statu=
s high(ch63~32) */
> + =A0 =A0 =A0 u32 dmahrsl; =A0 =A0 =A0 =A0 =A0 =A0/* DMA hardware request=
status low(ch31~0) */
> + =A0 =A0 =A0 u32 dmaihsa; =A0 =A0 =A0 =A0 =A0 =A0/* DMA interrupt high s=
elect AXE(ch63~32) */
> + =A0 =A0 =A0 u32 dmailsa; =A0 =A0 =A0 =A0 =A0 =A0/* DMA interrupt low se=
lect AXE(ch31~0) */
> + =A0 =A0 =A0 /* 0x40 ~ 0xff */
> + =A0 =A0 =A0 u32 reserve0[48]; =A0 =A0 =A0 /* Reserved */
> + =A0 =A0 =A0 /* 0x100 */
> + =A0 =A0 =A0 u8 dchpri[MPC_DMA_CHANNELS];
> + =A0 =A0 =A0 /* DMA channels(0~63) priority */
> +};
> +
> +struct __attribute__ ((__packed__)) mpc_dma_tcd {
> + =A0 =A0 =A0 /* 0x00 */
> + =A0 =A0 =A0 u32 saddr; =A0 =A0 =A0 =A0 =A0 =A0 =A0/* Source address */
> +
> + =A0 =A0 =A0 u32 smod:5; =A0 =A0 =A0 =A0 =A0 =A0 /* Source address modul=
o */
> + =A0 =A0 =A0 u32 ssize:3; =A0 =A0 =A0 =A0 =A0 =A0/* Source data transfer=
size */
> + =A0 =A0 =A0 u32 dmod:5; =A0 =A0 =A0 =A0 =A0 =A0 /* Destination address =
modulo */
> + =A0 =A0 =A0 u32 dsize:3; =A0 =A0 =A0 =A0 =A0 =A0/* Destination data tra=
nsfer size */
> + =A0 =A0 =A0 u32 soff:16; =A0 =A0 =A0 =A0 =A0 =A0/* Signed source addres=
s offset */
> +
> + =A0 =A0 =A0 /* 0x08 */
> + =A0 =A0 =A0 u32 nbytes; =A0 =A0 =A0 =A0 =A0 =A0 /* Inner "minor" byte c=
ount */
> + =A0 =A0 =A0 u32 slast; =A0 =A0 =A0 =A0 =A0 =A0 =A0/* Last source addres=
s adjustment */
> + =A0 =A0 =A0 u32 daddr; =A0 =A0 =A0 =A0 =A0 =A0 =A0/* Destination addres=
s */
> +
> + =A0 =A0 =A0 /* 0x14 */
> + =A0 =A0 =A0 u32 citer_elink:1; =A0 =A0 =A0/* Enable channel-to-channel =
linking on
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* minor =
loop complete
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 u32 citer_linkch:6; =A0 =A0 /* Link channel for minor loop =
complete */
> + =A0 =A0 =A0 u32 citer:9; =A0 =A0 =A0 =A0 =A0 =A0/* Current "major" iter=
ation count */
> + =A0 =A0 =A0 u32 doff:16; =A0 =A0 =A0 =A0 =A0 =A0/* Signed destination a=
ddress offset */
> +
> + =A0 =A0 =A0 /* 0x18 */
> + =A0 =A0 =A0 u32 dlast_sga; =A0 =A0 =A0 =A0 =A0/* Last Destination addre=
ss adjustment/scatter
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* gather=
address
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> +
> + =A0 =A0 =A0 /* 0x1c */
> + =A0 =A0 =A0 u32 biter_elink:1; =A0 =A0 =A0/* Enable channel-to-channel =
linking on major
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* loop c=
omplete
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 u32 biter_linkch:6;
> + =A0 =A0 =A0 u32 biter:9; =A0 =A0 =A0 =A0 =A0 =A0/* Beginning "major" it=
eration count */
> + =A0 =A0 =A0 u32 bwc:2; =A0 =A0 =A0 =A0 =A0 =A0 =A0/* Bandwidth control =
*/
> + =A0 =A0 =A0 u32 major_linkch:6; =A0 =A0 /* Link channel number */
> + =A0 =A0 =A0 u32 done:1; =A0 =A0 =A0 =A0 =A0 =A0 /* Channel done */
> + =A0 =A0 =A0 u32 active:1; =A0 =A0 =A0 =A0 =A0 /* Channel active */
> + =A0 =A0 =A0 u32 major_elink:1; =A0 =A0 =A0/* Enable channel-to-channel =
linking on major
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* loop c=
omplete
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 u32 e_sg:1; =A0 =A0 =A0 =A0 =A0 =A0 /* Enable scatter/gathe=
r processing */
> + =A0 =A0 =A0 u32 d_req:1; =A0 =A0 =A0 =A0 =A0 =A0/* Disable request */
> + =A0 =A0 =A0 u32 int_half:1; =A0 =A0 =A0 =A0 /* Enable an interrupt when=
major counter is
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* half c=
omplete
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 u32 int_maj:1; =A0 =A0 =A0 =A0 =A0/* Enable an interrupt wh=
en major iteration
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* count =
completes
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 u32 start:1; =A0 =A0 =A0 =A0 =A0 =A0/* Channel start */
> +};
> +
> +struct mpc_dma_desc {
> + =A0 =A0 =A0 struct dma_async_tx_descriptor =A0desc;
> + =A0 =A0 =A0 struct mpc_dma_tcd =A0 =A0 =A0 =A0 =A0 =A0 =A0*tcd;
> + =A0 =A0 =A0 dma_addr_t =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0tcd_p=
addr;
> + =A0 =A0 =A0 int =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
error;
> + =A0 =A0 =A0 struct list_head =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0node;
> +};
> +
> +struct mpc_dma_chan {
> + =A0 =A0 =A0 struct dma_chan =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 chan;
> + =A0 =A0 =A0 struct list_head =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0free;
> + =A0 =A0 =A0 struct list_head =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0prepared;
> + =A0 =A0 =A0 struct list_head =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0queued;
> + =A0 =A0 =A0 struct list_head =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0active;
> + =A0 =A0 =A0 struct list_head =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0completed;
> + =A0 =A0 =A0 struct mpc_dma_tcd =A0 =A0 =A0 =A0 =A0 =A0 =A0*tcd;
> + =A0 =A0 =A0 dma_addr_t =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0tcd_p=
addr;
> + =A0 =A0 =A0 dma_cookie_t =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0complet=
ed_cookie;
> +
> + =A0 =A0 =A0 /* Lock for this structure */
> + =A0 =A0 =A0 spinlock_t =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0lock;
> +};
> +
> +struct mpc_dma {
> + =A0 =A0 =A0 struct dma_device =A0 =A0 =A0 =A0 =A0 =A0 =A0 dma;
> + =A0 =A0 =A0 struct tasklet_struct =A0 =A0 =A0 =A0 =A0 tasklet;
> + =A0 =A0 =A0 struct mpc_dma_chan =A0 =A0 =A0 =A0 =A0 =A0 channels[MPC_DM=
A_CHANNELS];
> + =A0 =A0 =A0 struct mpc_dma_regs __iomem =A0 =A0 *regs;
> + =A0 =A0 =A0 struct mpc_dma_tcd __iomem =A0 =A0 =A0*tcd;
> + =A0 =A0 =A0 int =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
irq;
> + =A0 =A0 =A0 uint =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0error_status;
> +
> + =A0 =A0 =A0 /* Lock for error_status field in this structure */
> + =A0 =A0 =A0 spinlock_t =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0error=
_status_lock;
> +};
> +
> +#define DRV_NAME =A0 =A0 =A0 "mpc512x_dma"
> +
> +/* Convert struct dma_chan to struct mpc_dma_chan */
> +static inline struct mpc_dma_chan *dma_chan_to_mpc_dma_chan(struct dma_c=
han *c)
> +{
> + =A0 =A0 =A0 return container_of(c, struct mpc_dma_chan, chan);
> +}
> +
> +/* Convert struct dma_chan to struct mpc_dma */
> +static inline struct mpc_dma *dma_chan_to_mpc_dma(struct dma_chan *c)
> +{
> + =A0 =A0 =A0 struct mpc_dma_chan *mchan =3D dma_chan_to_mpc_dma_chan(c);
> + =A0 =A0 =A0 return container_of(mchan, struct mpc_dma, channels[c->chan=
_id]);
> +}
> +
> +/*
> + * Execute all queued DMA descriptors.
> + *
> + * Following requirements must be met while calling mpc_dma_execute():
> + * =A0 =A0 a) mchan->lock is acquired,
> + * =A0 =A0 b) mchan->active list is empty,
> + * =A0 =A0 c) mchan->queued list contains at least one entry.
> + */
> +static void mpc_dma_execute(struct mpc_dma_chan *mchan)
> +{
> + =A0 =A0 =A0 struct mpc_dma *mdma =3D dma_chan_to_mpc_dma(&mchan->chan);
> + =A0 =A0 =A0 struct mpc_dma_desc *first =3D NULL;
> + =A0 =A0 =A0 struct mpc_dma_desc *prev =3D NULL;
> + =A0 =A0 =A0 struct mpc_dma_desc *mdesc;
> + =A0 =A0 =A0 int cid =3D mchan->chan.chan_id;
> +
> + =A0 =A0 =A0 /* Move all queued descriptors to active list */
> + =A0 =A0 =A0 list_splice_tail_init(&mchan->queued, &mchan->active);
> +
> + =A0 =A0 =A0 /* Chain descriptors into one transaction */
> + =A0 =A0 =A0 list_for_each_entry(mdesc, &mchan->active, node) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!first)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 first =3D mdesc;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!prev) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 prev =3D mdesc;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 prev->tcd->dlast_sga =3D mdesc->tcd_paddr;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 prev->tcd->e_sg =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mdesc->tcd->start =3D 1;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 prev =3D mdesc;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 prev->tcd->start =3D 0;
> + =A0 =A0 =A0 prev->tcd->int_maj =3D 1;
> +
> + =A0 =A0 =A0 /* Send first descriptor in chain into hardware */
> + =A0 =A0 =A0 memcpy_toio(&mdma->tcd[cid], first->tcd, sizeof(struct mpc_=
dma_tcd));
> + =A0 =A0 =A0 out_8(&mdma->regs->dmassrt, cid);
> +}
> +
> +/* Handle interrupt on one half of DMA controller (32 channels) */
> +static void mpc_dma_irq_process(struct mpc_dma *mdma, u32 is, u32 es, in=
t off)
> +{
> + =A0 =A0 =A0 struct mpc_dma_chan *mchan;
> + =A0 =A0 =A0 struct mpc_dma_desc *mdesc;
> + =A0 =A0 =A0 u32 status =3D is | es;
> + =A0 =A0 =A0 int ch;
> +
> + =A0 =A0 =A0 while ((ch =3D fls(status) - 1) >=3D 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 status &=3D ~(1 << ch);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mchan =3D &mdma->channels[ch + off];
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_lock(&mchan->lock);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Check error status */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (es & (1 << ch))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_for_each_entry(mdesc, =
&mchan->active, node)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mdesc->erro=
r =3D -EIO;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Execute queued descriptors */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_splice_tail_init(&mchan->active, &mcha=
n->completed);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!list_empty(&mchan->queued))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc_dma_execute(mchan);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock(&mchan->lock);
> + =A0 =A0 =A0 }
> +}
> +
> +/* Interrupt handler */
> +static irqreturn_t mpc_dma_irq(int irq, void *data)
> +{
> + =A0 =A0 =A0 struct mpc_dma *mdma =3D data;
> + =A0 =A0 =A0 uint es;
> +
> + =A0 =A0 =A0 /* Save error status register */
> + =A0 =A0 =A0 es =3D in_be32(&mdma->regs->dmaes);
> + =A0 =A0 =A0 spin_lock(&mdma->error_status_lock);
> + =A0 =A0 =A0 if ((es & MPC_DMA_DMAES_VLD) && mdma->error_status =3D=3D 0=
)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mdma->error_status =3D es;
> + =A0 =A0 =A0 spin_unlock(&mdma->error_status_lock);
> +
> + =A0 =A0 =A0 /* Handle interrupt on each channel */
> + =A0 =A0 =A0 mpc_dma_irq_process(mdma, in_be32(&mdma->regs->dmainth),
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 in_be32(&mdma->regs->dmaerrh), 32);
> + =A0 =A0 =A0 mpc_dma_irq_process(mdma, in_be32(&mdma->regs->dmaintl),
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 in_be32(&mdma->regs->dmaerrl), 0);
> +
> + =A0 =A0 =A0 /* Ack interrupt on all channels */
> + =A0 =A0 =A0 out_be32(&mdma->regs->dmainth, 0xFFFFFFFF);
> + =A0 =A0 =A0 out_be32(&mdma->regs->dmaintl, 0xFFFFFFFF);
> + =A0 =A0 =A0 out_be32(&mdma->regs->dmaerrh, 0xFFFFFFFF);
> + =A0 =A0 =A0 out_be32(&mdma->regs->dmaerrl, 0xFFFFFFFF);
> +
> + =A0 =A0 =A0 /* Schedule tasklet */
> + =A0 =A0 =A0 tasklet_schedule(&mdma->tasklet);
> +
> + =A0 =A0 =A0 return IRQ_HANDLED;
> +}
> +
> +/* DMA Tasklet */
> +static void mpc_dma_tasklet(unsigned long data)
> +{
> + =A0 =A0 =A0 struct mpc_dma *mdma =3D (void *)data;
> + =A0 =A0 =A0 dma_cookie_t last_cookie =3D 0;
> + =A0 =A0 =A0 struct mpc_dma_chan *mchan;
> + =A0 =A0 =A0 struct mpc_dma_desc *mdesc;
> + =A0 =A0 =A0 struct dma_async_tx_descriptor *desc;
> + =A0 =A0 =A0 unsigned long flags;
> + =A0 =A0 =A0 LIST_HEAD(list);
> + =A0 =A0 =A0 uint es;
> + =A0 =A0 =A0 int i;
> +
> + =A0 =A0 =A0 spin_lock_irqsave(&mdma->error_status_lock, flags);
> + =A0 =A0 =A0 es =3D mdma->error_status;
> + =A0 =A0 =A0 mdma->error_status =3D 0;
> + =A0 =A0 =A0 spin_unlock_irqrestore(&mdma->error_status_lock, flags);
> +
> + =A0 =A0 =A0 /* Print nice error report */
> + =A0 =A0 =A0 if (es) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(mdma->dma.dev,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "Hardware reported followin=
g error(s) on channel %u:\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 MPC_DMA_DMAES_ERRCHN(es));
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (es & MPC_DMA_DMAES_GPE)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(mdma->dma.dev, "- G=
roup Priority Error\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (es & MPC_DMA_DMAES_CPE)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(mdma->dma.dev, "- C=
hannel Priority Error\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (es & MPC_DMA_DMAES_SAE)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(mdma->dma.dev, "- S=
ource Address Error\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (es & MPC_DMA_DMAES_SOE)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(mdma->dma.dev, "- S=
ource Offset"
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 " Configuration Error\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (es & MPC_DMA_DMAES_DAE)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(mdma->dma.dev, "- D=
estination Address"
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 " Error\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (es & MPC_DMA_DMAES_DOE)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(mdma->dma.dev, "- D=
estination Offset"
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 " Configuration Error\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (es & MPC_DMA_DMAES_NCE)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(mdma->dma.dev, "- N=
Bytes/Citter"
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 " Configuration Error\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (es & MPC_DMA_DMAES_SGE)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(mdma->dma.dev, "- S=
catter/Gather"
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 " Configuration Error\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (es & MPC_DMA_DMAES_SBE)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(mdma->dma.dev, "- S=
ource Bus Error\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (es & MPC_DMA_DMAES_DBE)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(mdma->dma.dev, "- D=
estination Bus Error\n");
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 for (i =3D 0; i < mdma->dma.chancnt; i++) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mchan =3D &mdma->channels[i];
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Get all completed descriptors */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_lock_irqsave(&mchan->lock, flags);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!list_empty(&mchan->completed))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_splice_tail_init(&mcha=
n->completed, &list);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock_irqrestore(&mchan->lock, flags)=
;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (list_empty(&list))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Execute callbacks and run dependencies *=
/
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_for_each_entry(mdesc, &list, node) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 desc =3D &mdesc->desc;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (desc->callback)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 desc->callb=
ack(desc->callback_param);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 last_cookie =3D desc->cooki=
e;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dma_run_dependencies(desc);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Free descriptors */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_lock_irqsave(&mchan->lock, flags);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_splice_tail_init(&list, &mchan->free);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mchan->completed_cookie =3D last_cookie;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock_irqrestore(&mchan->lock, flags)=
;
> + =A0 =A0 =A0 }
> +}
> +
> +/* Submit descriptor to hardware */
> +static dma_cookie_t mpc_dma_tx_submit(struct dma_async_tx_descriptor *tx=
d)
> +{
> + =A0 =A0 =A0 struct mpc_dma_chan *mchan =3D dma_chan_to_mpc_dma_chan(txd=
->chan);
> + =A0 =A0 =A0 struct mpc_dma_desc *mdesc;
> + =A0 =A0 =A0 unsigned long flags;
> + =A0 =A0 =A0 dma_cookie_t cookie;
> +
> + =A0 =A0 =A0 mdesc =3D container_of(txd, struct mpc_dma_desc, desc);
> +
> + =A0 =A0 =A0 spin_lock_irqsave(&mchan->lock, flags);
> +
> + =A0 =A0 =A0 /* Move descriptor to queue */
> + =A0 =A0 =A0 list_move_tail(&mdesc->node, &mchan->queued);
> +
> + =A0 =A0 =A0 /* If channel is idle, execute all queued descriptors */
> + =A0 =A0 =A0 if (list_empty(&mchan->active))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc_dma_execute(mchan);
> +
> + =A0 =A0 =A0 /* Update cookie */
> + =A0 =A0 =A0 cookie =3D mchan->chan.cookie + 1;
> + =A0 =A0 =A0 if (cookie <=3D 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 cookie =3D 1;
> +
> + =A0 =A0 =A0 mchan->chan.cookie =3D cookie;
> + =A0 =A0 =A0 mdesc->desc.cookie =3D cookie;
> +
> + =A0 =A0 =A0 spin_unlock_irqrestore(&mchan->lock, flags);
> +
> + =A0 =A0 =A0 return cookie;
> +}
> +
> +/* Alloc channel resources */
> +static int mpc_dma_alloc_chan_resources(struct dma_chan *chan)
> +{
> + =A0 =A0 =A0 struct mpc_dma *mdma =3D dma_chan_to_mpc_dma(chan);
> + =A0 =A0 =A0 struct mpc_dma_chan *mchan =3D dma_chan_to_mpc_dma_chan(cha=
n);
> + =A0 =A0 =A0 struct mpc_dma_desc *mdesc;
> + =A0 =A0 =A0 struct mpc_dma_tcd *tcd;
> + =A0 =A0 =A0 dma_addr_t tcd_paddr;
> + =A0 =A0 =A0 unsigned long flags;
> + =A0 =A0 =A0 LIST_HEAD(descs);
> + =A0 =A0 =A0 int i;
> +
> + =A0 =A0 =A0 /* Alloc DMA memory for Transfer Control Descriptors */
> + =A0 =A0 =A0 tcd =3D dma_alloc_coherent(mdma->dma.dev,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 MPC_DMA_DESCRIPTORS * sizeo=
f(struct mpc_dma_tcd),
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &tcd_paddr, GFP_KERNEL);
> + =A0 =A0 =A0 if (!tcd)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENOMEM;
> +
> + =A0 =A0 =A0 /* Alloc descriptors for this channel */
> + =A0 =A0 =A0 for (i =3D 0; i < MPC_DMA_DESCRIPTORS; i++) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mdesc =3D kzalloc(sizeof(struct mpc_dma_des=
c), GFP_KERNEL);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!mdesc) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_notice(mdma->dma.dev, "=
Memory allocation error. "
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 "Allocated only %u descriptors\n", i);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dma_async_tx_descriptor_init(&mdesc->desc, =
chan);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mdesc->desc.flags =3D DMA_CTRL_ACK;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mdesc->desc.tx_submit =3D mpc_dma_tx_submit=
;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mdesc->tcd =3D &tcd[i];
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mdesc->tcd_paddr =3D tcd_paddr + (i * sizeo=
f(struct mpc_dma_tcd));
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_add_tail(&mdesc->node, &descs);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 /* Return error only if no descriptors were allocated */
> + =A0 =A0 =A0 if (i =3D=3D 0) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dma_free_coherent(mdma->dma.dev,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 MPC_DMA_DESCRIPTORS * sizeo=
f(struct mpc_dma_tcd),
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd, tcd_paddr);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENOMEM;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 spin_lock_irqsave(&mchan->lock, flags);
> + =A0 =A0 =A0 mchan->tcd =3D tcd;
> + =A0 =A0 =A0 mchan->tcd_paddr =3D tcd_paddr;
> + =A0 =A0 =A0 list_splice_tail_init(&descs, &mchan->free);
> + =A0 =A0 =A0 spin_unlock_irqrestore(&mchan->lock, flags);
> +
> + =A0 =A0 =A0 /* Enable Error Interrupt */
> + =A0 =A0 =A0 out_8(&mdma->regs->dmaseei, chan->chan_id);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +/* Free channel resources */
> +static void mpc_dma_free_chan_resources(struct dma_chan *chan)
> +{
> + =A0 =A0 =A0 struct mpc_dma *mdma =3D dma_chan_to_mpc_dma(chan);
> + =A0 =A0 =A0 struct mpc_dma_chan *mchan =3D dma_chan_to_mpc_dma_chan(cha=
n);
> + =A0 =A0 =A0 struct mpc_dma_desc *mdesc, *tmp;
> + =A0 =A0 =A0 struct mpc_dma_tcd *tcd;
> + =A0 =A0 =A0 dma_addr_t tcd_paddr;
> + =A0 =A0 =A0 unsigned long flags;
> + =A0 =A0 =A0 LIST_HEAD(descs);
> +
> + =A0 =A0 =A0 spin_lock_irqsave(&mchan->lock, flags);
> +
> + =A0 =A0 =A0 /* Channel must be idle */
> + =A0 =A0 =A0 BUG_ON(!list_empty(&mchan->prepared));
> + =A0 =A0 =A0 BUG_ON(!list_empty(&mchan->queued));
> + =A0 =A0 =A0 BUG_ON(!list_empty(&mchan->active));
> + =A0 =A0 =A0 BUG_ON(!list_empty(&mchan->completed));
> +
> + =A0 =A0 =A0 /* Move data */
> + =A0 =A0 =A0 list_splice_tail_init(&mchan->free, &descs);
> + =A0 =A0 =A0 tcd =3D mchan->tcd;
> + =A0 =A0 =A0 tcd_paddr =3D mchan->tcd_paddr;
> +
> + =A0 =A0 =A0 spin_unlock_irqrestore(&mchan->lock, flags);
> +
> + =A0 =A0 =A0 /* Free DMA memory used by descriptors */
> + =A0 =A0 =A0 dma_free_coherent(mdma->dma.dev,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 MPC_DMA_DESCRIPTORS * sizeo=
f(struct mpc_dma_tcd),
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd, tcd_paddr);
> +
> + =A0 =A0 =A0 /* Free descriptors */
> + =A0 =A0 =A0 list_for_each_entry_safe(mdesc, tmp, &descs, node)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 kfree(mdesc);
> +
> + =A0 =A0 =A0 /* Disable Error Interrupt */
> + =A0 =A0 =A0 out_8(&mdma->regs->dmaceei, chan->chan_id);
> +}
> +
> +/* Send all pending descriptor to hardware */
> +static void mpc_dma_issue_pending(struct dma_chan *chan)
> +{
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* We are posting descriptors to the hardware as soon as
> + =A0 =A0 =A0 =A0* they are ready, so this function does nothing.
> + =A0 =A0 =A0 =A0*/
> +}
> +
> +/* Check request completion status */
> +static enum dma_status
> +mpc_dma_is_tx_complete(struct dma_chan *chan, dma_cookie_t cookie,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 dma_cookie_t *done, dma_cookie_t *used)
> +{
> + =A0 =A0 =A0 struct mpc_dma_chan *mchan =3D dma_chan_to_mpc_dma_chan(cha=
n);
> + =A0 =A0 =A0 unsigned long flags;
> + =A0 =A0 =A0 dma_cookie_t last_used;
> + =A0 =A0 =A0 dma_cookie_t last_complete;
> +
> + =A0 =A0 =A0 spin_lock_irqsave(&mchan->lock, flags);
> + =A0 =A0 =A0 last_used =3D mchan->chan.cookie;
> + =A0 =A0 =A0 last_complete =3D mchan->completed_cookie;
> + =A0 =A0 =A0 spin_unlock_irqrestore(&mchan->lock, flags);
> +
> + =A0 =A0 =A0 if (done)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 *done =3D last_complete;
> +
> + =A0 =A0 =A0 if (used)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 *used =3D last_used;
> +
> + =A0 =A0 =A0 return dma_async_is_complete(cookie, last_complete, last_us=
ed);
> +}
> +
> +/* Prepare descriptor for memory to memory copy */
> +static struct dma_async_tx_descriptor *
> +mpc_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t sr=
c,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 size_t len, unsigned long flags)
> +{
> + =A0 =A0 =A0 struct mpc_dma_chan *mchan =3D dma_chan_to_mpc_dma_chan(cha=
n);
> + =A0 =A0 =A0 struct mpc_dma_desc *mdesc =3D NULL;
> + =A0 =A0 =A0 struct mpc_dma_tcd *tcd;
> + =A0 =A0 =A0 unsigned long iflags;
> +
> + =A0 =A0 =A0 /* Get free descriptor */
> + =A0 =A0 =A0 spin_lock_irqsave(&mchan->lock, iflags);
> + =A0 =A0 =A0 if (!list_empty(&mchan->free)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mdesc =3D list_first_entry(&mchan->free, st=
ruct mpc_dma_desc,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 node);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_del(&mdesc->node);
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 spin_unlock_irqrestore(&mchan->lock, iflags);
> +
> + =A0 =A0 =A0 if (!mdesc)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return NULL;
> +
> + =A0 =A0 =A0 mdesc->error =3D 0;
> + =A0 =A0 =A0 tcd =3D mdesc->tcd;
> +
> + =A0 =A0 =A0 /* Prepare Transfer Control Descriptor for this transaction=
*/
> + =A0 =A0 =A0 memset(tcd, 0, sizeof(struct mpc_dma_tcd));
> +
> + =A0 =A0 =A0 if (IS_ALIGNED(src | dst | len, 32)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->ssize =3D MPC_DMA_TSIZE_32;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->dsize =3D MPC_DMA_TSIZE_32;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->soff =3D 32;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->doff =3D 32;
> + =A0 =A0 =A0 } else if (IS_ALIGNED(src | dst | len, 16)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->ssize =3D MPC_DMA_TSIZE_16;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->dsize =3D MPC_DMA_TSIZE_16;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->soff =3D 16;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->doff =3D 16;
> + =A0 =A0 =A0 } else if (IS_ALIGNED(src | dst | len, 4)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->ssize =3D MPC_DMA_TSIZE_4;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->dsize =3D MPC_DMA_TSIZE_4;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->soff =3D 4;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->doff =3D 4;
> + =A0 =A0 =A0 } else if (IS_ALIGNED(src | dst | len, 2)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->ssize =3D MPC_DMA_TSIZE_2;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->dsize =3D MPC_DMA_TSIZE_2;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->soff =3D 2;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->doff =3D 2;
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->ssize =3D MPC_DMA_TSIZE_1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->dsize =3D MPC_DMA_TSIZE_1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->soff =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tcd->doff =3D 1;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 tcd->saddr =3D src;
> + =A0 =A0 =A0 tcd->daddr =3D dst;
> + =A0 =A0 =A0 tcd->nbytes =3D len;
> + =A0 =A0 =A0 tcd->biter =3D 1;
> + =A0 =A0 =A0 tcd->citer =3D 1;
> +
> + =A0 =A0 =A0 /* Place descriptor in prepared list */
> + =A0 =A0 =A0 spin_lock_irqsave(&mchan->lock, iflags);
> + =A0 =A0 =A0 list_add_tail(&mdesc->node, &mchan->prepared);
> + =A0 =A0 =A0 spin_unlock_irqrestore(&mchan->lock, iflags);
> +
> + =A0 =A0 =A0 return &mdesc->desc;
> +}
> +
> +static int __devinit mpc_dma_probe(struct of_device *op,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 const struct of_device_id *match)
> +{
> + =A0 =A0 =A0 struct device_node *dn =3D op->node;
> + =A0 =A0 =A0 struct device *dev =3D &op->dev;
> + =A0 =A0 =A0 struct dma_device *dma;
> + =A0 =A0 =A0 struct mpc_dma *mdma;
> + =A0 =A0 =A0 struct mpc_dma_chan *mchan;
> + =A0 =A0 =A0 struct resource res;
> + =A0 =A0 =A0 ulong regs_start, regs_size;
> + =A0 =A0 =A0 int retval, i;
> +
> + =A0 =A0 =A0 mdma =3D devm_kzalloc(dev, sizeof(struct mpc_dma), GFP_KERN=
EL);
> + =A0 =A0 =A0 if (!mdma) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Memory exhausted!\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENOMEM;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 mdma->irq =3D irq_of_parse_and_map(dn, 0);
> + =A0 =A0 =A0 if (mdma->irq =3D=3D NO_IRQ) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Error mapping IRQ!\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 retval =3D of_address_to_resource(dn, 0, &res);
> + =A0 =A0 =A0 if (retval) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Error parsing memory region!\=
n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return retval;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 regs_start =3D res.start;
> + =A0 =A0 =A0 regs_size =3D res.end - res.start + 1;
> +
> + =A0 =A0 =A0 if (!devm_request_mem_region(dev, regs_start, regs_size, DR=
V_NAME)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Error requesting memory regio=
n!\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EBUSY;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 mdma->regs =3D devm_ioremap(dev, regs_start, regs_size);
> + =A0 =A0 =A0 if (!mdma->regs) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Error mapping memory region!\=
n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -ENOMEM;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 mdma->tcd =3D (struct mpc_dma_tcd *)((u8 *)(mdma->regs)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 + MPC_DMA_TCD_OFFSET);
> +
> + =A0 =A0 =A0 retval =3D devm_request_irq(dev, mdma->irq, &mpc_dma_irq, 0=
, DRV_NAME,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mdma);
> + =A0 =A0 =A0 if (retval) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(dev, "Error requesting IRQ!\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 spin_lock_init(&mdma->error_status_lock);
> +
> + =A0 =A0 =A0 dma =3D &mdma->dma;
> + =A0 =A0 =A0 dma->dev =3D dev;
> + =A0 =A0 =A0 dma->chancnt =3D MPC_DMA_CHANNELS;
> + =A0 =A0 =A0 dma->device_alloc_chan_resources =3D mpc_dma_alloc_chan_res=
ources;
> + =A0 =A0 =A0 dma->device_free_chan_resources =3D mpc_dma_free_chan_resou=
rces;
> + =A0 =A0 =A0 dma->device_issue_pending =3D mpc_dma_issue_pending;
> + =A0 =A0 =A0 dma->device_is_tx_complete =3D mpc_dma_is_tx_complete;
> + =A0 =A0 =A0 dma->device_prep_dma_memcpy =3D mpc_dma_prep_memcpy;
> +
> + =A0 =A0 =A0 INIT_LIST_HEAD(&dma->channels);
> + =A0 =A0 =A0 dma_cap_set(DMA_MEMCPY, dma->cap_mask);
> +
> + =A0 =A0 =A0 for (i =3D 0; i < dma->chancnt; i++) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mchan =3D &mdma->channels[i];
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mchan->chan.device =3D dma;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mchan->chan.chan_id =3D i;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mchan->chan.cookie =3D 1;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 mchan->completed_cookie =3D mchan->chan.coo=
kie;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 INIT_LIST_HEAD(&mchan->free);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 INIT_LIST_HEAD(&mchan->prepared);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 INIT_LIST_HEAD(&mchan->queued);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 INIT_LIST_HEAD(&mchan->active);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 INIT_LIST_HEAD(&mchan->completed);
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_lock_init(&mchan->lock);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 list_add_tail(&mchan->chan.device_node, &dm=
a->channels);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 tasklet_init(&mdma->tasklet, mpc_dma_tasklet, (unsigned lon=
g)mdma);
> +
> + =A0 =A0 =A0 /*
> + =A0 =A0 =A0 =A0* Configure DMA Engine:
> + =A0 =A0 =A0 =A0* - Dynamic clock,
> + =A0 =A0 =A0 =A0* - Round-robin group arbitration,
> + =A0 =A0 =A0 =A0* - Round-robin channel arbitration.
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 out_be32(&mdma->regs->dmacr, MPC_DMA_DMACR_EDCG |
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 MPC_DMA_DMA=
CR_ERGA | MPC_DMA_DMACR_ERCA);
> +
> + =A0 =A0 =A0 /* Disable hardware DMA requests */
> + =A0 =A0 =A0 out_be32(&mdma->regs->dmaerqh, 0);
> + =A0 =A0 =A0 out_be32(&mdma->regs->dmaerql, 0);
> +
> + =A0 =A0 =A0 /* Disable error interrupts */
> + =A0 =A0 =A0 out_be32(&mdma->regs->dmaeeih, 0);
> + =A0 =A0 =A0 out_be32(&mdma->regs->dmaeeil, 0);
> +
> + =A0 =A0 =A0 /* Clear interrupts status */
> + =A0 =A0 =A0 out_be32(&mdma->regs->dmainth, 0xFFFFFFFF);
> + =A0 =A0 =A0 out_be32(&mdma->regs->dmaintl, 0xFFFFFFFF);
> + =A0 =A0 =A0 out_be32(&mdma->regs->dmaerrh, 0xFFFFFFFF);
> + =A0 =A0 =A0 out_be32(&mdma->regs->dmaerrl, 0xFFFFFFFF);
> +
> + =A0 =A0 =A0 /* Route interrupts to IPIC */
> + =A0 =A0 =A0 out_be32(&mdma->regs->dmaihsa, 0);
> + =A0 =A0 =A0 out_be32(&mdma->regs->dmailsa, 0);
> +
> + =A0 =A0 =A0 /* Register DMA engine */
> + =A0 =A0 =A0 dev_set_drvdata(dev, mdma);
> + =A0 =A0 =A0 retval =3D dma_async_device_register(dma);
> + =A0 =A0 =A0 if (retval) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 devm_free_irq(dev, mdma->irq, mdma);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 irq_dispose_mapping(mdma->irq);
> + =A0 =A0 =A0 }
> +
> + =A0 =A0 =A0 return retval;
> +}
> +
> +static int __devexit mpc_dma_remove(struct of_device *op)
> +{
> + =A0 =A0 =A0 struct device *dev =3D &op->dev;
> + =A0 =A0 =A0 struct mpc_dma *mdma =3D dev_get_drvdata(dev);
> +
> + =A0 =A0 =A0 dma_async_device_unregister(&mdma->dma);
> + =A0 =A0 =A0 devm_free_irq(dev, mdma->irq, mdma);
> + =A0 =A0 =A0 irq_dispose_mapping(mdma->irq);
> +
> + =A0 =A0 =A0 return 0;
> +}
> +
> +static struct of_device_id mpc_dma_match[] =3D {
> + =A0 =A0 =A0 { .compatible =3D "fsl,mpc5121-dma", },
> + =A0 =A0 =A0 {},
> +};
> +
> +static struct of_platform_driver mpc_dma_driver =3D {
> + =A0 =A0 =A0 .match_table =A0 =A0=3D mpc_dma_match,
> + =A0 =A0 =A0 .probe =A0 =A0 =A0 =A0 =A0=3D mpc_dma_probe,
> + =A0 =A0 =A0 .remove =A0 =A0 =A0 =A0 =3D __devexit_p(mpc_dma_remove),
> + =A0 =A0 =A0 .driver =A0 =A0 =A0 =A0 =3D {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .name =A0 =3D DRV_NAME,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 .owner =A0=3D THIS_MODULE,
> + =A0 =A0 =A0 },
> +};
> +
> +static int __init mpc_dma_init(void)
> +{
> + =A0 =A0 =A0 return of_register_platform_driver(&mpc_dma_driver);
> +}
> +module_init(mpc_dma_init);
> +
> +static void __exit mpc_dma_exit(void)
> +{
> + =A0 =A0 =A0 of_unregister_platform_driver(&mpc_dma_driver);
> +}
> +module_exit(mpc_dma_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Piotr Ziecik <kosmo@semihalf.com>");
> --
> 1.6.3.3
>
>
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* [PATCH] powerpc: Use lwarx hint bit in spinlocks
From: Anton Blanchard @ 2010-02-10 2:50 UTC (permalink / raw)
To: benh; +Cc: linuxppc-dev
Recent versions of the PowerPC architecture added a hint bit to the larx
instructions to differentiate between an atomic operation and a lock operation:
> 0 Other programs might attempt to modify the word in storage addressed by EA
> even if the subsequent Store Conditional succeeds.
>
> 1 Other programs will not attempt to modify the word in storage addressed by
> EA until the program that has acquired the lock performs a subsequent store
> releasing the lock.
To avoid a binutils dependency this patch create macros for the extended lwarx
format and uses it in the spinlock code. To test this change I used a simple
test case that acquires and releases a global pthread mutex:
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);
On a 32 core POWER6 running 32 test threads we spend almost all our time in
the futex spinlock code (as expected):
94.37% perf [kernel] [k] ._raw_spin_lock
|
|--99.95%-- ._raw_spin_lock
| |
| |--63.29%-- .futex_wake
| |
| |--36.64%-- .futex_wait_setup
which is a good test for this patch. The results (in lock/unlock operations
per second) are:
before: 1538203 ops/sec
after: 2189219 ops/sec
An improvement of 42%
Signed-off-by: Anton Blanchard <anton@samba.org>
---
We've had issues in the past with chips that do the wrong thing and don't
ignore instruction reserved bits. One option might be to limit this change to
64bit and verify on that limited set of CPUs.
Index: powerpc.git/arch/powerpc/include/asm/ppc-opcode.h
===================================================================
--- powerpc.git.orig/arch/powerpc/include/asm/ppc-opcode.h 2010-01-04 09:13:10.773027301 +1100
+++ powerpc.git/arch/powerpc/include/asm/ppc-opcode.h 2010-02-10 13:46:20.423071697 +1100
@@ -24,6 +24,7 @@
#define PPC_INST_ISEL_MASK 0xfc00003e
#define PPC_INST_LSWI 0x7c0004aa
#define PPC_INST_LSWX 0x7c00042a
+#define PPC_INST_LWARX 0x7c000029
#define PPC_INST_LWSYNC 0x7c2004ac
#define PPC_INST_LXVD2X 0x7c000698
#define PPC_INST_MCRXR 0x7c000400
@@ -55,15 +56,20 @@
#define __PPC_RA(a) (((a) & 0x1f) << 16)
#define __PPC_RB(b) (((b) & 0x1f) << 11)
#define __PPC_RS(s) (((s) & 0x1f) << 21)
+#define __PPC_RT(s) __PPC_RS(s)
#define __PPC_XS(s) ((((s) & 0x1f) << 21) | (((s) & 0x20) >> 5))
#define __PPC_T_TLB(t) (((t) & 0x3) << 21)
#define __PPC_WC(w) (((w) & 0x3) << 21)
+#define __PPC_EH(eh) (((eh) & 0x1) << 0)
/* Deal with instructions that older assemblers aren't aware of */
#define PPC_DCBAL(a, b) stringify_in_c(.long PPC_INST_DCBAL | \
__PPC_RA(a) | __PPC_RB(b))
#define PPC_DCBZL(a, b) stringify_in_c(.long PPC_INST_DCBZL | \
__PPC_RA(a) | __PPC_RB(b))
+#define PPC_LWARX(t, a, b, eh) stringify_in_c(.long PPC_INST_LWARX | \
+ __PPC_RT(t) | __PPC_RA(a) | \
+ __PPC_RB(b) | __PPC_EH(eh))
#define PPC_MSGSND(b) stringify_in_c(.long PPC_INST_MSGSND | \
__PPC_RB(b))
#define PPC_RFCI stringify_in_c(.long PPC_INST_RFCI)
Index: powerpc.git/arch/powerpc/include/asm/spinlock.h
===================================================================
--- powerpc.git.orig/arch/powerpc/include/asm/spinlock.h 2010-01-04 09:13:10.783027472 +1100
+++ powerpc.git/arch/powerpc/include/asm/spinlock.h 2010-02-10 13:46:20.433071870 +1100
@@ -27,6 +27,7 @@
#endif
#include <asm/asm-compat.h>
#include <asm/synch.h>
+#include <asm/ppc-opcode.h>
#define arch_spin_is_locked(x) ((x)->slock != 0)
@@ -60,7 +61,7 @@ static inline unsigned long __arch_spin_
token = LOCK_TOKEN;
__asm__ __volatile__(
-"1: lwarx %0,0,%2\n\
+"1: " PPC_LWARX(%0,0,%2,1) "\n\
cmpwi 0,%0,0\n\
bne- 2f\n\
stwcx. %1,0,%2\n\
@@ -186,7 +187,7 @@ static inline long __arch_read_trylock(a
long tmp;
__asm__ __volatile__(
-"1: lwarx %0,0,%1\n"
+"1: " PPC_LWARX(%0,0,%1,1) "\n"
__DO_SIGN_EXTEND
" addic. %0,%0,1\n\
ble- 2f\n"
@@ -211,7 +212,7 @@ static inline long __arch_write_trylock(
token = WRLOCK_TOKEN;
__asm__ __volatile__(
-"1: lwarx %0,0,%2\n\
+"1: " PPC_LWARX(%0,0,%2,1) "\n\
cmpwi 0,%0,0\n\
bne- 2f\n"
PPC405_ERR77(0,%1)
^ permalink raw reply
* Re: [PATCH] powerpc: Use lwarx hint bit in spinlocks
From: Josh Boyer @ 2010-02-10 3:19 UTC (permalink / raw)
To: Anton Blanchard; +Cc: linuxppc-dev
In-Reply-To: <20100210025010.GC3702@kryten>
On Wed, Feb 10, 2010 at 01:50:11PM +1100, Anton Blanchard wrote:
>
>Recent versions of the PowerPC architecture added a hint bit to the larx
>instructions to differentiate between an atomic operation and a lock operation:
>
>> 0 Other programs might attempt to modify the word in storage addressed by EA
>> even if the subsequent Store Conditional succeeds.
>>
>> 1 Other programs will not attempt to modify the word in storage addressed by
>> EA until the program that has acquired the lock performs a subsequent store
>> releasing the lock.
>
>To avoid a binutils dependency this patch create macros for the extended lwarx
>format and uses it in the spinlock code. To test this change I used a simple
>test case that acquires and releases a global pthread mutex:
>
> pthread_mutex_lock(&mutex);
> pthread_mutex_unlock(&mutex);
>
>On a 32 core POWER6 running 32 test threads we spend almost all our time in
>the futex spinlock code (as expected):
>
> 94.37% perf [kernel] [k] ._raw_spin_lock
> |
> |--99.95%-- ._raw_spin_lock
> | |
> | |--63.29%-- .futex_wake
> | |
> | |--36.64%-- .futex_wait_setup
>
>which is a good test for this patch. The results (in lock/unlock operations
>per second) are:
>
>before: 1538203 ops/sec
>after: 2189219 ops/sec
>
>An improvement of 42%
>
>Signed-off-by: Anton Blanchard <anton@samba.org>
>---
>
>We've had issues in the past with chips that do the wrong thing and don't
>ignore instruction reserved bits. One option might be to limit this change to
>64bit and verify on that limited set of CPUs.
In the off chance that someone actually does an SMP 44x, I think the hint bit
here would just be ignored (I could test possibly if we want to verify).
However, I thought the FSL parts didn't like toggling the reserved bits and
those already are SMP in some cases.
Kumar?
josh
^ permalink raw reply
* PPC: Possible bug in prom_parse.c:of_irq_map_raw?
From: John Williams @ 2010-02-10 3:21 UTC (permalink / raw)
To: Linux Kernel list, linuxppc-dev, Grant Likely, Michal Simek
Hi,
Perhaps this is my misunderstanding, but I'm looking at the bit of
code in of_irq_map_raw() that iterates the interrupt-map DTS node,
which I am seeing to behave strangely when handling the interrupt-map
property on a PCI bridge node.
Early in the function, we get the #address-cells value from the node -
in this case the PCI bridge, and store it in local var addrsize:
/* Look for this #address-cells. We have to implement the old linux
* trick of looking for the parent here as some device-trees rely on it
*/
old = of_node_get(ipar);
do {
tmp = of_get_property(old, "#address-cells", NULL);
tnode = of_get_parent(old);
of_node_put(old);
old = tnode;
} while(old && tmp == NULL);
of_node_put(old);
old = NULL;
addrsize = (tmp == NULL) ? 2 : *tmp;
DBG(" -> addrsize=%d\n", addrsize);
Later, once we've found the interrupt-map and are iterating over it
attempting to match on PCI device addresses, there is this little
fragment:
/* Get the interrupt parent */
if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
newpar = of_node_get(of_irq_dflt_pic);
else
newpar =
of_find_node_by_phandle((phandle)*imap);
imap++;
--imaplen;
/* Check if not found */
if (newpar == NULL) {
DBG(" -> imap parent not found !\n");
goto fail;
}
/* Get #interrupt-cells and #address-cells of new
* parent
*/
tmp = of_get_property(newpar, "#interrupt-cells", NULL);
if (tmp == NULL) {
DBG(" -> parent lacks #interrupt-cells !\n");
goto fail;
}
newintsize = *tmp;
tmp = of_get_property(newpar, "#address-cells", NULL);
newaddrsize = (tmp == NULL) ? 0 : *tmp;
Finally, later in the loop we update addrsize=newaddrsize
As I read this code, and the behaviour I'm seeing, is that if the
interrupt controller parent device doesn't have an #address-cells
entry, then this get_property will return fail, therefore setting
newaddrsize to zero. This then means that subsequent match attempts
when iterating the interrupt-map always fail, because the match length
(addrsize) is 0.
Maybe I'm missing something here, but shouldn't this last bit of code
instead be:
tmp = of_get_property(newpar, "#address-cells", NULL);
newaddrsize = (tmp == NULL) ? addrsize : *tmp;
^^^^^^^^^
?
In other words, if the parent node has an #address-cells value, then
use it, otherwise stick to the #address-cells value we picked up for
the actual node in question (ie the PCI bridge).
The way this is manifesting itself in the system I'm looking at is
that only the PCI device matching the first entry in the PCI bridge
interrupt-map property is correctly matching. For PCI devices that
require more than one pass through the interrupt-map loop, addrsize
goes to zero and no further match is possible.
I might be able to hack around this in the device-tree by setting
#address-cells=<3> in the interrupt controller node, but that doesn't
smell right either - it's only true for PCI devices for a start,
whereas this controller handles interrupts from many sources on the
32-bit PLB bus as well. I looked at the ML510 DTS in boot/dts and
it's not doing anything like this.
As a footnote, I'm actually doing this in MicroBlaze whose PCI
infrastructure is not yet in mainline, however we copied this bit of
code directly from PPC and the logic is the same.
Thanks,
John
--
John Williams
PetaLogix - Linux Solutions for a Reconfigurable World
w: www.petalogix.com p: +61-7-30090663 f: +61-7-30090663
^ permalink raw reply
* [git pull] Please pull powerpc.git merge branch
From: Benjamin Herrenschmidt @ 2010-02-10 3:52 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Andrew Morton, linuxppc-dev list, Linux Kernel list
Hi Linus !
Here's a fix for a nasty regression that went in this release cycle
and breaks 64K pages on HW that only does 4K such as 970's.
Cheers,
Ben.
The following changes since commit ac73fddfc523bf3c3525d16356b44527c44fae6d:
Linus Torvalds (1):
Merge branch 'for-linus' of git://neil.brown.name/md
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc.git merge
David Gibson (1):
powerpc: Fix address masking bug in hpte_need_flush()
arch/powerpc/mm/tlb_hash64.c | 12 +++++++++---
1 files changed, 9 insertions(+), 3 deletions(-)
^ permalink raw reply
* Re: [PATCH] Restrict initial stack space expansion to rlimit
From: KOSAKI Motohiro @ 2010-02-10 5:12 UTC (permalink / raw)
To: Helge Deller
Cc: Michael Neuling, linux-parisc, stable, aeb, Oleg Nesterov,
miltonm, James Morris, linuxppc-dev, Paul Mackerras,
Anton Blanchard, kosaki.motohiro, Serge Hallyn, linux-fsdevel,
Americo Wang, Andrew Morton, Linus Torvalds, Ingo Molnar,
linux-kernel, Alexander Viro
In-Reply-To: <4B71E13C.2050905@gmx.de>
> On 02/09/2010 10:51 PM, Michael Neuling wrote:
> >>> I'd still like someone with a CONFIG_STACK_GROWSUP arch to test/ACK it
> >>> as well.
> >>
> >> There's only one CONFIG_GROWSUP arch - parisc.
> >> Could someone please test it on parisc?
>
> I did.
>
> > How about doing:
> > 'ulimit -s 15; ls'
> > before and after the patch is applied. Before it's applied, 'ls' should
> > be killed. After the patch is applied, 'ls' should no longer be killed.
> >
> > I'm suggesting a stack limit of 15KB since it's small enough to trigger
> > 20*PAGE_SIZE. Also 15KB not a multiple of PAGE_SIZE, which is a trickier
> > case to handle correctly with this code.
> >
> > 4K pages on parisc should be fine to test with.
>
> Mikey, thanks for the suggested test plan.
>
> I'm not sure if your patch does it correct for parisc/stack-grows-up-case.
>
> I tested your patch on a 4k pages kernel:
> root@c3000:~# uname -a
> Linux c3000 2.6.33-rc7-32bit #221 Tue Feb 9 23:17:06 CET 2010 parisc GNU/Linux
>
> Without your patch:
> root@c3000:~# ulimit -s 15; ls
> Killed
> -> correct.
>
> With your patch:
> root@c3000:~# ulimit -s 15; ls
> Killed
> _or_:
> root@c3000:~# ulimit -s 15; ls
> Segmentation fault
> -> ??
>
> Any idea?
My x86_64 box also makes segmentation fault. I think "ulimit -s 15" is too small stack for ls.
"ulimit -s 27; ls " wroks perfectly fine.
^ permalink raw reply
* Re: [PATCH] Restrict initial stack space expansion to rlimit
From: Michael Neuling @ 2010-02-10 5:30 UTC (permalink / raw)
To: KOSAKI Motohiro
Cc: linux-parisc, stable, Helge Deller, aeb, Oleg Nesterov, miltonm,
James Morris, linuxppc-dev, Paul Mackerras, Alexander Viro,
Serge Hallyn, linux-fsdevel, Americo Wang, Andrew Morton,
Linus Torvalds, Ingo Molnar, linux-kernel, Anton Blanchard
In-Reply-To: <20100210141016.4D18.A69D9226@jp.fujitsu.com>
In message <20100210141016.4D18.A69D9226@jp.fujitsu.com> you wrote:
> > On 02/09/2010 10:51 PM, Michael Neuling wrote:
> > >>> I'd still like someone with a CONFIG_STACK_GROWSUP arch to test/ACK it
> > >>> as well.
> > >>
> > >> There's only one CONFIG_GROWSUP arch - parisc.
> > >> Could someone please test it on parisc?
> >
> > I did.
> >
> > > How about doing:
> > > 'ulimit -s 15; ls'
> > > before and after the patch is applied. Before it's applied, 'ls' should
> > > be killed. After the patch is applied, 'ls' should no longer be killed.
> > >
> > > I'm suggesting a stack limit of 15KB since it's small enough to trigger
> > > 20*PAGE_SIZE. Also 15KB not a multiple of PAGE_SIZE, which is a trickier
> > > case to handle correctly with this code.
> > >
> > > 4K pages on parisc should be fine to test with.
> >
> > Mikey, thanks for the suggested test plan.
> >
> > I'm not sure if your patch does it correct for parisc/stack-grows-up-case.
> >
> > I tested your patch on a 4k pages kernel:
> > root@c3000:~# uname -a
> > Linux c3000 2.6.33-rc7-32bit #221 Tue Feb 9 23:17:06 CET 2010 parisc GNU/Li
nux
> >
> > Without your patch:
> > root@c3000:~# ulimit -s 15; ls
> > Killed
> > -> correct.
> >
> > With your patch:
> > root@c3000:~# ulimit -s 15; ls
> > Killed
> > _or_:
> > root@c3000:~# ulimit -s 15; ls
> > Segmentation fault
> > -> ??
> >
> > Any idea?
>
> My x86_64 box also makes segmentation fault. I think "ulimit -s 15" is too sm
all stack for ls.
> "ulimit -s 27; ls " wroks perfectly fine.
Arrh. I asked Helge offline earlier to check what use to work on parisc
on 2.6.31.
I guess PPC has a nice clean non-bloated ABI :-D
Mikey
^ permalink raw reply
* Re: [PATCH] Restrict initial stack space expansion to rlimit
From: Michael Neuling @ 2010-02-10 5:31 UTC (permalink / raw)
To: KOSAKI Motohiro
Cc: linux-parisc, stable, Helge Deller, aeb, Oleg Nesterov, miltonm,
James Morris, linuxppc-dev, Paul Mackerras, Alexander Viro,
Serge Hallyn, linux-fsdevel, Americo Wang, Andrew Morton,
Linus Torvalds, Ingo Molnar, linux-kernel, Anton Blanchard
In-Reply-To: <20100210141016.4D18.A69D9226@jp.fujitsu.com>
In message <20100210141016.4D18.A69D9226@jp.fujitsu.com> you wrote:
> > On 02/09/2010 10:51 PM, Michael Neuling wrote:
> > >>> I'd still like someone with a CONFIG_STACK_GROWSUP arch to test/ACK it
> > >>> as well.
> > >>
> > >> There's only one CONFIG_GROWSUP arch - parisc.
> > >> Could someone please test it on parisc?
> >
> > I did.
> >
> > > How about doing:
> > > 'ulimit -s 15; ls'
> > > before and after the patch is applied. Before it's applied, 'ls' should
> > > be killed. After the patch is applied, 'ls' should no longer be killed.
> > >
> > > I'm suggesting a stack limit of 15KB since it's small enough to trigger
> > > 20*PAGE_SIZE. Also 15KB not a multiple of PAGE_SIZE, which is a trickier
> > > case to handle correctly with this code.
> > >
> > > 4K pages on parisc should be fine to test with.
> >
> > Mikey, thanks for the suggested test plan.
> >
> > I'm not sure if your patch does it correct for parisc/stack-grows-up-case.
> >
> > I tested your patch on a 4k pages kernel:
> > root@c3000:~# uname -a
> > Linux c3000 2.6.33-rc7-32bit #221 Tue Feb 9 23:17:06 CET 2010 parisc GNU/Li
nux
> >
> > Without your patch:
> > root@c3000:~# ulimit -s 15; ls
> > Killed
> > -> correct.
> >
> > With your patch:
> > root@c3000:~# ulimit -s 15; ls
> > Killed
> > _or_:
> > root@c3000:~# ulimit -s 15; ls
> > Segmentation fault
> > -> ??
> >
> > Any idea?
>
> My x86_64 box also makes segmentation fault. I think "ulimit -s 15" is too sm
all stack for ls.
> "ulimit -s 27; ls " wroks perfectly fine.
Arrh. I asked Helge offline earlier to check what use to work on parisc
on 2.6.31.
I guess PPC has a nice clean non-bloated ABI :-D
Mikey
^ permalink raw reply
* [PATCH] powerpc/44x: Update Glacier dts
From: Stefan Roese @ 2010-02-10 9:08 UTC (permalink / raw)
To: linuxppc-dev
Sync Glacier dts with latest Canyonlands version:
- Add l2 cache support
- Add NDFC support
- Add RTC support
- Add AD7414 hwmon support
- Change EMAC compatible node from emac4 to emac4sync and correct the
register size
- Add support for ISA holes on 4xx PCI/X/E
(as done in Benjamin Herrenschmidt's patch for Canyonlands)
- Add Crypto device node
Signed-off-by: Stefan Roese <sr@denx.de>
Cc: Josh Boyer <jwboyer@linux.vnet.ibm.com>
---
arch/powerpc/boot/dts/glacier.dts | 76 ++++++++++++++++++++++++++++++++----
1 files changed, 67 insertions(+), 9 deletions(-)
diff --git a/arch/powerpc/boot/dts/glacier.dts b/arch/powerpc/boot/dts/glacier.dts
index f6f6189..d62a4fb 100644
--- a/arch/powerpc/boot/dts/glacier.dts
+++ b/arch/powerpc/boot/dts/glacier.dts
@@ -1,7 +1,7 @@
/*
* Device Tree Source for AMCC Glacier (460GT)
*
- * Copyright 2008 DENX Software Engineering, Stefan Roese <sr@denx.de>
+ * Copyright 2008-2010 DENX Software Engineering, Stefan Roese <sr@denx.de>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without
@@ -42,6 +42,7 @@
d-cache-size = <32768>;
dcr-controller;
dcr-access-method = "native";
+ next-level-cache = <&L2C0>;
};
};
@@ -106,6 +107,16 @@
dcr-reg = <0x00c 0x002>;
};
+ L2C0: l2c {
+ compatible = "ibm,l2-cache-460gt", "ibm,l2-cache";
+ dcr-reg = <0x020 0x008 /* Internal SRAM DCR's */
+ 0x030 0x008>; /* L2 cache DCR's */
+ cache-line-size = <32>; /* 32 bytes */
+ cache-size = <262144>; /* L2, 256K */
+ interrupt-parent = <&UIC1>;
+ interrupts = <11 1>;
+ };
+
plb {
compatible = "ibm,plb-460gt", "ibm,plb4";
#address-cells = <2>;
@@ -118,6 +129,13 @@
dcr-reg = <0x010 0x002>;
};
+ CRYPTO: crypto@180000 {
+ compatible = "amcc,ppc460gt-crypto", "amcc,ppc4xx-crypto";
+ reg = <4 0x00180000 0x80400>;
+ interrupt-parent = <&UIC0>;
+ interrupts = <0x1d 0x4>;
+ };
+
MAL0: mcmal {
compatible = "ibm,mcmal-460gt", "ibm,mcmal2";
dcr-reg = <0x180 0x062>;
@@ -186,6 +204,29 @@
reg = <0x03fa0000 0x00060000>;
};
};
+
+ ndfc@3,0 {
+ compatible = "ibm,ndfc";
+ reg = <0x00000003 0x00000000 0x00002000>;
+ ccr = <0x00001000>;
+ bank-settings = <0x80002222>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ nand {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ partition@0 {
+ label = "u-boot";
+ reg = <0x00000000 0x00100000>;
+ };
+ partition@100000 {
+ label = "user";
+ reg = <0x00000000 0x03f00000>;
+ };
+ };
+ };
};
UART0: serial@ef600300 {
@@ -237,6 +278,20 @@
reg = <0xef600700 0x00000014>;
interrupt-parent = <&UIC0>;
interrupts = <0x2 0x4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ rtc@68 {
+ compatible = "stm,m41t80";
+ reg = <0x68>;
+ interrupt-parent = <&UIC2>;
+ interrupts = <0x19 0x8>;
+ };
+ sttm@48 {
+ compatible = "ad,ad7414";
+ reg = <0x48>;
+ interrupt-parent = <&UIC1>;
+ interrupts = <0x14 0x8>;
+ };
};
IIC1: i2c@ef600800 {
@@ -275,7 +330,7 @@
EMAC0: ethernet@ef600e00 {
device_type = "network";
- compatible = "ibm,emac-460gt", "ibm,emac4";
+ compatible = "ibm,emac-460gt", "ibm,emac4sync";
interrupt-parent = <&EMAC0>;
interrupts = <0x0 0x1>;
#interrupt-cells = <1>;
@@ -283,7 +338,7 @@
#size-cells = <0>;
interrupt-map = </*Status*/ 0x0 &UIC2 0x10 0x4
/*Wake*/ 0x1 &UIC2 0x14 0x4>;
- reg = <0xef600e00 0x00000074>;
+ reg = <0xef600e00 0x000000c4>;
local-mac-address = [000000000000]; /* Filled in by U-Boot */
mal-device = <&MAL0>;
mal-tx-channel = <0>;
@@ -305,7 +360,7 @@
EMAC1: ethernet@ef600f00 {
device_type = "network";
- compatible = "ibm,emac-460gt", "ibm,emac4";
+ compatible = "ibm,emac-460gt", "ibm,emac4sync";
interrupt-parent = <&EMAC1>;
interrupts = <0x0 0x1>;
#interrupt-cells = <1>;
@@ -313,7 +368,7 @@
#size-cells = <0>;
interrupt-map = </*Status*/ 0x0 &UIC2 0x11 0x4
/*Wake*/ 0x1 &UIC2 0x15 0x4>;
- reg = <0xef600f00 0x00000074>;
+ reg = <0xef600f00 0x000000c4>;
local-mac-address = [000000000000]; /* Filled in by U-Boot */
mal-device = <&MAL0>;
mal-tx-channel = <1>;
@@ -336,7 +391,7 @@
EMAC2: ethernet@ef601100 {
device_type = "network";
- compatible = "ibm,emac-460gt", "ibm,emac4";
+ compatible = "ibm,emac-460gt", "ibm,emac4sync";
interrupt-parent = <&EMAC2>;
interrupts = <0x0 0x1>;
#interrupt-cells = <1>;
@@ -344,7 +399,7 @@
#size-cells = <0>;
interrupt-map = </*Status*/ 0x0 &UIC2 0x12 0x4
/*Wake*/ 0x1 &UIC2 0x16 0x4>;
- reg = <0xef601100 0x00000074>;
+ reg = <0xef601100 0x000000c4>;
local-mac-address = [000000000000]; /* Filled in by U-Boot */
mal-device = <&MAL0>;
mal-tx-channel = <2>;
@@ -366,7 +421,7 @@
EMAC3: ethernet@ef601200 {
device_type = "network";
- compatible = "ibm,emac-460gt", "ibm,emac4";
+ compatible = "ibm,emac-460gt", "ibm,emac4sync";
interrupt-parent = <&EMAC3>;
interrupts = <0x0 0x1>;
#interrupt-cells = <1>;
@@ -374,7 +429,7 @@
#size-cells = <0>;
interrupt-map = </*Status*/ 0x0 &UIC2 0x13 0x4
/*Wake*/ 0x1 &UIC2 0x17 0x4>;
- reg = <0xef601200 0x00000074>;
+ reg = <0xef601200 0x000000c4>;
local-mac-address = [000000000000]; /* Filled in by U-Boot */
mal-device = <&MAL0>;
mal-tx-channel = <3>;
@@ -414,6 +469,7 @@
* later cannot be changed
*/
ranges = <0x02000000 0x00000000 0x80000000 0x0000000d 0x80000000 0x00000000 0x80000000
+ 0x02000000 0x00000000 0x00000000 0x0000000c 0x0ee00000 0x00000000 0x00100000
0x01000000 0x00000000 0x00000000 0x0000000c 0x08000000 0x00000000 0x00010000>;
/* Inbound 2GB range starting at 0 */
@@ -444,6 +500,7 @@
* later cannot be changed
*/
ranges = <0x02000000 0x00000000 0x80000000 0x0000000e 0x00000000 0x00000000 0x80000000
+ 0x02000000 0x00000000 0x00000000 0x0000000f 0x00000000 0x00000000 0x00100000
0x01000000 0x00000000 0x00000000 0x0000000f 0x80000000 0x00000000 0x00010000>;
/* Inbound 2GB range starting at 0 */
@@ -485,6 +542,7 @@
* later cannot be changed
*/
ranges = <0x02000000 0x00000000 0x80000000 0x0000000e 0x80000000 0x00000000 0x80000000
+ 0x02000000 0x00000000 0x00000000 0x0000000f 0x00100000 0x00000000 0x00100000
0x01000000 0x00000000 0x00000000 0x0000000f 0x80010000 0x00000000 0x00010000>;
/* Inbound 2GB range starting at 0 */
--
1.6.6.1
^ permalink raw reply related
* [PATCH] powerpc/44x: Fix L2-cache support for 460GT
From: Stefan Roese @ 2010-02-10 9:08 UTC (permalink / raw)
To: linuxppc-dev
Also set L2C_CFG_RDBW on 460GT platforms and not only on 460EX.
Signed-off-by: Stefan Roese <sr@denx.de>
Cc: Josh Boyer <jwboyer@linux.vnet.ibm.com>
---
arch/powerpc/sysdev/ppc4xx_soc.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/sysdev/ppc4xx_soc.c b/arch/powerpc/sysdev/ppc4xx_soc.c
index 5b32adc..5c01435 100644
--- a/arch/powerpc/sysdev/ppc4xx_soc.c
+++ b/arch/powerpc/sysdev/ppc4xx_soc.c
@@ -174,7 +174,8 @@ static int __init ppc4xx_l2c_probe(void)
| L2C_CFG_CPIM | L2C_CFG_TPIM | L2C_CFG_LIM | L2C_CFG_SMCM;
/* Check for 460EX/GT special handling */
- if (of_device_is_compatible(np, "ibm,l2-cache-460ex"))
+ if (of_device_is_compatible(np, "ibm,l2-cache-460ex") ||
+ of_device_is_compatible(np, "ibm,l2-cache-460gt"))
r |= L2C_CFG_RDBW;
mtdcr(dcrbase_l2c + DCRN_L2C0_CFG, r);
--
1.6.6.1
^ permalink raw reply related
* [PATCH] powerpc/44x: Update Arches dts
From: Stefan Roese @ 2010-02-10 9:08 UTC (permalink / raw)
To: linuxppc-dev
Sync Arches dts with latest Canyonlands version:
- Add 16k FIFO size to supported EMAC nodes
- Add next-level-cache property
- Add Crypto device node
Signed-off-by: Stefan Roese <sr@denx.de>
Cc: Josh Boyer <jwboyer@linux.vnet.ibm.com>
---
arch/powerpc/boot/dts/arches.dts | 12 ++++++++++++
1 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/boot/dts/arches.dts b/arch/powerpc/boot/dts/arches.dts
index 414ef8b..30f4120 100644
--- a/arch/powerpc/boot/dts/arches.dts
+++ b/arch/powerpc/boot/dts/arches.dts
@@ -60,6 +60,7 @@
d-cache-size = <32768>;
dcr-controller;
dcr-access-method = "native";
+ next-level-cache = <&L2C0>;
};
};
@@ -146,6 +147,13 @@
dcr-reg = <0x010 0x002>;
};
+ CRYPTO: crypto@180000 {
+ compatible = "amcc,ppc460gt-crypto", "amcc,ppc4xx-crypto";
+ reg = <4 0x00180000 0x80400>;
+ interrupt-parent = <&UIC0>;
+ interrupts = <0x1d 0x4>;
+ };
+
MAL0: mcmal {
compatible = "ibm,mcmal-460gt", "ibm,mcmal2";
dcr-reg = <0x180 0x062>;
@@ -274,6 +282,7 @@
max-frame-size = <9000>;
rx-fifo-size = <4096>;
tx-fifo-size = <2048>;
+ rx-fifo-size-gige = <16384>;
phy-mode = "sgmii";
phy-map = <0xffffffff>;
gpcs-address = <0x0000000a>;
@@ -302,6 +311,7 @@
max-frame-size = <9000>;
rx-fifo-size = <4096>;
tx-fifo-size = <2048>;
+ rx-fifo-size-gige = <16384>;
phy-mode = "sgmii";
phy-map = <0x00000000>;
gpcs-address = <0x0000000b>;
@@ -331,6 +341,8 @@
max-frame-size = <9000>;
rx-fifo-size = <4096>;
tx-fifo-size = <2048>;
+ rx-fifo-size-gige = <16384>;
+ tx-fifo-size-gige = <16384>; /* emac2&3 only */
phy-mode = "sgmii";
phy-map = <0x00000001>;
gpcs-address = <0x0000000C>;
--
1.6.6.1
^ permalink raw reply related
* Re: [net-next-2.6 PATCH 2/3] fs_enet: Add support for MPC512x to fs_enet driver
From: Wolfgang Grandegger @ 2010-02-10 9:15 UTC (permalink / raw)
To: David Miller; +Cc: wd, dzu, netdev, linuxppc-dev, agust, kosmo
In-Reply-To: <20100209.121356.216640014.davem@davemloft.net>
Hi David,
David Miller wrote:
> From: Anatolij Gustschin <agust@denx.de>
> Date: Tue, 9 Feb 2010 15:23:17 +0100
>
>> In my understanding, in the ESP scsi driver the set of defines for
>> the register offsets is common for all chip drivers. The chip driver
>> methods for register access translate the offsets because the
>> registers on some chips are at different intervals (4-byte, 1-byte,
>> 16-byte for mac_esp.c). But the register order is the same for
>> different chips.
>>
>> In our case non only the register order is not the same for 8xx
>> FEC and 5121 FEC, but there are also other differences, different
>> reserved areas between several registers, some registers are
>> available only on 8xx and some only on 5121.
>
> That only means you would need to use a table based register address
> translation scheme, rather than a simple calculation. Something
> like:
>
> static unsigned int chip_xxx_table[] =
> {
> [GENERIC_REG_FOO] = CHIP_XXX_FOO,
> ...
> };
>
> static u32 chip_xxx_read_reg(struct chip *p, unsigned int reg)
> {
> unsigned int reg_off = chip_xxx_table[reg];
>
> return readl(p->regs + reg_off);
> }
>
> And this table can have special tokens in entries for
> registers which do not exist on a chip, so you can trap
> attempted access to them in these read/write handlers.
Yes, that could be done, but to honest, I do not see any improvement in
respect to the previous patch where the register offset were defined via
pointers within a structure.
> Please stop looking for excuses to fork this driver, a
> unified driver I think can be done cleanly.
Other people suggested to fork the driver because it's getting too ugly.
Wolfgang.
^ permalink raw reply
* [PATCH v6 0/4] i2c-mpc: add support for the Freescale MPC512x and other fixes
From: Wolfgang Grandegger @ 2010-02-10 10:05 UTC (permalink / raw)
To: Linux-i2c
Cc: Devicetree-discuss, Linuxppc-dev, Wolfgang Grandegger, Ben Dooks
From: Wolfgang Grandegger <wg@denx.de>
This patch series adds support for the MPC512x from Freescale to the
i2c-mpc driver. At that occasion, issues with __devinit[data] have
been fixed and the doc of the FSL I2C dts bindings updated. It has
been tested on a MPC5121ADS, TQM5200 and TQM8560 board
Changes since v1:
- use macro MPC_I2C_CLOCK_PRESERVE/SAFE for the special clock settings.
- document the special DTS node "fsl,mpc5121-i2c-ctrl".
- update and correct the Kconfig help.
- some other minor fixes as suggested by Wolfram.
Changes since v2:
- use __init[data] instead of __devinit[data] for this driver.
Changes since v3:
- switch back to __devinit[data] as pointed out by Ben.
Changes since v4:
- check MPC_I2C_CLOCK_SAFE instead of "!clock" as suggested by Wolfram.
- update MODULE_DESCRIPTION().
Changes since v5 (suggested by Grant Likely):
- various correctings for labling initialization functions and data
(this is tricky because section mismatches are not always obvious).
- add a separate patch for renaming the setclock into setup functions.
- correct the doc of the I2C bindings, e.g. don't mention the legacy
clock setting and remove obsolte parts.
Wolfgang
Wolfgang Grandegger (4):
i2c-mpc: use __devinit[data] for initialization functions and data
i2c-mpc: rename "setclock" initialization functions to "setup"
i2c-mpc: add support for the MPC512x processors from Freescale
powerpc: doc/dts-bindings: update doc of FSL I2C bindings
Documentation/powerpc/dts-bindings/fsl/i2c.txt | 30 +++-
drivers/i2c/busses/Kconfig | 7 +-
drivers/i2c/busses/i2c-mpc.c | 194 +++++++++++++++---------
3 files changed, 146 insertions(+), 85 deletions(-)
^ permalink raw reply
* [PATCH v6 1/4] i2c-mpc: use __devinit[data] for initialization functions and data
From: Wolfgang Grandegger @ 2010-02-10 10:05 UTC (permalink / raw)
To: Linux-i2c
Cc: Devicetree-discuss, Linuxppc-dev, Wolfgang Grandegger, Ben Dooks
In-Reply-To: <1265796330-15976-1-git-send-email-wg@grandegger.com>
From: Wolfgang Grandegger <wg@denx.de>
"__devinit[data]" has not yet been used for all initialization functions
and data. To avoid truncating lines, the struct "mpc_i2c_match_data" has
been renamed to "mpc_i2c_data", which is even the better name.
Signed-off-by: Wolfgang Grandegger <wg@denx.de>
Tested-by: Wolfram Sang <w.sang@pengutronix.de>
---
drivers/i2c/busses/i2c-mpc.c | 99 +++++++++++++++++++----------------------
1 files changed, 46 insertions(+), 53 deletions(-)
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index f627001..ba4e7af 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -66,7 +66,7 @@ struct mpc_i2c_divider {
u16 fdr; /* including dfsrr */
};
-struct mpc_i2c_match_data {
+struct mpc_i2c_data {
void (*setclock)(struct device_node *node,
struct mpc_i2c *i2c,
u32 clock, u32 prescaler);
@@ -165,7 +165,7 @@ static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
}
#ifdef CONFIG_PPC_MPC52xx
-static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] = {
+static struct mpc_i2c_divider mpc_i2c_dividers_52xx[] __devinitdata = {
{20, 0x20}, {22, 0x21}, {24, 0x22}, {26, 0x23},
{28, 0x24}, {30, 0x01}, {32, 0x25}, {34, 0x02},
{36, 0x26}, {40, 0x27}, {44, 0x04}, {48, 0x28},
@@ -186,7 +186,8 @@ static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] = {
{10240, 0x9d}, {12288, 0x9e}, {15360, 0x9f}
};
-int mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock, int prescaler)
+static int __devinit mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock,
+ int prescaler)
{
const struct mpc_i2c_divider *div = NULL;
unsigned int pvr = mfspr(SPRN_PVR);
@@ -215,9 +216,9 @@ int mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock, int prescaler)
return div ? (int)div->fdr : -EINVAL;
}
-static void mpc_i2c_setclock_52xx(struct device_node *node,
- struct mpc_i2c *i2c,
- u32 clock, u32 prescaler)
+static void __devinit mpc_i2c_setclock_52xx(struct device_node *node,
+ struct mpc_i2c *i2c,
+ u32 clock, u32 prescaler)
{
int ret, fdr;
@@ -230,15 +231,15 @@ static void mpc_i2c_setclock_52xx(struct device_node *node,
dev_info(i2c->dev, "clock %d Hz (fdr=%d)\n", clock, fdr);
}
#else /* !CONFIG_PPC_MPC52xx */
-static void mpc_i2c_setclock_52xx(struct device_node *node,
- struct mpc_i2c *i2c,
- u32 clock, u32 prescaler)
+static void __devinit mpc_i2c_setclock_52xx(struct device_node *node,
+ struct mpc_i2c *i2c,
+ u32 clock, u32 prescaler)
{
}
#endif /* CONFIG_PPC_MPC52xx*/
#ifdef CONFIG_FSL_SOC
-static const struct mpc_i2c_divider mpc_i2c_dividers_8xxx[] = {
+static struct mpc_i2c_divider mpc_i2c_dividers_8xxx[] __devinitdata = {
{160, 0x0120}, {192, 0x0121}, {224, 0x0122}, {256, 0x0123},
{288, 0x0100}, {320, 0x0101}, {352, 0x0601}, {384, 0x0102},
{416, 0x0602}, {448, 0x0126}, {480, 0x0103}, {512, 0x0127},
@@ -258,7 +259,7 @@ static const struct mpc_i2c_divider mpc_i2c_dividers_8xxx[] = {
{49152, 0x011e}, {61440, 0x011f}
};
-u32 mpc_i2c_get_sec_cfg_8xxx(void)
+static u32 __devinit mpc_i2c_get_sec_cfg_8xxx(void)
{
struct device_node *node = NULL;
u32 __iomem *reg;
@@ -287,7 +288,8 @@ u32 mpc_i2c_get_sec_cfg_8xxx(void)
return val;
}
-int mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock, u32 prescaler)
+static int __devinit mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock,
+ u32 prescaler)
{
const struct mpc_i2c_divider *div = NULL;
u32 divider;
@@ -320,9 +322,9 @@ int mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock, u32 prescaler)
return div ? (int)div->fdr : -EINVAL;
}
-static void mpc_i2c_setclock_8xxx(struct device_node *node,
- struct mpc_i2c *i2c,
- u32 clock, u32 prescaler)
+static void __devinit mpc_i2c_setclock_8xxx(struct device_node *node,
+ struct mpc_i2c *i2c,
+ u32 clock, u32 prescaler)
{
int ret, fdr;
@@ -338,9 +340,9 @@ static void mpc_i2c_setclock_8xxx(struct device_node *node,
}
#else /* !CONFIG_FSL_SOC */
-static void mpc_i2c_setclock_8xxx(struct device_node *node,
- struct mpc_i2c *i2c,
- u32 clock, u32 prescaler)
+static void __devinit mpc_i2c_setclock_8xxx(struct device_node *node,
+ struct mpc_i2c *i2c,
+ u32 clock, u32 prescaler)
{
}
#endif /* CONFIG_FSL_SOC */
@@ -529,8 +531,8 @@ static int __devinit fsl_i2c_probe(struct of_device *op,
clock = *prop;
if (match->data) {
- struct mpc_i2c_match_data *data =
- (struct mpc_i2c_match_data *)match->data;
+ struct mpc_i2c_data *data =
+ (struct mpc_i2c_data *)match->data;
data->setclock(op->node, i2c, clock, data->prescaler);
} else {
/* Backwards compatibility */
@@ -582,44 +584,35 @@ static int __devexit fsl_i2c_remove(struct of_device *op)
return 0;
};
+static struct mpc_i2c_data mpc_i2c_data_52xx __devinitdata = {
+ .setup = mpc_i2c_setup_52xx,
+};
+
+static struct mpc_i2c_data mpc_i2c_data_8313 __devinitdata = {
+ .setup = mpc_i2c_setup_8xxx,
+};
+
+static struct mpc_i2c_data mpc_i2c_data_8543 __devinitdata = {
+ .setup = mpc_i2c_setup_8xxx,
+ .prescaler = 2,
+};
+
+static struct mpc_i2c_data mpc_i2c_data_8544 __devinitdata = {
+ .setup = mpc_i2c_setup_8xxx,
+ .prescaler = 3,
+};
+
static const struct of_device_id mpc_i2c_of_match[] = {
- {.compatible = "mpc5200-i2c",
- .data = &(struct mpc_i2c_match_data) {
- .setclock = mpc_i2c_setclock_52xx,
- },
- },
- {.compatible = "fsl,mpc5200b-i2c",
- .data = &(struct mpc_i2c_match_data) {
- .setclock = mpc_i2c_setclock_52xx,
- },
- },
- {.compatible = "fsl,mpc5200-i2c",
- .data = &(struct mpc_i2c_match_data) {
- .setclock = mpc_i2c_setclock_52xx,
- },
- },
- {.compatible = "fsl,mpc8313-i2c",
- .data = &(struct mpc_i2c_match_data) {
- .setclock = mpc_i2c_setclock_8xxx,
- },
- },
- {.compatible = "fsl,mpc8543-i2c",
- .data = &(struct mpc_i2c_match_data) {
- .setclock = mpc_i2c_setclock_8xxx,
- .prescaler = 2,
- },
- },
- {.compatible = "fsl,mpc8544-i2c",
- .data = &(struct mpc_i2c_match_data) {
- .setclock = mpc_i2c_setclock_8xxx,
- .prescaler = 3,
- },
+ {.compatible = "mpc5200-i2c", .data = &mpc_i2c_data_52xx, },
+ {.compatible = "fsl,mpc5200b-i2c", .data = &mpc_i2c_data_52xx, },
+ {.compatible = "fsl,mpc5200-i2c", .data = &mpc_i2c_data_52xx, },
+ {.compatible = "fsl,mpc8313-i2c", .data = &mpc_i2c_data_8313, },
+ {.compatible = "fsl,mpc8543-i2c", .data = &mpc_i2c_data_8543, },
+ {.compatible = "fsl,mpc8544-i2c", .data = &mpc_i2c_data_8544, },
/* Backward compatibility */
- },
{.compatible = "fsl-i2c", },
{},
};
-
MODULE_DEVICE_TABLE(of, mpc_i2c_of_match);
--
1.6.2.5
^ permalink raw reply related
* [PATCH v6 2/4] i2c-mpc: rename "setclock" initialization functions to "setup"
From: Wolfgang Grandegger @ 2010-02-10 10:05 UTC (permalink / raw)
To: Linux-i2c
Cc: Devicetree-discuss, Linuxppc-dev, Wolfgang Grandegger, Ben Dooks
In-Reply-To: <1265796330-15976-2-git-send-email-wg@grandegger.com>
From: Wolfgang Grandegger <wg@denx.de>
To prepare support for the MPC512x processors from Freescale the
"setclock" initialization functions have been renamed to "setup"
because I2C interrupts must be enabled for the MPC512x by this
function as well.
Signed-off-by: Wolfgang Grandegger <wg@denx.de>
---
drivers/i2c/busses/i2c-mpc.c | 34 ++++++++++++++++------------------
1 files changed, 16 insertions(+), 18 deletions(-)
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index ba4e7af..51d5280 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -67,9 +67,8 @@ struct mpc_i2c_divider {
};
struct mpc_i2c_data {
- void (*setclock)(struct device_node *node,
- struct mpc_i2c *i2c,
- u32 clock, u32 prescaler);
+ void (*setup)(struct device_node *node, struct mpc_i2c *i2c,
+ u32 clock, u32 prescaler);
u32 prescaler;
};
@@ -216,9 +215,9 @@ static int __devinit mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock,
return div ? (int)div->fdr : -EINVAL;
}
-static void __devinit mpc_i2c_setclock_52xx(struct device_node *node,
- struct mpc_i2c *i2c,
- u32 clock, u32 prescaler)
+static void __devinit mpc_i2c_setup_52xx(struct device_node *node,
+ struct mpc_i2c *i2c,
+ u32 clock, u32 prescaler)
{
int ret, fdr;
@@ -231,9 +230,9 @@ static void __devinit mpc_i2c_setclock_52xx(struct device_node *node,
dev_info(i2c->dev, "clock %d Hz (fdr=%d)\n", clock, fdr);
}
#else /* !CONFIG_PPC_MPC52xx */
-static void __devinit mpc_i2c_setclock_52xx(struct device_node *node,
- struct mpc_i2c *i2c,
- u32 clock, u32 prescaler)
+static void __devinit mpc_i2c_setup_52xx(struct device_node *node,
+ struct mpc_i2c *i2c,
+ u32 clock, u32 prescaler)
{
}
#endif /* CONFIG_PPC_MPC52xx*/
@@ -322,9 +321,9 @@ static int __devinit mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock,
return div ? (int)div->fdr : -EINVAL;
}
-static void __devinit mpc_i2c_setclock_8xxx(struct device_node *node,
- struct mpc_i2c *i2c,
- u32 clock, u32 prescaler)
+static void __devinit mpc_i2c_setup_8xxx(struct device_node *node,
+ struct mpc_i2c *i2c,
+ u32 clock, u32 prescaler)
{
int ret, fdr;
@@ -340,9 +339,9 @@ static void __devinit mpc_i2c_setclock_8xxx(struct device_node *node,
}
#else /* !CONFIG_FSL_SOC */
-static void __devinit mpc_i2c_setclock_8xxx(struct device_node *node,
- struct mpc_i2c *i2c,
- u32 clock, u32 prescaler)
+static void __devinit mpc_i2c_setup_8xxx(struct device_node *node,
+ struct mpc_i2c *i2c,
+ u32 clock, u32 prescaler)
{
}
#endif /* CONFIG_FSL_SOC */
@@ -533,12 +532,11 @@ static int __devinit fsl_i2c_probe(struct of_device *op,
if (match->data) {
struct mpc_i2c_data *data =
(struct mpc_i2c_data *)match->data;
- data->setclock(op->node, i2c, clock, data->prescaler);
+ data->setup(op->node, i2c, clock, data->prescaler);
} else {
/* Backwards compatibility */
if (of_get_property(op->node, "dfsrr", NULL))
- mpc_i2c_setclock_8xxx(op->node, i2c,
- clock, 0);
+ mpc_i2c_setup_8xxx(op->node, i2c, clock, 0);
}
}
--
1.6.2.5
^ permalink raw reply related
* [PATCH v6 3/4] i2c-mpc: add support for the MPC512x processors from Freescale
From: Wolfgang Grandegger @ 2010-02-10 10:05 UTC (permalink / raw)
To: Linux-i2c
Cc: Devicetree-discuss, Linuxppc-dev, Wolfgang Grandegger, Ben Dooks
In-Reply-To: <1265796330-15976-3-git-send-email-wg@grandegger.com>
From: Wolfgang Grandegger <wg@denx.de>
As I2C interrupts must be enabled for the MPC512x by the setup function
as well, "fsl,preserve-clocking" is handled in a slighly different way.
Also, the old settings are now reported calling dev_dbg(). For the
MPC512x the clock setup function of the MPC52xx can be re-used.
Furthermore, the Kconfig help has been updated and corrected.
Signed-off-by: Wolfgang Grandegger <wg@denx.de>
Reviewed-by: Wolfram Sang <w.sang@pengutronix.de>
---
drivers/i2c/busses/Kconfig | 7 +--
drivers/i2c/busses/i2c-mpc.c | 93 +++++++++++++++++++++++++++++++++--------
2 files changed, 78 insertions(+), 22 deletions(-)
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 5f318ce..5477e41 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -418,13 +418,12 @@ config I2C_IXP2000
instead.
config I2C_MPC
- tristate "MPC107/824x/85xx/52xx/86xx"
+ tristate "MPC107/824x/85xx/512x/52xx/83xx/86xx"
depends on PPC32
help
If you say yes to this option, support will be included for the
- built-in I2C interface on the MPC107/Tsi107/MPC8240/MPC8245 and
- MPC85xx/MPC8641 family processors. The driver may also work on 52xx
- family processors, though interrupts are known not to work.
+ built-in I2C interface on the MPC107, Tsi107, MPC512x, MPC52xx,
+ MPC8240, MPC8245, MPC83xx, MPC85xx and MPC8641 family processors.
This driver can also be built as a module. If so, the module
will be called i2c-mpc.
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index 51d5280..106bf90 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -31,6 +31,9 @@
#define DRV_NAME "mpc-i2c"
+#define MPC_I2C_CLOCK_LEGACY 0
+#define MPC_I2C_CLOCK_PRESERVE (~0U)
+
#define MPC_I2C_FDR 0x04
#define MPC_I2C_CR 0x08
#define MPC_I2C_SR 0x0c
@@ -163,7 +166,7 @@ static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
return 0;
}
-#ifdef CONFIG_PPC_MPC52xx
+#if defined(CONFIG_PPC_MPC52xx) || defined(CONFIG_PPC_MPC512x)
static struct mpc_i2c_divider mpc_i2c_dividers_52xx[] __devinitdata = {
{20, 0x20}, {22, 0x21}, {24, 0x22}, {26, 0x23},
{28, 0x24}, {30, 0x01}, {32, 0x25}, {34, 0x02},
@@ -193,7 +196,7 @@ static int __devinit mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock,
u32 divider;
int i;
- if (!clock)
+ if (clock == MPC_I2C_CLOCK_LEGACY)
return -EINVAL;
/* Determine divider value */
@@ -221,6 +224,12 @@ static void __devinit mpc_i2c_setup_52xx(struct device_node *node,
{
int ret, fdr;
+ if (clock == MPC_I2C_CLOCK_PRESERVE) {
+ dev_dbg(i2c->dev, "using fdr %d\n",
+ readb(i2c->base + MPC_I2C_FDR));
+ return;
+ }
+
ret = mpc_i2c_get_fdr_52xx(node, clock, prescaler);
fdr = (ret >= 0) ? ret : 0x3f; /* backward compatibility */
@@ -229,13 +238,49 @@ static void __devinit mpc_i2c_setup_52xx(struct device_node *node,
if (ret >= 0)
dev_info(i2c->dev, "clock %d Hz (fdr=%d)\n", clock, fdr);
}
-#else /* !CONFIG_PPC_MPC52xx */
+#else /* !(CONFIG_PPC_MPC52xx || CONFIG_PPC_MPC512x) */
static void __devinit mpc_i2c_setup_52xx(struct device_node *node,
struct mpc_i2c *i2c,
u32 clock, u32 prescaler)
{
}
-#endif /* CONFIG_PPC_MPC52xx*/
+#endif /* CONFIG_PPC_MPC52xx || CONFIG_PPC_MPC512x */
+
+#ifdef CONFIG_PPC_MPC512x
+static void __devinit mpc_i2c_setup_512x(struct device_node *node,
+ struct mpc_i2c *i2c,
+ u32 clock, u32 prescaler)
+{
+ struct device_node *node_ctrl;
+ void __iomem *ctrl;
+ const u32 *pval;
+ u32 idx;
+
+ /* Enable I2C interrupts for mpc5121 */
+ node_ctrl = of_find_compatible_node(NULL, NULL,
+ "fsl,mpc5121-i2c-ctrl");
+ if (node_ctrl) {
+ ctrl = of_iomap(node_ctrl, 0);
+ if (ctrl) {
+ /* Interrupt enable bits for i2c-0/1/2: bit 24/26/28 */
+ pval = of_get_property(node, "reg", NULL);
+ idx = (*pval & 0xff) / 0x20;
+ setbits32(ctrl, 1 << (24 + idx * 2));
+ iounmap(ctrl);
+ }
+ of_node_put(node_ctrl);
+ }
+
+ /* The clock setup for the 52xx works also fine for the 512x */
+ mpc_i2c_setup_52xx(node, i2c, clock, prescaler);
+}
+#else /* CONFIG_PPC_MPC512x */
+static void __devinit mpc_i2c_setup_512x(struct device_node *node,
+ struct mpc_i2c *i2c,
+ u32 clock, u32 prescaler)
+{
+}
+#endif /* CONFIG_PPC_MPC512x */
#ifdef CONFIG_FSL_SOC
static struct mpc_i2c_divider mpc_i2c_dividers_8xxx[] __devinitdata = {
@@ -294,7 +339,7 @@ static int __devinit mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock,
u32 divider;
int i;
- if (!clock)
+ if (clock == MPC_I2C_CLOCK_LEGACY)
return -EINVAL;
/* Determine proper divider value */
@@ -327,6 +372,13 @@ static void __devinit mpc_i2c_setup_8xxx(struct device_node *node,
{
int ret, fdr;
+ if (clock == MPC_I2C_CLOCK_PRESERVE) {
+ dev_dbg(i2c->dev, "using dfsrr %d, fdr %d\n",
+ readb(i2c->base + MPC_I2C_DFSRR),
+ readb(i2c->base + MPC_I2C_FDR));
+ return;
+ }
+
ret = mpc_i2c_get_fdr_8xxx(node, clock, prescaler);
fdr = (ret >= 0) ? ret : 0x1031; /* backward compatibility */
@@ -495,7 +547,7 @@ static int __devinit fsl_i2c_probe(struct of_device *op,
{
struct mpc_i2c *i2c;
const u32 *prop;
- u32 clock = 0;
+ u32 clock = MPC_I2C_CLOCK_LEGACY;
int result = 0;
int plen;
@@ -524,20 +576,21 @@ static int __devinit fsl_i2c_probe(struct of_device *op,
}
}
- if (!of_get_property(op->node, "fsl,preserve-clocking", NULL)) {
+ if (of_get_property(op->node, "fsl,preserve-clocking", NULL)) {
+ clock = MPC_I2C_CLOCK_PRESERVE;
+ } else {
prop = of_get_property(op->node, "clock-frequency", &plen);
if (prop && plen == sizeof(u32))
clock = *prop;
+ }
- if (match->data) {
- struct mpc_i2c_data *data =
- (struct mpc_i2c_data *)match->data;
- data->setup(op->node, i2c, clock, data->prescaler);
- } else {
- /* Backwards compatibility */
- if (of_get_property(op->node, "dfsrr", NULL))
- mpc_i2c_setup_8xxx(op->node, i2c, clock, 0);
- }
+ if (match->data) {
+ struct mpc_i2c_data *data = match->data;
+ data->setup(op->node, i2c, clock, data->prescaler);
+ } else {
+ /* Backwards compatibility */
+ if (of_get_property(op->node, "dfsrr", NULL))
+ mpc_i2c_setup_8xxx(op->node, i2c, clock, 0);
}
dev_set_drvdata(&op->dev, i2c);
@@ -582,6 +635,10 @@ static int __devexit fsl_i2c_remove(struct of_device *op)
return 0;
};
+static struct mpc_i2c_data mpc_i2c_data_512x __devinitdata = {
+ .setup = mpc_i2c_setup_512x,
+};
+
static struct mpc_i2c_data mpc_i2c_data_52xx __devinitdata = {
.setup = mpc_i2c_setup_52xx,
};
@@ -604,6 +661,7 @@ static const struct of_device_id mpc_i2c_of_match[] = {
{.compatible = "mpc5200-i2c", .data = &mpc_i2c_data_52xx, },
{.compatible = "fsl,mpc5200b-i2c", .data = &mpc_i2c_data_52xx, },
{.compatible = "fsl,mpc5200-i2c", .data = &mpc_i2c_data_52xx, },
+ {.compatible = "fsl,mpc5121-i2c", .data = &mpc_i2c_data_512x, },
{.compatible = "fsl,mpc8313-i2c", .data = &mpc_i2c_data_8313, },
{.compatible = "fsl,mpc8543-i2c", .data = &mpc_i2c_data_8543, },
{.compatible = "fsl,mpc8544-i2c", .data = &mpc_i2c_data_8544, },
@@ -613,7 +671,6 @@ static const struct of_device_id mpc_i2c_of_match[] = {
};
MODULE_DEVICE_TABLE(of, mpc_i2c_of_match);
-
/* Structure for a device driver */
static struct of_platform_driver mpc_i2c_driver = {
.match_table = mpc_i2c_of_match,
@@ -646,5 +703,5 @@ module_exit(fsl_i2c_exit);
MODULE_AUTHOR("Adrian Cox <adrian@humboldt.co.uk>");
MODULE_DESCRIPTION("I2C-Bus adapter for MPC107 bridge and "
- "MPC824x/85xx/52xx processors");
+ "MPC824x/83xx/85xx/86xx/512x/52xx processors");
MODULE_LICENSE("GPL");
--
1.6.2.5
^ permalink raw reply related
* [PATCH v6 4/4] powerpc: doc/dts-bindings: update doc of FSL I2C bindings
From: Wolfgang Grandegger @ 2010-02-10 10:05 UTC (permalink / raw)
To: Linux-i2c
Cc: Devicetree-discuss, Linuxppc-dev, Wolfgang Grandegger, Ben Dooks
In-Reply-To: <1265796330-15976-4-git-send-email-wg@grandegger.com>
From: Wolfgang Grandegger <wg@denx.de>
This patch adds the MPC5121 to the list of supported devices,
enhances the doc of the "clock-frequency" property and removes
the obsolete "cell-index", "device_type" and "fsl-i2c" property.
Furthermore an example for the MPC5121 has been added.
Signed-off-by: Wolfgang Grandegger <wg@denx.de>
Reviewed-by: Wolfram Sang <w.sang@pengutronix.de>
---
Documentation/powerpc/dts-bindings/fsl/i2c.txt | 30 +++++++++++++++++------
1 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/Documentation/powerpc/dts-bindings/fsl/i2c.txt b/Documentation/powerpc/dts-bindings/fsl/i2c.txt
index b6d2e21..50da203 100644
--- a/Documentation/powerpc/dts-bindings/fsl/i2c.txt
+++ b/Documentation/powerpc/dts-bindings/fsl/i2c.txt
@@ -2,15 +2,14 @@
Required properties :
- - device_type : Should be "i2c"
- reg : Offset and length of the register set for the device
+ - compatible : should be "fsl,CHIP-i2c" where CHIP is the name of a
+ compatible processor, e.g. mpc8313, mpc8543, mpc8544, mpc5121,
+ mpc5200 or mpc5200b. For the mpc5121, an additional node
+ "fsl,mpc5121-i2c-ctrl" is required as shown in the example below.
Recommended properties :
- - compatible : compatibility list with 2 entries, the first should
- be "fsl,CHIP-i2c" where CHIP is the name of a compatible processor,
- e.g. mpc8313, mpc8543, mpc8544, mpc5200 or mpc5200b. The second one
- should be "fsl-i2c".
- interrupts : <a b> where a is the interrupt number and b is a
field that represents an encoding of the sense and level
information for the interrupt. This should be encoded based on
@@ -24,25 +23,40 @@ Recommended properties :
Examples :
+ /* MPC5121 based board */
+ i2c@1740 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,mpc5121-i2c", "fsl-i2c";
+ reg = <0x1740 0x20>;
+ interrupts = <11 0x8>;
+ interrupt-parent = <&ipic>;
+ clock-frequency = <100000>;
+ };
+
+ i2ccontrol@1760 {
+ compatible = "fsl,mpc5121-i2c-ctrl";
+ reg = <0x1760 0x8>;
+ };
+
+ /* MPC5200B based board */
i2c@3d00 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,mpc5200b-i2c","fsl,mpc5200-i2c","fsl-i2c";
- cell-index = <0>;
reg = <0x3d00 0x40>;
interrupts = <2 15 0>;
interrupt-parent = <&mpc5200_pic>;
fsl,preserve-clocking;
};
+ /* MPC8544 base board */
i2c@3100 {
#address-cells = <1>;
#size-cells = <0>;
- cell-index = <1>;
compatible = "fsl,mpc8544-i2c", "fsl-i2c";
reg = <0x3100 0x100>;
interrupts = <43 2>;
interrupt-parent = <&mpic>;
clock-frequency = <400000>;
};
-
--
1.6.2.5
^ permalink raw reply related
* Re: [PATCH v4 1/3] i2c-mpc: use __devinit[data] for initialization functions and data
From: Wolfgang Grandegger @ 2010-02-10 10:09 UTC (permalink / raw)
To: Grant Likely
Cc: Ben Dooks, Devicetree-discuss, Linuxppc-dev, Linux-i2c,
Wolfgang Grandegger
In-Reply-To: <fa686aa41002090941je3e4fdqbecf2616a8b90e42@mail.gmail.com>
Grant Likely wrote:
> On Thu, Jan 28, 2010 at 6:25 AM, Wolfgang Grandegger <wg@grandegger.com> wrote:
>> From: Wolfgang Grandegger <wg@denx.de>
>>
>> "__devinit[data]" has not yet been used for all initialization functions
>> and data. To avoid truncating lines, the struct "mpc_i2c_match_data" has
>> been renamed to "mpc_i2c_data", which is even the better name.
>>
>> Signed-off-by: Wolfgang Grandegger <wg@denx.de>
>
> Several comments below.
>
>> ---
>> drivers/i2c/busses/i2c-mpc.c | 103 +++++++++++++++++++----------------------
>> 1 files changed, 48 insertions(+), 55 deletions(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
>> index f627001..275ebe6 100644
>> --- a/drivers/i2c/busses/i2c-mpc.c
>> +++ b/drivers/i2c/busses/i2c-mpc.c
>> @@ -66,7 +66,7 @@ struct mpc_i2c_divider {
>> u16 fdr; /* including dfsrr */
>> };
>>
>> -struct mpc_i2c_match_data {
>> +struct mpc_i2c_data {
>> void (*setclock)(struct device_node *node,
>> struct mpc_i2c *i2c,
>> u32 clock, u32 prescaler);
>> @@ -165,7 +165,7 @@ static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
>> }
>>
>> #ifdef CONFIG_PPC_MPC52xx
>> -static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] = {
>> +static const struct __devinitdata mpc_i2c_divider mpc_i2c_dividers_52xx[] = {
>
> __devinitdata goes at the end, immediately before the '='. Ditto
> throughout the file.
This made a difference and revealed section mismatches. "const" seems to
be incompatible with "__devinitdata".
>> {20, 0x20}, {22, 0x21}, {24, 0x22}, {26, 0x23},
>> {28, 0x24}, {30, 0x01}, {32, 0x25}, {34, 0x02},
>> {36, 0x26}, {40, 0x27}, {44, 0x04}, {48, 0x28},
>> @@ -582,44 +584,35 @@ static int __devexit fsl_i2c_remove(struct of_device *op)
>> return 0;
>> };
>>
>> +static struct mpc_i2c_data __devinitdata mpc_i2c_data_52xx = {
>> + .setclock = mpc_i2c_setclock_52xx,
>> +};
>> +
>> +static struct mpc_i2c_data __devinitdata mpc_i2c_data_8313 = {
>> + .setclock = mpc_i2c_setclock_8xxx,
>> +};
>> +
>> +static struct mpc_i2c_data __devinitdata mpc_i2c_data_8543 = {
>> + .setclock = mpc_i2c_setclock_8xxx,
>> + .prescaler = 2,
>> +};
>> +
>> +static struct mpc_i2c_data __devinitdata mpc_i2c_data_8544 = {
>> + .setclock = mpc_i2c_setclock_8xxx,
>> + .prescaler = 3,
>> +};
>> +
>> static const struct of_device_id mpc_i2c_of_match[] = {
>
> You can make this __devinitdata too.
This results in a section mismatch as it's referenced by fsl_i2c_init().
I just sent out v6. Hope it's OK now.
Thanks,
Wolfgang.
^ permalink raw reply
* Re: [PATCH v6 2/4] i2c-mpc: rename "setclock" initialization functions to "setup"
From: Wolfram Sang @ 2010-02-10 10:21 UTC (permalink / raw)
To: Wolfgang Grandegger
Cc: Wolfgang Grandegger, Devicetree-discuss, Linuxppc-dev, Linux-i2c,
Ben Dooks
In-Reply-To: <1265796330-15976-3-git-send-email-wg@grandegger.com>
[-- Attachment #1: Type: text/plain, Size: 604 bytes --]
On Wed, Feb 10, 2010 at 11:05:28AM +0100, Wolfgang Grandegger wrote:
> From: Wolfgang Grandegger <wg@denx.de>
>
> To prepare support for the MPC512x processors from Freescale the
> "setclock" initialization functions have been renamed to "setup"
> because I2C interrupts must be enabled for the MPC512x by this
> function as well.
>
> Signed-off-by: Wolfgang Grandegger <wg@denx.de>
Acked-by: Wolfram Sang <w.sang@pengutronix.de>
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* Re: [net-next-2.6 PATCH 2/3] fs_enet: Add support for MPC512x to fs_enet driver
From: Wolfgang Grandegger @ 2010-02-10 10:20 UTC (permalink / raw)
To: David Miller; +Cc: wd, dzu, netdev, linuxppc-dev, agust, kosmo
In-Reply-To: <4B72794A.9010805@grandegger.com>
Wolfgang Grandegger wrote:
> Hi David,
>
> David Miller wrote:
>> From: Anatolij Gustschin <agust@denx.de>
>> Date: Tue, 9 Feb 2010 15:23:17 +0100
>>
>>> In my understanding, in the ESP scsi driver the set of defines for
>>> the register offsets is common for all chip drivers. The chip driver
>>> methods for register access translate the offsets because the
>>> registers on some chips are at different intervals (4-byte, 1-byte,
>>> 16-byte for mac_esp.c). But the register order is the same for
>>> different chips.
>>>
>>> In our case non only the register order is not the same for 8xx
>>> FEC and 5121 FEC, but there are also other differences, different
>>> reserved areas between several registers, some registers are
>>> available only on 8xx and some only on 5121.
>> That only means you would need to use a table based register address
>> translation scheme, rather than a simple calculation. Something
>> like:
>>
>> static unsigned int chip_xxx_table[] =
>> {
>> [GENERIC_REG_FOO] = CHIP_XXX_FOO,
>> ...
>> };
>>
>> static u32 chip_xxx_read_reg(struct chip *p, unsigned int reg)
>> {
>> unsigned int reg_off = chip_xxx_table[reg];
>>
>> return readl(p->regs + reg_off);
>> }
>>
>> And this table can have special tokens in entries for
>> registers which do not exist on a chip, so you can trap
>> attempted access to them in these read/write handlers.
>
> Yes, that could be done, but to honest, I do not see any improvement in
> respect to the previous patch where the register offset were defined via
> pointers within a structure.
>
>> Please stop looking for excuses to fork this driver, a
>> unified driver I think can be done cleanly.
>
> Other people suggested to fork the driver because it's getting too ugly.
That said, I think there is consensus that it does not make sense, and
it's even not possible, to provide a kernel image which runs on both,
the 8xx and the mpc512x. Therefore, there is also no need for sharing
this driver at run time. Compile time selection would allow a more
elegant and transparent implementation. Would that be an acceptable
solution?
Wolfgang.
^ permalink raw reply
* Re: [PATCH v4 1/3] i2c-mpc: use __devinit[data] for initialization functions and data
From: Stephen Rothwell @ 2010-02-10 10:32 UTC (permalink / raw)
To: Wolfgang Grandegger
Cc: Devicetree-discuss, Linux-i2c, Ben Dooks, Linuxppc-dev,
Wolfgang Grandegger
In-Reply-To: <4B7285D5.9060008@grandegger.com>
[-- Attachment #1: Type: text/plain, Size: 510 bytes --]
Hi Wolfgang,
On Wed, 10 Feb 2010 11:09:25 +0100 Wolfgang Grandegger <wg@grandegger.com> wrote:
>
> > __devinitdata goes at the end, immediately before the '='. Ditto
> > throughout the file.
>
> This made a difference and revealed section mismatches. "const" seems to
> be incompatible with "__devinitdata".
Maybe try no "const" and "__devinitconst" (I am not sure if both will
work).
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* Re: [PATCH v4 1/3] i2c-mpc: use __devinit[data] for initialization functions and data
From: Wolfgang Grandegger @ 2010-02-10 10:53 UTC (permalink / raw)
To: Stephen Rothwell
Cc: Devicetree-discuss, Linux-i2c, Ben Dooks, Linuxppc-dev,
Wolfgang Grandegger
In-Reply-To: <20100210213203.48a9105e.sfr@canb.auug.org.au>
Stephen Rothwell wrote:
> Hi Wolfgang,
>
> On Wed, 10 Feb 2010 11:09:25 +0100 Wolfgang Grandegger <wg@grandegger.com> wrote:
>>> __devinitdata goes at the end, immediately before the '='. Ditto
>>> throughout the file.
>> This made a difference and revealed section mismatches. "const" seems to
>> be incompatible with "__devinitdata".
>
> Maybe try no "const" and "__devinitconst" (I am not sure if both will
> work).
Yes, that works, even together with "const". To avoid rolling out the
whole patch series again, I can prepare a follow-up patch fixing this issue.
Wolfgang.
^ permalink raw reply
* [PATCH 1/6] powerpc: Use lwarx hint in spinlocks
From: Anton Blanchard @ 2010-02-10 10:57 UTC (permalink / raw)
To: benh; +Cc: npiggin, linuxppc-dev
Recent versions of the PowerPC architecture added a hint bit to the larx
instructions to differentiate between an atomic operation and a lock operation:
> 0 Other programs might attempt to modify the word in storage addressed by EA
> even if the subsequent Store Conditional succeeds.
>
> 1 Other programs will not attempt to modify the word in storage addressed by
> EA until the program that has acquired the lock performs a subsequent store
> releasing the lock.
To avoid a binutils dependency this patch create macros for the extended lwarx
format and uses it in the spinlock code. To test this change I used a simple
test case that acquires and releases a global pthread mutex:
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);
On a 32 core POWER6, running 32 test threads we spend almost all our time in
the futex spinlock code:
94.37% perf [kernel] [k] ._raw_spin_lock
|
|--99.95%-- ._raw_spin_lock
| |
| |--63.29%-- .futex_wake
| |
| |--36.64%-- .futex_wait_setup
Which is a good test for this patch. The results (in lock/unlock operations per
second) are:
before: 1538203 ops/sec
after: 2189219 ops/sec
An improvement of 42%
A 32 core POWER7 improves even more:
before: 1279529 ops/sec
after: 2282076 ops/sec
An improvement of 78%
Signed-off-by: Anton Blanchard <anton@samba.org>
---
v2: We do this only for 64bit until we can verify all 32bit CPUs.
Tested so far: 970 (thanks Ben), POWER5, POWER6, POWER7
Still to test: RS64, POWER3, POWER4
Index: powerpc.git/arch/powerpc/include/asm/ppc-opcode.h
===================================================================
--- powerpc.git.orig/arch/powerpc/include/asm/ppc-opcode.h 2010-02-10 15:28:58.453072362 +1100
+++ powerpc.git/arch/powerpc/include/asm/ppc-opcode.h 2010-02-10 15:33:08.963071793 +1100
@@ -24,6 +24,7 @@
#define PPC_INST_ISEL_MASK 0xfc00003e
#define PPC_INST_LSWI 0x7c0004aa
#define PPC_INST_LSWX 0x7c00042a
+#define PPC_INST_LWARX 0x7c000029
#define PPC_INST_LWSYNC 0x7c2004ac
#define PPC_INST_LXVD2X 0x7c000698
#define PPC_INST_MCRXR 0x7c000400
@@ -55,15 +56,28 @@
#define __PPC_RA(a) (((a) & 0x1f) << 16)
#define __PPC_RB(b) (((b) & 0x1f) << 11)
#define __PPC_RS(s) (((s) & 0x1f) << 21)
+#define __PPC_RT(s) __PPC_RS(s)
#define __PPC_XS(s) ((((s) & 0x1f) << 21) | (((s) & 0x20) >> 5))
#define __PPC_T_TLB(t) (((t) & 0x3) << 21)
#define __PPC_WC(w) (((w) & 0x3) << 21)
+/*
+ * Only use the larx hint bit on 64bit CPUs. Once we verify it doesn't have
+ * any side effects on all 32bit processors, we can do this all the time.
+ */
+#ifdef CONFIG_PPC64
+#define __PPC_EH(eh) (((eh) & 0x1) << 0)
+#else
+#define __PPC_EH(eh) 0
+#endif
/* Deal with instructions that older assemblers aren't aware of */
#define PPC_DCBAL(a, b) stringify_in_c(.long PPC_INST_DCBAL | \
__PPC_RA(a) | __PPC_RB(b))
#define PPC_DCBZL(a, b) stringify_in_c(.long PPC_INST_DCBZL | \
__PPC_RA(a) | __PPC_RB(b))
+#define PPC_LWARX(t, a, b, eh) stringify_in_c(.long PPC_INST_LWARX | \
+ __PPC_RT(t) | __PPC_RA(a) | \
+ __PPC_RB(b) | __PPC_EH(eh))
#define PPC_MSGSND(b) stringify_in_c(.long PPC_INST_MSGSND | \
__PPC_RB(b))
#define PPC_RFCI stringify_in_c(.long PPC_INST_RFCI)
Index: powerpc.git/arch/powerpc/include/asm/spinlock.h
===================================================================
--- powerpc.git.orig/arch/powerpc/include/asm/spinlock.h 2010-02-10 15:28:58.473072327 +1100
+++ powerpc.git/arch/powerpc/include/asm/spinlock.h 2010-02-10 15:29:29.454322618 +1100
@@ -27,6 +27,7 @@
#endif
#include <asm/asm-compat.h>
#include <asm/synch.h>
+#include <asm/ppc-opcode.h>
#define arch_spin_is_locked(x) ((x)->slock != 0)
@@ -60,7 +61,7 @@ static inline unsigned long __arch_spin_
token = LOCK_TOKEN;
__asm__ __volatile__(
-"1: lwarx %0,0,%2\n\
+"1: " PPC_LWARX(%0,0,%2,1) "\n\
cmpwi 0,%0,0\n\
bne- 2f\n\
stwcx. %1,0,%2\n\
@@ -186,7 +187,7 @@ static inline long __arch_read_trylock(a
long tmp;
__asm__ __volatile__(
-"1: lwarx %0,0,%1\n"
+"1: " PPC_LWARX(%0,0,%1,1) "\n"
__DO_SIGN_EXTEND
" addic. %0,%0,1\n\
ble- 2f\n"
@@ -211,7 +212,7 @@ static inline long __arch_write_trylock(
token = WRLOCK_TOKEN;
__asm__ __volatile__(
-"1: lwarx %0,0,%2\n\
+"1: " PPC_LWARX(%0,0,%2,1) "\n\
cmpwi 0,%0,0\n\
bne- 2f\n"
PPC405_ERR77(0,%1)
^ permalink raw reply
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