Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 2/4] rtc: OMAP: Add system pm_power_off to rtc driver
From: AnilKumar, Chimata @ 2012-11-12  9:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <B5906170F1614E41A8A28DE3B8D121433EC04226@DBDE01.ent.ti.com>

On Tue, Nov 06, 2012 at 11:15:34, Bedia, Vaibhav wrote:
> On Mon, Nov 05, 2012 at 15:12:27, AnilKumar, Chimata wrote:
> [...]
> >  
> > +#define SHUTDOWN_TIME_SEC		2
> > +#define SECS_IN_MIN			60
> > +#define WAIT_AFTER			(SECS_IN_MIN - SHUTDOWN_TIME_SEC)
> > +#define WAIT_TIME_MS			(SHUTDOWN_TIME_SEC * 1000)
> > +
> >  static void __iomem	*rtc_base;
> >  
> [...]
> > +
> > +	/* Wait few seconds instead of rollover */
> > +	do {
> > +		omap_rtc_read_time(NULL, &tm);
> > +		if (WAIT_AFTER <= tm.tm_sec)
> > +			mdelay(WAIT_TIME_MS);
> > +	} while (WAIT_AFTER <= tm.tm_sec);
> 
> This hardcoded wait for rollover doesn't look good. I see some
> helper functions in rtc-lib.c which probably could be used for
> converting the current time to elapsed seconds, add the delay and
> then convert it back to the time to be programmed in RTC without
> worrying about rollover. Why not use that?

I am not aware of those APIs, can you point some?

> 
> > +
> > +	/* Add shutdown time to the current value */
> > +	tm.tm_sec += SHUTDOWN_TIME_SEC;
> > +
> > +	if (tm2bcd(&tm) < 0)
> > +		return;
> > +
> > +	pr_info("System will go to power_off state in approx. %d secs\n",
> > +			SHUTDOWN_TIME_SEC);
> > +
> > +	/* Set the ALARM2 time */
> > +	rtc_write(tm.tm_sec, OMAP_RTC_ALARM2_SECONDS_REG);
> > +	rtc_write(tm.tm_min, OMAP_RTC_ALARM2_MINUTES_REG);
> > +	rtc_write(tm.tm_hour, OMAP_RTC_ALARM2_HOURS_REG);
> > +	rtc_write(tm.tm_mday, OMAP_RTC_ALARM2_DAYS_REG);
> > +	rtc_write(tm.tm_mon, OMAP_RTC_ALARM2_MONTHS_REG);
> > +	rtc_write(tm.tm_year, OMAP_RTC_ALARM2_YEARS_REG);
> > +
> > +	/* Enable alarm2 interrupt */
> > +	val = readl(rtc_base + OMAP_RTC_INTERRUPTS_REG);
> > +	writel(val | OMAP_RTC_INTERRUPTS_IT_ALARM2,
> > +				rtc_base + OMAP_RTC_INTERRUPTS_REG);
> > +
> 
> These registers are not present in older versions of the IP so how
> does that get handled?

I think, earlier this feature is not supported/not used.

> 
> You also need to describe the connection between the ALARM2 and the
> power off logic in detail.

Sure, I will add.

Thanks
AnilKumar

^ permalink raw reply

* [PATCH 2/4] rtc: OMAP: Add system pm_power_off to rtc driver
From: AnilKumar, Chimata @ 2012-11-12  9:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <50994156.4080305@ti.com>

On Tue, Nov 06, 2012 at 22:26:54, Cousson, Benoit wrote:
> Hi Anil,
> 
> On 11/06/2012 06:07 AM, AnilKumar, Chimata wrote:
> > On Mon, Nov 05, 2012 at 22:13:25, Cousson, Benoit wrote:
> >> Hi Anil / Colin,
> >>
> >> On 11/05/2012 10:42 AM, AnilKumar Ch wrote:
> >>> From: Colin Foe-Parker <colin.foeparker@logicpd.com>
> >>>
> >>> Add system power off control to rtc driver which is the in-charge
> >>> of controlling the BeagleBone system power. The power_off routine
> >>> can be hooked up to "pm_power_off" system call.
> >>>
> >>> System power off sequence:-
> >>> * Set PMIC STATUS_OFF when PMIC_POWER_EN is pulled low
> >>> * Enable PMIC_POWER_EN in rtc module
> >>> * Set rtc ALARM2 time
> >>> * Enable ALARM2 interrupt
> >>>
> >>> Added while (1); after the above steps to make sure that no other
> >>> process acquire cpu. Otherwise we might see an unexpected behaviour
> >>> because we are shutting down all the power rails of SoC except RTC.
> >>>
> >>> Signed-off-by: Colin Foe-Parker <colin.foeparker@logicpd.com>
> >>> [anilkumar at ti.com: move poweroff additions to rtc driver]
> >>> Signed-off-by: AnilKumar Ch <anilkumar@ti.com>
> >>> ---
> >>>  Documentation/devicetree/bindings/rtc/rtc-omap.txt |    5 ++
> >>>  drivers/rtc/rtc-omap.c                             |   79 +++++++++++++++++++-
> >>>  2 files changed, 83 insertions(+), 1 deletion(-)
> >>>
> >>> diff --git a/Documentation/devicetree/bindings/rtc/rtc-omap.txt b/Documentation/devicetree/bindings/rtc/rtc-omap.txt
> >>> index b47aa41..8d9f4f9 100644
> >>> --- a/Documentation/devicetree/bindings/rtc/rtc-omap.txt
> >>> +++ b/Documentation/devicetree/bindings/rtc/rtc-omap.txt
> >>> @@ -6,6 +6,10 @@ Required properties:
> >>>  - interrupts: rtc timer, alarm interrupts in order
> >>>  - interrupt-parent: phandle for the interrupt controller
> >>>  
> >>> +Optional properties:
> >>> +- ti,system-power-controller: Telling whether or not rtc is controlling
> >>> +  the system power.
> >>
> >> I don't know how it is connected at board level, but I'm not sure the
> >> binding is the proper one.
> > 
> > Hi Benoit,
> >  ________________________________
> > |   ______          _______      |
> > |  |      |        |       |     |
> > |  |RTC   |        |       |     |
> > |  |PMIC  |  Line  |       |     |
> > |  |PWR_EN|=======>|PWR_EN |     |
> > |  |______|        |_______|     |
> > |  AM335x SoC       TPS65217     |
> > |                                |
> > |________________________________|
> >           BeagleBone
> > 
> > This is how RTC PMIC_PWR_EN is connected to PWR_EN of TPS65217 PMIC. Only when
> > RTC pull low in PMIC_PWR_EN then PMIC will go to power off state provided TPS65217
> > status should be changed to STATUS_OFF.
> > 
> > ALARM2 event should be trigger to configure PMIC_PWR_EN properly then the "Line"
> > driven low so that PMIC will go to shutdown mode.
> 
> Thanks for the nice diagram :-)

I missed this mail thread so delayed in response

> 
> I'm wondering if we cannot abuse the gpio binding to describe that
> connection instead of creating two custom attributes (PMIC + RTC).
> 
> Ideally we should do that without having to change the RTC to use the
> gpiolib at all.
> 
> 
> rtc: rtc at 44e3e000 {
> 	compatible = "ti,da830-rtc";
> 	reg = <0x44e3e000 0x1000>;
> 	interrupts = <75, 76>;
> 	ti,hwmods = "rtc";
> 
> 	/* expose the PWR_EN functionality of this RTC*/
> 	gpio-controller;
> 	#gpio-cells = <0>; /* assuming we can use 0 ??? */
> };
> 
> ...
> 
> tps: tps at 24 {
>  	compatible = "ti,tps65217";
> 	/*
>          * Enable the power enable feature from
>          * the input line if that attribute is there.
>          */
> 	gpio-power-en = <&rtc>; /* PWR_EN */
> 
> 	...
> }	
> 
> Any thought?

No, these two are independent controllers. PMIC can go to power
off mode if we pull PWR_EN to low. We can pull down that line
by any means like PRCM or GPIO or some other. So these two flags
should be independent from each other.

Thanks
AnilKumar

^ permalink raw reply

* [PATCH RFT 2/2] pinctrl: dove: Checking valid config in dove_audio1_ctrl_set
From: Sebastian Hesselbarth @ 2012-11-12  9:43 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352641569.3113.2.camel@phoenix>

On 11/11/2012 02:46 PM, Axel Lin wrote:
> Move the code checking valid config to dove_audio1_ctrl_set(), this ensures we
> always set valid config. And then dove_audio1_ctrl_get() always returns correct
> config.
>
> Signed-off-by: Axel Lin<axel.lin@ingics.com>

Axel,

although correct in a software point-of-view, I don't think we should move this
from _get to _set because the hardware allows to set these bits without changing
the actual function. There are some dominant bits in this settings so the masking
is done to not have ~5 different values for the same function here.

The pinctrl driver will set one of the (software) supported values anyway but on
reset there could be one of the values mentioned above written into the registers.

Sebastian

^ permalink raw reply

* [PATCH RFT 1/2] pinctrl: dove: Fix dove_audio1_ctrl_set when BIT(0|1|2|3) of config is clear
From: Sebastian Hesselbarth @ 2012-11-12  9:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352641495.3113.1.camel@phoenix>

On 11/11/2012 02:44 PM, Axel Lin wrote:
> Current implementation in dove_audio1_ctrl_set() does not clear corresponding
> register bit if BIT(0|1|2|3) of config is clear. Fix it.
>
> Signed-off-by: Axel Lin<axel.lin@ingics.com>

Axel,

thanks for the hint on not clearing the audio1 bits before setting them.
I will test this tonight and guess you should prepare a real patch for
this one.

Sebastian

^ permalink raw reply

* Build failure: OMAP4430 failed due to exynos4 pinctrl
From: Russell King - ARM Linux @ 2012-11-12  9:33 UTC (permalink / raw)
  To: linux-arm-kernel

Last night's randconfig for OMAP4430 failed with:

drivers/built-in.o:(.rodata+0x1a60): undefined reference to `exynos4210_pin_ctrl'

Config and log:
http://www.arm.linux.org.uk/developer/build/file.php?type=config&idx=2693
http://www.arm.linux.org.uk/developer/build/result.php?type=build&idx=2693

^ permalink raw reply

* [RFC 3/6] sched: pack small tasks
From: Vincent Guittot @ 2012-11-12  9:30 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <5093A63B.2070700@ti.com>

On 2 November 2012 11:53, Santosh Shilimkar <santosh.shilimkar@ti.com> wrote:
> On Monday 29 October 2012 06:42 PM, Vincent Guittot wrote:
>>
>> On 24 October 2012 17:20, Santosh Shilimkar <santosh.shilimkar@ti.com>
>> wrote:
>>>
>>> Vincent,
>>>
>>> Few comments/questions.
>>>
>>>
>>> On Sunday 07 October 2012 01:13 PM, Vincent Guittot wrote:
>>>>
>>>>
>>>> During sched_domain creation, we define a pack buddy CPU if available.
>>>>
>>>> On a system that share the powerline at all level, the buddy is set to
>>>> -1
>>>>
>>>> On a dual clusters / dual cores system which can powergate each core and
>>>> cluster independantly, the buddy configuration will be :
>>>>         | CPU0 | CPU1 | CPU2 | CPU3 |
>>>> -----------------------------------
>>>> buddy | CPU0 | CPU0 | CPU0 | CPU2 |
>>>
>>>
>>>                          ^
>>> Is that a typo ? Should it be CPU2 instead of
>>> CPU0 ?
>>
>>
>> No it's not a typo.
>> The system packs at each scheduling level. It starts to pack in
>> cluster because each core can power gate independently so CPU1 tries
>> to pack its tasks in CPU0 and CPU3 in CPU2. Then, it packs at CPU
>> level so CPU2 tries to pack in the cluster of CPU0 and CPU0 packs in
>> itself
>>
> I get it. Though in above example a task may migrate from say
> CPU3->CPU2->CPU0 as part of packing. I was just thinking whether
> moving such task from say CPU3 to CPU0 might be best instead.

We pack in the cluster then at CPU level. Tasks could sometimes
migrate directly to CPU0 but we would miss the case where CPU0 is busy
but CPU2 is not

Vincent

>
>
>>>
>>>> Small tasks tend to slip out of the periodic load balance.
>>>> The best place to choose to migrate them is at their wake up.
>>>>
>>> I have tried this series since I was looking at some of these packing
>>> bits. On Mobile workloads like OSIdle with Screen ON, MP3, gallary,
>>> I did see some additional filtering of threads with this series
>>> but its not making much difference in power. More on this below.
>>
>>
>> Can I ask you which configuration you have used ? how many cores and
>> cluster ?  Can they be power gated independently ?
>>
> I have been trying with couple of setups. Dual Core ARM machine and
> Quad core X86 box with single package thought most of the mobile
> workload analysis I was doing on ARM machine. On both setups
> CPUs can be gated independently.
>
>
>>>
>>>
>>>> Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org>
>>>> ---
>>>>    kernel/sched/core.c  |    1 +
>>>>    kernel/sched/fair.c  |  109
>>>> ++++++++++++++++++++++++++++++++++++++++++++++++++
>>>>    kernel/sched/sched.h |    1 +
>>>>    3 files changed, 111 insertions(+)
>>>>
>>>> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
>>>> index dab7908..70cadbe 100644
>>>> --- a/kernel/sched/core.c
>>>> +++ b/kernel/sched/core.c
>>>> @@ -6131,6 +6131,7 @@ cpu_attach_domain(struct sched_domain *sd, struct
>>>> root_domain *rd, int cpu)
>>>>          rcu_assign_pointer(rq->sd, sd);
>>>>          destroy_sched_domains(tmp, cpu);
>>>>
>>>> +       update_packing_domain(cpu);
>>>>          update_top_cache_domain(cpu);
>>>>    }
>>>>
>>>> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
>>>> index 4f4a4f6..8c9d3ed 100644
>>>> --- a/kernel/sched/fair.c
>>>> +++ b/kernel/sched/fair.c
>>>> @@ -157,6 +157,63 @@ void sched_init_granularity(void)
>>>>          update_sysctl();
>>>>    }
>>>>
>>>> +
>>>> +/*
>>>> + * Save the id of the optimal CPU that should be used to pack small
>>>> tasks
>>>> + * The value -1 is used when no buddy has been found
>>>> + */
>>>> +DEFINE_PER_CPU(int, sd_pack_buddy);
>>>> +
>>>> +/* Look for the best buddy CPU that can be used to pack small tasks
>>>> + * We make the assumption that it doesn't wort to pack on CPU that
>>>> share
>>>> the
>>>
>>>
>>> s/wort/worth
>>
>>
>> yes
>>
>>>
>>>> + * same powerline. We looks for the 1st sched_domain without the
>>>> + * SD_SHARE_POWERLINE flag. Then We look for the sched_group witht the
>>>> lowest
>>>> + * power per core based on the assumption that their power efficiency
>>>> is
>>>> + * better */
>>>
>>>
>>> Commenting style..
>>> /*
>>>   *
>>>   */
>>>
>>
>> yes
>>
>>> Can you please expand the why the assumption is right ?
>>> "it doesn't wort to pack on CPU that share the same powerline"
>>
>>
>> By "share the same power-line", I mean that the CPUs can't power off
>> independently. So if some CPUs can't power off independently, it's
>> worth to try to use most of them to race to idle.
>>
> In that case I suggest we use different word here. Power line can be
> treated as voltage line, power domain.
> May be SD_SHARE_CPU_POWERDOMAIN ?
>
>
>>>
>>> Think about a scenario where you have quad core, ducal cluster system
>>>
>>>          |Cluster1|                      |cluster 2|
>>> | CPU0 | CPU1 | CPU2 | CPU3 |   | CPU0 | CPU1 | CPU2 | CPU3 |
>>>
>>>
>>> Both clusters run from same voltage rail and have same PLL
>>> clocking them. But the cluster have their own power domain
>>> and all CPU's can power gate them-self to low power states.
>>> Clusters also have their own level2 caches.
>>>
>>> In this case, you will still save power if you try to pack
>>> load on one cluster. No ?
>>
>>
>> yes, I need to update the description of SD_SHARE_POWERLINE because
>> I'm afraid I was not clear enough. SD_SHARE_POWERLINE includes the
>> power gating capacity of each core. For your example above, the
>> SD_SHARE_POWERLINE shoud be cleared at both MC and CPU level.
>>
> Thanks for clarification.
>
>
>>>
>>>
>>>> +void update_packing_domain(int cpu)
>>>> +{
>>>> +       struct sched_domain *sd;
>>>> +       int id = -1;
>>>> +
>>>> +       sd = highest_flag_domain(cpu, SD_SHARE_POWERLINE);
>>>> +       if (!sd)
>>>> +               sd =
>>>> rcu_dereference_check_sched_domain(cpu_rq(cpu)->sd);
>>>> +       else
>>>> +               sd = sd->parent;
>>>> +
>>>> +       while (sd) {
>>>> +               struct sched_group *sg = sd->groups;
>>>> +               struct sched_group *pack = sg;
>>>> +               struct sched_group *tmp = sg->next;
>>>> +
>>>> +               /* 1st CPU of the sched domain is a good candidate */
>>>> +               if (id == -1)
>>>> +                       id = cpumask_first(sched_domain_span(sd));
>>>> +
>>>> +               /* loop the sched groups to find the best one */
>>>> +               while (tmp != sg) {
>>>> +                       if (tmp->sgp->power * sg->group_weight <
>>>> +                                       sg->sgp->power *
>>>> tmp->group_weight)
>>>> +                               pack = tmp;
>>>> +                       tmp = tmp->next;
>>>> +               }
>>>> +
>>>> +               /* we have found a better group */
>>>> +               if (pack != sg)
>>>> +                       id = cpumask_first(sched_group_cpus(pack));
>>>> +
>>>> +               /* Look for another CPU than itself */
>>>> +               if ((id != cpu)
>>>> +                || ((sd->parent) && !(sd->parent->flags &&
>>>> SD_LOAD_BALANCE)))
>>>
>>>
>>> Is the condition "!(sd->parent->flags && SD_LOAD_BALANCE)" for
>>> big.LITTLE kind of system where SD_LOAD_BALANCE may not be used ?
>>
>>
>> No, packing small tasks is part of the load balance so if the
>> LOAD_BALANCE flag is cleared, we will not try to pack which is a kind
>> of load balance. There is no link with big.LITTLE
>>
> Now it make sense to me.
>
>
>>>
>>>
>>>> +                       break;
>>>> +
>>>> +               sd = sd->parent;
>>>> +       }
>>>> +
>>>> +       pr_info(KERN_INFO "CPU%d packing on CPU%d\n", cpu, id);
>>>> +       per_cpu(sd_pack_buddy, cpu) = id;
>>>> +}
>>>> +
>>>>    #if BITS_PER_LONG == 32
>>>>    # define WMULT_CONST  (~0UL)
>>>>    #else
>>>> @@ -3073,6 +3130,55 @@ static int select_idle_sibling(struct task_struct
>>>> *p, int target)
>>>>          return target;
>>>>    }
>>>>
>>>> +static inline bool is_buddy_busy(int cpu)
>>>> +{
>>>> +       struct rq *rq = cpu_rq(cpu);
>>>> +
>>>> +       /*
>>>> +        * A busy buddy is a CPU with a high load or a small load with a
>>>> lot of
>>>> +        * running tasks.
>>>> +        */
>>>> +       return ((rq->avg.usage_avg_sum << rq->nr_running) >
>>>> +                       rq->avg.runnable_avg_period);
>>>
>>>
>>> I agree busy CPU is the one with high load, but many small threads may
>>> not make CPU fully busy, right ? Should we just stick to the load
>>> parameter alone here ?
>>
>>
>> IMO, the busy state of a CPU isn't only the load but also how many
>> threads are waiting for running on it. This formula tries to take into
>> account both inputs. If you have dozen of small tasks on a CPU, the
>> latency can be large even if the tasks are small.
>>
> Sure. Your point is to avoid throttling and probably use race for
> idle.
>
>
>>>
>>>
>>>> +}
>>>> +
>>>> +static inline bool is_light_task(struct task_struct *p)
>>>> +{
>>>> +       /* A light task runs less than 25% in average */
>>>> +       return ((p->se.avg.usage_avg_sum << 2) <
>>>> p->se.avg.runnable_avg_period);
>>>> +}
>>>
>>>
>>> Since the whole packing logic relies on the light threads only, the
>>> overall effectiveness is not significant. Infact with multiple tries on
>>> Dual core system, I didn't see any major improvement in power. I think
>>> we need to be more aggressive here. From the cover letter, I noticed
>>> that, you were concerned about any performance drop due to packing and
>>> may be that is the reason you chose the conservative threshold. But the
>>> fact is, if we want to save meaningful power, there will be slight
>>> performance drop which is expected.
>>
>>
>> I think that everybody agrees that packing small tasks will save power
>> whereas it seems to be not so obvious for heavy task. But I may have
>> set the threshold a bit too low
>>
> I agree on packing saves power part for sure.
>
>
>> Up to which load, you would like to pack on 1 core of your dual core
>> system ?
>> Can you provide more details of your load ? Have you got a trace that
>> you can share ?
>>
> More than how much load to pack, I was more looking from the power
> savings delta we can achieve by doing it. Some of the usecases like
> osidle, mp3, gallary are already very low power and that might be
> the reason I didn't notice major mA delta. Though the perf
> traces did show some filtering even at 25 % load. i tried upto
> 50 % threshold to see the effectiveness and there was more
> improvement and hence the suggestion about aggressiveness.
>
> May be you can try some of these use-cases on your setup instead of
> synthetic workload and see the results.

Yes, I will do that. The main advantage of synthetic workload is the
reproducibility and the interdependency against the framework that is
used. But I'm going to use more realistic use-cases.

Regards,
Vincent

>
> Regards
> Santosh
>
>
>

^ permalink raw reply

* [PATCH v4 06/13] ARM: KVM: VGIC distributor handling
From: Dong Aisheng @ 2012-11-12  9:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20121110154458.3061.28629.stgit@chazy-air>

On Sat, Nov 10, 2012 at 04:44:58PM +0100, Christoffer Dall wrote:
[...]
> @@ -141,7 +519,98 @@ struct mmio_range *find_matching_range(const struct mmio_range *ranges,
>   */
>  bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exit_mmio *mmio)
>  {
> -	return KVM_EXIT_MMIO;
> +	const struct mmio_range *range;
> +	struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
> +	unsigned long base = dist->vgic_dist_base;
> +	bool updated_state;
> +
> +	if (!irqchip_in_kernel(vcpu->kvm) ||
> +	    mmio->phys_addr < base ||
> +	    (mmio->phys_addr + mmio->len) > (base + dist->vgic_dist_size))
> +		return false;
> +
> +	range = find_matching_range(vgic_ranges, mmio, base);
> +	if (unlikely(!range || !range->handle_mmio)) {
> +		pr_warn("Unhandled access %d %08llx %d\n",
> +			mmio->is_write, mmio->phys_addr, mmio->len);
> +		return false;
> +	}
> +
> +	spin_lock(&vcpu->kvm->arch.vgic.lock);
> +	updated_state = range->handle_mmio(vcpu, mmio,mmio->phys_addr - range->base - base);
Missing space after ','.
Checkpatch may fail here.

Regards
Dong Aisheng

^ permalink raw reply

* [PATCH 17/17] mtd: m25p80: change the m25p80_read to reading page to page
From: Baruch Siach @ 2012-11-12  9:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352710357-3265-18-git-send-email-wenyou.yang@atmel.com>

Hi Wenyou Yang,

On Mon, Nov 12, 2012 at 04:52:37PM +0800, Wenyou Yang wrote:
> When run "flashcp /bin/busybox /dev/mtdX", it arised a OOPS. changing to fix the [BUG].

Please post the BUG printout, and include it with the commit log.

baruch

> Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
> Cc: dwmw2 at infradead.org
> Cc: linux-mtd at lists.infradead.org
> ---
>  drivers/mtd/devices/m25p80.c |   44 +++++++++++++++++++++++++++++++++++++++---
>  1 file changed, 41 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> index 03838ba..73e5fea 100644
> --- a/drivers/mtd/devices/m25p80.c
> +++ b/drivers/mtd/devices/m25p80.c
> @@ -340,6 +340,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
>  	size_t *retlen, u_char *buf)
>  {
>  	struct m25p *flash = mtd_to_m25p(mtd);
> +	u32 page_offset, page_size;
>  	struct spi_transfer t[2];
>  	struct spi_message m;
>  
> @@ -358,7 +359,6 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
>  	spi_message_add_tail(&t[0], &m);
>  
>  	t[1].rx_buf = buf;
> -	t[1].len = len;
>  	spi_message_add_tail(&t[1], &m);
>  
>  	mutex_lock(&flash->lock);
> @@ -379,9 +379,47 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
>  	flash->command[0] = OPCODE_READ;
>  	m25p_addr2cmd(flash, from, flash->command);
>  
> -	spi_sync(flash->spi, &m);
> +	page_offset = from & (flash->page_size - 1);
> +
> +	/* do all the bytes fit onto one page? */
> +	if (page_offset + len <= flash->page_size) {
> +		t[1].len = len;
> +
> +		spi_sync(flash->spi, &m);
> +
> +		*retlen = m.actual_length - m25p_cmdsz(flash)
> +					- FAST_READ_DUMMY_BYTE;
> +
> +	} else {
> +		u32 i;
> +
> +		/* the size of data remaining on the first page */
> +		page_size = flash->page_size - page_offset;
> +
> +		t[1].len = page_size;
> +		spi_sync(flash->spi, &m);
> +
> +		*retlen = m.actual_length - m25p_cmdsz(flash)
> +					- FAST_READ_DUMMY_BYTE;
> +
> +		/* write everything in flash->page_size chunks */
> +		for (i = page_size; i < len; i += page_size) {
> +			page_size = len - i;
> +			if (page_size > flash->page_size)
> +				page_size = flash->page_size;
> +
> +			/* write the next page to flash */
> +			m25p_addr2cmd(flash, from + i, flash->command);
> +
> +			t[1].rx_buf = buf + i;
> +			t[1].len = page_size;
> +
> +			spi_sync(flash->spi, &m);
>  
> -	*retlen = m.actual_length - m25p_cmdsz(flash) - FAST_READ_DUMMY_BYTE;
> +			*retlen += m.actual_length - m25p_cmdsz(flash)
> +						- FAST_READ_DUMMY_BYTE;
> +		}
> +	}
>  
>  	mutex_unlock(&flash->lock);
>  
> -- 
> 1.7.9.5

-- 
     http://baruch.siach.name/blog/                  ~. .~   Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
   - baruch at tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il -

^ permalink raw reply

* [PATCH v4 05/13] ARM: KVM: VGIC accept vcpu and dist base addresses from user space
From: Dong Aisheng @ 2012-11-12  8:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20121110154451.3061.74235.stgit@chazy-air>

On Sat, Nov 10, 2012 at 04:44:51PM +0100, Christoffer Dall wrote:
[...]
> +int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr)
> +{
> +	int r = 0;
> +	struct vgic_dist *vgic = &kvm->arch.vgic;
> +
> +	if (addr & ~KVM_PHYS_MASK)
> +		return -E2BIG;
> +
> +	if (addr & ~PAGE_MASK)
> +		return -EINVAL;
> +
> +	mutex_lock(&kvm->lock);
> +	switch (type) {
> +	case KVM_VGIC_V2_ADDR_TYPE_DIST:
> +		if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_dist_base))
> +			return -EEXIST;
> +		if (addr + VGIC_DIST_SIZE < addr)
> +			return -EINVAL;
> +		kvm->arch.vgic.vgic_dist_base = addr;
> +		break;
> +	case KVM_VGIC_V2_ADDR_TYPE_CPU:
> +		if (!IS_VGIC_ADDR_UNDEF(vgic->vgic_cpu_base))
> +			return -EEXIST;
> +		if (addr + VGIC_CPU_SIZE < addr)
> +			return -EINVAL;
> +		kvm->arch.vgic.vgic_cpu_base = addr;
> +		break;
> +	default:
> +		r = -ENODEV;
> +	}
> +
> +	if (vgic_ioaddr_overlap(kvm)) {
> +		kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
> +		kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;

Missing mutex_unlock?

> +		return -EINVAL;
> +	}
> +
> +	mutex_unlock(&kvm->lock);
> +	return r;
> +}
> 

Regards
Dong Aisheng

^ permalink raw reply

* [PATCH v4 04/13] ARM: KVM: Initial VGIC MMIO support code
From: Dong Aisheng @ 2012-11-12  8:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20121110154444.3061.22464.stgit@chazy-air>

On Sat, Nov 10, 2012 at 04:44:44PM +0100, Christoffer Dall wrote:
> From: Marc Zyngier <marc.zyngier@arm.com>
> 
> Wire the initial in-kernel MMIO support code for the VGIC, used
> for the distributor emulation.
> 
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
> Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
> ---
>  arch/arm/include/asm/kvm_vgic.h |    6 +-
>  arch/arm/kvm/Makefile           |    1 
>  arch/arm/kvm/vgic.c             |  138 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 144 insertions(+), 1 deletion(-)
>  create mode 100644 arch/arm/kvm/vgic.c
> 
> diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h
> index d75540a..b444ecf 100644
> --- a/arch/arm/include/asm/kvm_vgic.h
> +++ b/arch/arm/include/asm/kvm_vgic.h
> @@ -30,7 +30,11 @@ struct kvm_vcpu;
>  struct kvm_run;
>  struct kvm_exit_mmio;
>  
> -#ifndef CONFIG_KVM_ARM_VGIC
> +#ifdef CONFIG_KVM_ARM_VGIC
> +bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
> +		      struct kvm_exit_mmio *mmio);
> +
> +#else
>  static inline int kvm_vgic_hyp_init(void)
>  {
>  	return 0;
> diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile
> index 8a4f396..c019f02 100644
> --- a/arch/arm/kvm/Makefile
> +++ b/arch/arm/kvm/Makefile
> @@ -20,3 +20,4 @@ obj-$(CONFIG_KVM_ARM_HOST) += $(addprefix ../../../virt/kvm/, kvm_main.o coalesc
>  
>  obj-$(CONFIG_KVM_ARM_HOST) += arm.o guest.o mmu.o emulate.o reset.o
>  obj-$(CONFIG_KVM_ARM_HOST) += coproc.o coproc_a15.o mmio.o decode.o
> +obj-$(CONFIG_KVM_ARM_VGIC) += vgic.o
> diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
> new file mode 100644
> index 0000000..26ada3b
> --- /dev/null
> +++ b/arch/arm/kvm/vgic.c
> @@ -0,0 +1,138 @@
> +/*
> + * Copyright (C) 2012 ARM Ltd.
> + * Author: Marc Zyngier <marc.zyngier@arm.com>
> + *
> + * 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.
> + *
> + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + */
> +
> +#include <linux/kvm.h>
> +#include <linux/kvm_host.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <asm/kvm_emulate.h>
> +
> +#define ACCESS_READ_VALUE	(1 << 0)
> +#define ACCESS_READ_RAZ		(0 << 0)
> +#define ACCESS_READ_MASK(x)	((x) & (1 << 0))
> +#define ACCESS_WRITE_IGNORED	(0 << 1)
> +#define ACCESS_WRITE_SETBIT	(1 << 1)
> +#define ACCESS_WRITE_CLEARBIT	(2 << 1)
> +#define ACCESS_WRITE_VALUE	(3 << 1)
> +#define ACCESS_WRITE_MASK(x)	((x) & (3 << 1))
> +
> +/**
> + * vgic_reg_access - access vgic register
> + * @mmio:   pointer to the data describing the mmio access
> + * @reg:    pointer to the virtual backing of the vgic distributor struct

Is this correct?

> + * @offset: least significant 2 bits used for word offset
> + * @mode:   ACCESS_ mode (see defines above)
> + *
> + * Helper to make vgic register access easier using one of the access
> + * modes defined for vgic register access
> + * (read,raz,write-ignored,setbit,clearbit,write)
> + */
> +static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg,
> +			    u32 offset, int mode)
> +{
> +	int word_offset = offset & 3;
> +	int shift = word_offset * 8;
> +	u32 mask;
> +	u32 regval;
> +
> +	/*
> +	 * Any alignment fault should have been delivered to the guest
> +	 * directly (ARM ARM B3.12.7 "Prioritization of aborts").
> +	 */
> +
> +	mask = (~0U) >> (word_offset * 8);
> +	if (reg)
> +		regval = *reg;
> +	else {
> +		BUG_ON(mode != (ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED));
> +		regval = 0;
> +	}
> +
> +	if (mmio->is_write) {
> +		u32 data = (*((u32 *)mmio->data) & mask) << shift;
> +		switch (ACCESS_WRITE_MASK(mode)) {
> +		case ACCESS_WRITE_IGNORED:
> +			return;
> +
> +		case ACCESS_WRITE_SETBIT:
> +			regval |= data;
> +			break;
> +
> +		case ACCESS_WRITE_CLEARBIT:
> +			regval &= ~data;
> +			break;
> +
> +		case ACCESS_WRITE_VALUE:
> +			regval = (regval & ~(mask << shift)) | data;
> +			break;
> +		}
> +		*reg = regval;
> +	} else {
> +		switch (ACCESS_READ_MASK(mode)) {
> +		case ACCESS_READ_RAZ:
> +			regval = 0;
> +			/* fall through */
> +
> +		case ACCESS_READ_VALUE:
> +			*((u32 *)mmio->data) = (regval >> shift) & mask;
> +		}
> +	}
> +}
> +
> +/* All this should be handled by kvm_bus_io_*()... FIXME!!! */
> +struct mmio_range {
> +	unsigned long base;
> +	unsigned long len;
> +	bool (*handle_mmio)(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
> +			    u32 offset);
> +};
> +
> +static const struct mmio_range vgic_ranges[] = {
> +	{}
> +};
> +
> +static const
> +struct mmio_range *find_matching_range(const struct mmio_range *ranges,
> +				       struct kvm_exit_mmio *mmio,
> +				       unsigned long base)
> +{
> +	const struct mmio_range *r = ranges;
> +	unsigned long addr = mmio->phys_addr - base;
> +
> +	while (r->len) {
> +		if (addr >= r->base &&
> +		    (addr + mmio->len) <= (r->base + r->len))
> +			return r;
> +		r++;
> +	}
> +
> +	return NULL;
> +}
> +
> +/**
> + * vgic_handle_mmio - handle an in-kernel MMIO access
> + * @vcpu:	pointer to the vcpu performing the access
> + * @mmio:	pointer to the data describing the access

Can we also have @run here?

> + *
> + * returns true if the MMIO access has been performed in kernel space,
> + * and false if it needs to be emulated in user space.
> + */
> +bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exit_mmio *mmio)
> +{
> +	return KVM_EXIT_MMIO;
> +}
> 

Regards
Dong Aisheng

^ permalink raw reply

* [PATCH 17/17] mtd: m25p80: change the m25p80_read to reading page to page
From: Wenyou Yang @ 2012-11-12  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352710357-3265-1-git-send-email-wenyou.yang@atmel.com>

When run "flashcp /bin/busybox /dev/mtdX", it arised a OOPS. changing to fix the [BUG].

Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
Cc: dwmw2 at infradead.org
Cc: linux-mtd at lists.infradead.org
---
 drivers/mtd/devices/m25p80.c |   44 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 41 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 03838ba..73e5fea 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -340,6 +340,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
 	size_t *retlen, u_char *buf)
 {
 	struct m25p *flash = mtd_to_m25p(mtd);
+	u32 page_offset, page_size;
 	struct spi_transfer t[2];
 	struct spi_message m;
 
@@ -358,7 +359,6 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
 	spi_message_add_tail(&t[0], &m);
 
 	t[1].rx_buf = buf;
-	t[1].len = len;
 	spi_message_add_tail(&t[1], &m);
 
 	mutex_lock(&flash->lock);
@@ -379,9 +379,47 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
 	flash->command[0] = OPCODE_READ;
 	m25p_addr2cmd(flash, from, flash->command);
 
-	spi_sync(flash->spi, &m);
+	page_offset = from & (flash->page_size - 1);
+
+	/* do all the bytes fit onto one page? */
+	if (page_offset + len <= flash->page_size) {
+		t[1].len = len;
+
+		spi_sync(flash->spi, &m);
+
+		*retlen = m.actual_length - m25p_cmdsz(flash)
+					- FAST_READ_DUMMY_BYTE;
+
+	} else {
+		u32 i;
+
+		/* the size of data remaining on the first page */
+		page_size = flash->page_size - page_offset;
+
+		t[1].len = page_size;
+		spi_sync(flash->spi, &m);
+
+		*retlen = m.actual_length - m25p_cmdsz(flash)
+					- FAST_READ_DUMMY_BYTE;
+
+		/* write everything in flash->page_size chunks */
+		for (i = page_size; i < len; i += page_size) {
+			page_size = len - i;
+			if (page_size > flash->page_size)
+				page_size = flash->page_size;
+
+			/* write the next page to flash */
+			m25p_addr2cmd(flash, from + i, flash->command);
+
+			t[1].rx_buf = buf + i;
+			t[1].len = page_size;
+
+			spi_sync(flash->spi, &m);
 
-	*retlen = m.actual_length - m25p_cmdsz(flash) - FAST_READ_DUMMY_BYTE;
+			*retlen += m.actual_length - m25p_cmdsz(flash)
+						- FAST_READ_DUMMY_BYTE;
+		}
+	}
 
 	mutex_unlock(&flash->lock);
 
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 16/17] ARM: dts: add spi nodes for atmel boards
From: Wenyou Yang @ 2012-11-12  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352710357-3265-1-git-send-email-wenyou.yang@atmel.com>

From: Richard Genoud <richard.genoud@gmail.com>

Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
[wenyou.yang at atmel.com: added spi nodes for other atmel boards]
[wenyou.yang at atmel.com: remove the cs-gpios property for sam9x5ek boards]
Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
Cc: linux at arm.linux.org.uk
---
Hi, Richard,

This patch is based on the orignal patch from Richard Genoud 
	[PATCH] sam9x5ek DTS: enable SPI dataflash
and referring to this patch Wenyou Yang add spi nodes for other atmel boards.
	and remove the cs-gpios property for sam9x5ek boards.

Is it ok for you?

Best Regards,
Wenyou Yang

 arch/arm/boot/dts/at91sam9263ek.dts         |    9 +++++++++
 arch/arm/boot/dts/at91sam9g20ek_common.dtsi |    9 +++++++++
 arch/arm/boot/dts/at91sam9g25ek.dts         |    9 +++++++++
 arch/arm/boot/dts/at91sam9m10g45ek.dts      |    9 +++++++++
 arch/arm/boot/dts/at91sam9n12ek.dts         |    9 +++++++++
 5 files changed, 45 insertions(+)

diff --git a/arch/arm/boot/dts/at91sam9263ek.dts b/arch/arm/boot/dts/at91sam9263ek.dts
index f86ac4b..11e274d 100644
--- a/arch/arm/boot/dts/at91sam9263ek.dts
+++ b/arch/arm/boot/dts/at91sam9263ek.dts
@@ -50,6 +50,15 @@
 				atmel,vbus-gpio = <&pioA 25 0>;
 				status = "okay";
 			};
+
+			spi0: spi at fffa4000 {
+				status = "okay";
+				mtd_dataflash at 0 {
+					compatible = "atmel,at45", "atmel,dataflash";
+					spi-max-frequency = <50000000>;
+					reg = <0>;
+				};
+			};
 		};
 
 		nand0: nand at 40000000 {
diff --git a/arch/arm/boot/dts/at91sam9g20ek_common.dtsi b/arch/arm/boot/dts/at91sam9g20ek_common.dtsi
index e6391a4..3c3b63d 100644
--- a/arch/arm/boot/dts/at91sam9g20ek_common.dtsi
+++ b/arch/arm/boot/dts/at91sam9g20ek_common.dtsi
@@ -51,6 +51,15 @@
 				atmel,vbus-gpio = <&pioC 5 0>;
 				status = "okay";
 			};
+
+			spi0: spi at fffc8000 {
+				status = "okay";
+				mtd_dataflash at 0 {
+					compatible = "atmel,at45", "atmel,dataflash";
+					spi-max-frequency = <50000000>;
+					reg = <1>;
+				};
+			};
 		};
 
 		nand0: nand at 40000000 {
diff --git a/arch/arm/boot/dts/at91sam9g25ek.dts b/arch/arm/boot/dts/at91sam9g25ek.dts
index 877c08f..3b84bc9 100644
--- a/arch/arm/boot/dts/at91sam9g25ek.dts
+++ b/arch/arm/boot/dts/at91sam9g25ek.dts
@@ -44,6 +44,15 @@
 			i2c2: i2c at f8018000 {
 				status = "okay";
 			};
+
+			spi0: spi at f0000000 {
+				status = "okay";
+				m25p80 at 0 {
+					compatible = "atmel,at25df321a";
+					spi-max-frequency = <50000000>;
+					reg = <0>;
+				};
+			};
 		};
 
 		usb0: ohci at 00600000 {
diff --git a/arch/arm/boot/dts/at91sam9m10g45ek.dts b/arch/arm/boot/dts/at91sam9m10g45ek.dts
index 15e1dd4..a23ac6e 100644
--- a/arch/arm/boot/dts/at91sam9m10g45ek.dts
+++ b/arch/arm/boot/dts/at91sam9m10g45ek.dts
@@ -54,6 +54,15 @@
 			i2c1: i2c at fff88000 {
 				status = "okay";
 			};
+
+			spi0: spi at fffa4000{
+				status = "okay";
+				mtd_dataflash at 0 {
+					compatible = "atmel,at45", "atmel,dataflash";
+					spi-max-frequency = <13000000>;
+					reg = <0>;
+				};
+			};
 		};
 
 		nand0: nand at 40000000 {
diff --git a/arch/arm/boot/dts/at91sam9n12ek.dts b/arch/arm/boot/dts/at91sam9n12ek.dts
index 912b2c2..706bd00 100644
--- a/arch/arm/boot/dts/at91sam9n12ek.dts
+++ b/arch/arm/boot/dts/at91sam9n12ek.dts
@@ -45,6 +45,15 @@
 			i2c1: i2c at f8014000 {
 				status = "okay";
 			};
+
+			spi0: spi at f0000000 {
+				status = "okay";
+				m25p80 at 0 {
+					compatible = "atmel,at25df321a";
+					spi-max-frequency = <50000000>;
+					reg = <0>;
+				};
+			};
 		};
 
 		nand0: nand at 40000000 {
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 15/17] ARM: dts: add spi nodes for atmel SoC
From: Wenyou Yang @ 2012-11-12  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352710357-3265-1-git-send-email-wenyou.yang@atmel.com>

From: Richard Genoud <richard.genoud@gmail.com>

Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
[wenyou.yang at atmel.com: add spi nodes for other atmel SOC]
Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
Cc: linux at arm.linux.org.uk
---
Hi, Richard,

This patches is based on the original patch from Richard Genoud
	[PATCH] spi-atmel: add sam9x5 SPI in device tree
and referring to this patch, Wenyou Yang add spi nodes for
	other atmel SoC.

Is it OK for you?

Best Regards,
Wenyou Yang

 arch/arm/boot/dts/at91sam9260.dtsi |   34 ++++++++++++++++++++++++++++++++++
 arch/arm/boot/dts/at91sam9263.dtsi |   34 ++++++++++++++++++++++++++++++++++
 arch/arm/boot/dts/at91sam9g45.dtsi |   34 ++++++++++++++++++++++++++++++++++
 arch/arm/boot/dts/at91sam9n12.dtsi |   34 ++++++++++++++++++++++++++++++++++
 arch/arm/boot/dts/at91sam9x5.dtsi  |   34 ++++++++++++++++++++++++++++++++++
 5 files changed, 170 insertions(+)

diff --git a/arch/arm/boot/dts/at91sam9260.dtsi b/arch/arm/boot/dts/at91sam9260.dtsi
index d410581..51e4cd2 100644
--- a/arch/arm/boot/dts/at91sam9260.dtsi
+++ b/arch/arm/boot/dts/at91sam9260.dtsi
@@ -246,6 +246,40 @@
 					trigger-external;
 				};
 			};
+
+			spi0: spi at fffc8000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "atmel,at91rm9200-spi";
+				reg = <0xfffc8000 0x200>;
+				interrupts = <12 4 3>;
+				cs-gpios = <&pioA 3 0
+					    &pioC 11 0
+					    &pioC 16 0
+					    &pioC 17 0
+					   >;
+				dma-mask = <0xffffffff>;
+				dma_type = <1>;
+				version = <2>;
+				status = "disabled";
+			};
+
+			spi1: spi at fffcc000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "atmel,at91rm9200-spi";
+				reg = <0xfffcc000 0x200>;
+				interrupts = <13 4 3>;
+				cs-gpios = <&pioB 3 0
+					    &pioC 5 0
+					    &pioC 4 0
+					    &pioC 3 0
+					   >;
+				dma-mask = <0xffffffff>;
+				dma_type = <1>;
+				version = <2>;
+				status = "disabled";
+			};
 		};
 
 		nand0: nand at 40000000 {
diff --git a/arch/arm/boot/dts/at91sam9263.dtsi b/arch/arm/boot/dts/at91sam9263.dtsi
index 3e6e5c1..228cbe8 100644
--- a/arch/arm/boot/dts/at91sam9263.dtsi
+++ b/arch/arm/boot/dts/at91sam9263.dtsi
@@ -195,6 +195,40 @@
 				#size-cells = <0>;
 				status = "disabled";
 			};
+
+			spi0: spi at fffa4000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "atmel,at91rm9200-spi";
+				reg = <0xfffa4000 0x200>;
+				interrupts = <14 4 3>;
+				cs-gpios = <&pioA 5 0
+					    &pioA 3 0
+					    &pioA 4 0
+					    &pioB 11 0
+					   >;
+				dma-mask = <0xffffffff>;
+				dma_type = <1>;
+				version = <2>;
+				status = "disabled";
+			};
+
+			spi1: spi at fffa8000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "atmel,at91rm9200-spi";
+				reg = <0xfffa8000 0x200>;
+				interrupts = <15 4 3>;
+				cs-gpios = <&pioB 15 0
+					    &pioB 16 0
+					    &pioB 17 0
+					    &pioB 18 0
+					   >;
+				dma-mask = <0xffffffff>;
+				dma_type = <1>;
+				version = <2>;
+				status = "disabled";
+			};
 		};
 
 		nand0: nand at 40000000 {
diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi
index 3add030..f34f327 100644
--- a/arch/arm/boot/dts/at91sam9g45.dtsi
+++ b/arch/arm/boot/dts/at91sam9g45.dtsi
@@ -262,6 +262,40 @@
 					trigger-value = <0x6>;
 				};
 			};
+
+			spi0: spi at fffa4000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "atmel,at91rm9200-spi";
+				reg = <0xfffa4000 0x200>;
+				interrupts = <14 4 3>;
+				cs-gpios = <&pioB 3 0
+					    &pioB 18 0
+					    &pioB 19 0
+					    &pioD 27 0
+					   >;
+				dma-mask = <0xffffffff>;
+				dma_type = <1>;
+				version = <2>;
+				status = "disabled";
+			};
+
+			spi1: spi at fffa8000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "atmel,at91rm9200-spi";
+				reg = <0xfffa8000 0x200>;
+				interrupts = <15 4 3>;
+				cs-gpios = <&pioB 17 0
+					    &pioD 28 0
+					    &pioD 18 0
+					    &pioD 19 0
+					   >;
+				dma-mask = <0xffffffff>;
+				dma_type = <1>;
+				version = <2>;
+				status = "disabled";
+			};
 		};
 
 		nand0: nand at 40000000 {
diff --git a/arch/arm/boot/dts/at91sam9n12.dtsi b/arch/arm/boot/dts/at91sam9n12.dtsi
index 82508d6..a923003 100644
--- a/arch/arm/boot/dts/at91sam9n12.dtsi
+++ b/arch/arm/boot/dts/at91sam9n12.dtsi
@@ -202,6 +202,40 @@
 				#size-cells = <0>;
 				status = "disabled";
 			};
+
+			spi0: spi at f0000000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "atmel,at91rm9200-spi";
+				reg = <0xf0000000 0x100>;
+				interrupts = <13 4 3>;
+				cs-gpios = <&pioA 14 0
+					    &pioA 7 0
+					    &pioA 1 0
+					    &pioB 3 0
+					   >;
+				dma-mask = <0xffffffff>;
+				dma_type = <2>;
+				version = <2>;
+				status = "disabled";
+			};
+
+			spi1: spi at f0004000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "atmel,at91rm9200-spi";
+				reg = <0xf0004000 0x100>;
+				interrupts = <14 4 3>;
+				cs-gpios = <&pioA 8 0
+					    &pioA 0 0
+					    &pioA 31 0
+					    &pioA 30 0
+					   >;
+				dma-mask = <0xffffffff>;
+				dma_type = <2>;
+				version = <2>;
+				status = "disabled";
+			};
 		};
 
 		nand0: nand at 40000000 {
diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi
index 03fc136..25909e0 100644
--- a/arch/arm/boot/dts/at91sam9x5.dtsi
+++ b/arch/arm/boot/dts/at91sam9x5.dtsi
@@ -263,6 +263,40 @@
 					trigger-value = <0x6>;
 				};
 			};
+
+			spi0: spi at f0000000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "atmel,at91rm9200-spi";
+				reg = <0xf0000000 0x100>;
+				interrupts = <13 4 3>;
+				cs-gpios = <&pioA 14 0
+					    &pioA 7 0 /* conflicts with TXD2 */
+					    &pioA 1 0 /* conflicts with RXD0 */
+					    &pioB 3 0 /* conflicts with ERXDV */
+					   >;
+				dma-mask = <0xffffffff>;
+				dma_type = <2>;
+				version = <2>;
+				status = "disabled";
+			};
+
+			spi1: spi at f0004000 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "atmel,at91rm9200-spi";
+				reg = <0xf0004000 0x100>;
+				interrupts = <14 4 3>;
+				cs-gpios = <&pioA 8 0 /* conflitcs with RXD2 */
+					    &pioA 0 0 /* conflitcs with TXD0 */
+					    &pioA 31 0 /* conflitcs with TWCK0 */
+					    &pioA 30 0 /* conflitcs with TWD0 */
+					   >;
+				dma-mask = <0xffffffff>;
+				dma_type = <2>;
+				version = <2>;
+				status = "disabled";
+			};
 		};
 
 		nand0: nand at 40000000 {
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 14/17] ARM: at91: add clocks for spi DT entries
From: Wenyou Yang @ 2012-11-12  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352710357-3265-1-git-send-email-wenyou.yang@atmel.com>

From: Richard Genoud <richard.genoud@gmail.com>

Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
[<wenyou.yang at atmel.com: declare the spi clocks for other at91 SoC]
Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
Cc: linux at arm.linux.org.uk
Cc: plagnioj at jcrosoft.com
Cc: linux at maxim.org.za
---
Hi, Richard,

This patch is based on the original patch from Richard Genoud,
	[PATCH] sam9x5: declare SPI clocks
and referring to this patch, Wenyou Yang declare the spi clocks for
	other at91 SoC.

Is it ok for you?

Best Regards
Wenyou Yang

 arch/arm/mach-at91/at91sam9260.c |    2 ++
 arch/arm/mach-at91/at91sam9g45.c |    2 ++
 arch/arm/mach-at91/at91sam9n12.c |    2 ++
 arch/arm/mach-at91/at91sam9x5.c  |    2 ++
 4 files changed, 8 insertions(+)

diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c
index f820261..6c217eb 100644
--- a/arch/arm/mach-at91/at91sam9260.c
+++ b/arch/arm/mach-at91/at91sam9260.c
@@ -230,6 +230,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
 	CLKDEV_CON_DEV_ID("t1_clk", "fffdc000.timer", &tc4_clk),
 	CLKDEV_CON_DEV_ID("t2_clk", "fffdc000.timer", &tc5_clk),
 	CLKDEV_CON_DEV_ID("hclk", "500000.ohci", &ohci_clk),
+	CLKDEV_CON_DEV_ID("spi_clk", "fffc8000.spi", &spi0_clk),
+	CLKDEV_CON_DEV_ID("spi_clk", "fffcc000.spi", &spi1_clk),
 	/* fake hclk clock */
 	CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk),
 	CLKDEV_CON_ID("pioA", &pioA_clk),
diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
index 84af1b5..d419002 100644
--- a/arch/arm/mach-at91/at91sam9g45.c
+++ b/arch/arm/mach-at91/at91sam9g45.c
@@ -256,6 +256,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
 	CLKDEV_CON_DEV_ID("t0_clk", "fffd4000.timer", &tcb0_clk),
 	CLKDEV_CON_DEV_ID("hclk", "700000.ohci", &uhphs_clk),
 	CLKDEV_CON_DEV_ID("ehci_clk", "800000.ehci", &uhphs_clk),
+	CLKDEV_CON_DEV_ID("spi_clk", "fffa4000.spi", &spi0_clk),
+	CLKDEV_CON_DEV_ID("spi_clk", "fffa8000.spi", &spi1_clk),
 	CLKDEV_CON_DEV_ID(NULL, "fff84000.i2c", &twi0_clk),
 	CLKDEV_CON_DEV_ID(NULL, "fff88000.i2c", &twi1_clk),
 	/* fake hclk clock */
diff --git a/arch/arm/mach-at91/at91sam9n12.c b/arch/arm/mach-at91/at91sam9n12.c
index 732d3d3..ea87f0e 100644
--- a/arch/arm/mach-at91/at91sam9n12.c
+++ b/arch/arm/mach-at91/at91sam9n12.c
@@ -169,6 +169,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
 	CLKDEV_CON_DEV_ID("t0_clk", "f8008000.timer", &tcb_clk),
 	CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb_clk),
 	CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma_clk),
+	CLKDEV_CON_DEV_ID("spi_clk", "f0000000.spi", &spi0_clk),
+	CLKDEV_CON_DEV_ID("spi_clk", "f0004000.spi", &spi1_clk),
 	CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk),
 	CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk),
 	CLKDEV_CON_ID("pioA", &pioAB_clk),
diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c
index e503538..19408af 100644
--- a/arch/arm/mach-at91/at91sam9x5.c
+++ b/arch/arm/mach-at91/at91sam9x5.c
@@ -231,6 +231,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
 	CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb0_clk),
 	CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma0_clk),
 	CLKDEV_CON_DEV_ID("dma_clk", "ffffee00.dma-controller", &dma1_clk),
+	CLKDEV_CON_DEV_ID("spi_clk", "f0000000.spi", &spi0_clk),
+	CLKDEV_CON_DEV_ID("spi_clk", "f0004000.spi", &spi1_clk),
 	CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk),
 	CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk),
 	CLKDEV_CON_DEV_ID(NULL, "f8018000.i2c", &twi2_clk),
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 13/17] spi/atmel_spi: add function to read the spi data from the dts
From: Wenyou Yang @ 2012-11-12  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352710357-3265-1-git-send-email-wenyou.yang@atmel.com>

The spi data include: dma_type and version.
dma_type to decide the SPI xfer mode: = 1(pdc), = 2(dmaengine), 0(no dma, using PIO)
version to give the SPI ip version.

Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
Cc: grant.likely at secretlab.ca
Cc: rob at landley.net
Cc: devicetree-discuss at lists.ozlabs.org
Cc: linux-doc at vger.kernel.org
Cc: spi-devel-general at lists.sourceforge.net
---
 .../devicetree/bindings/spi/spi_atmel.txt          |    4 +++
 drivers/spi/spi-atmel.c                            |   28 ++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/Documentation/devicetree/bindings/spi/spi_atmel.txt b/Documentation/devicetree/bindings/spi/spi_atmel.txt
index 20cdc91..a1ceeb5 100644
--- a/Documentation/devicetree/bindings/spi/spi_atmel.txt
+++ b/Documentation/devicetree/bindings/spi/spi_atmel.txt
@@ -6,6 +6,8 @@ Required properties:
 - interrupts: Should contain macb interrupt
 - cs-gpio: Should contain the GPIOs used for chipselect.
 - dma-mask: device coherent dma mask.
+- dma_type: The dma type supported by the spi of SoC: = 0 (no used), = 1 (pdc), = 2 (dma)
+- version: The version of the spi IP.
 
 spi0: spi at f0000000 {
 	#address-cells = <1>;
@@ -19,5 +21,7 @@ spi0: spi at f0000000 {
 		    &pioB 3 0 /* conflicts with ERXDV */
 		   >;
 	dma-mask = <0xffffffff>;
+	dma_type = <1>;
+	version = <2>;
 	status = "disabled";
 };
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 568df5b..791800e 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -1467,6 +1467,30 @@ static void atmel_spi_cleanup(struct spi_device *spi)
 	kfree(asd);
 }
 
+static int of_get_atmel_spi_data(struct device_node *np, struct atmel_spi *as)
+{
+	const __be32	*val;
+
+	val = of_get_property(np, "dma_type", NULL);
+	if (!val) {
+		pr_err("%s: have no 'dma_type' property\n",
+						np->full_name);
+		return -EINVAL;
+	}
+
+	as->data.dma_type = be32_to_cpup(val);
+
+	val = of_get_property(np, "version", NULL);
+	if (!val) {
+		pr_err("%s: have no 'version' property\n", np->full_name);
+		return -EINVAL;
+	}
+
+	as->data.version = be32_to_cpup(val);
+
+	return 0;
+}
+
 /*-------------------------------------------------------------------------*/
 
 static int __devinit atmel_spi_probe(struct platform_device *pdev)
@@ -1535,6 +1559,10 @@ static int __devinit atmel_spi_probe(struct platform_device *pdev)
 	if (ret)
 		goto out_unmap_regs;
 
+	ret = of_get_atmel_spi_data(pdev->dev.of_node, as);
+	if (ret)
+		goto out_unmap_regs;
+
 	/* Initialize the hardware */
 	clk_enable(clk);
 	spi_writel(as, CR, SPI_BIT(SWRST));
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 12/17] spi/atmel_spi: add version propety as the spi data
From: Wenyou Yang @ 2012-11-12  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352710357-3265-1-git-send-email-wenyou.yang@atmel.com>

To remove the function atmel_spi_is_v2() which depends on the SOC,
for future using DTS section to decide the IP version of SOC.

Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
Cc: grant.likely at secretlab.ca
Cc: spi-devel-general at lists.sourceforge.net
---
 drivers/spi/spi-atmel.c |   26 +++++++++++++++++++-------
 1 file changed, 19 insertions(+), 7 deletions(-)

diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 0cc347c..568df5b 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -187,6 +187,7 @@
  */
 struct atmel_spi_data {
 	u8			dma_type;
+	u8			version;
 	struct at_dma_slave	dma_slave;
 };
 
@@ -264,9 +265,20 @@ static struct dma_slave_config slave_config;
  * register, but I haven't checked that it exists on all chips, and
  * this is cheaper anyway.
  */
-static bool atmel_spi_is_v2(void)
+static bool atmel_spi_is_v1(struct atmel_spi *as)
 {
-	return !cpu_is_at91rm9200();
+	if (as->data.version == 1)
+		return true;
+	else
+		return false;
+}
+
+static bool atmel_spi_is_v2(struct atmel_spi *as)
+{
+	if (as->data.version == 2)
+		return true;
+	else
+		return false;
 }
 
 /*
@@ -300,7 +312,7 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
 	unsigned active = spi->mode & SPI_CS_HIGH;
 	u32 mr;
 
-	if (atmel_spi_is_v2()) {
+	if (atmel_spi_is_v2(as)) {
 		/*
 		 * Always use CSR0. This ensures that the clock
 		 * switches to the correct idle polarity before we
@@ -355,7 +367,7 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi)
 			asd->npcs_pin, active ? " (low)" : "",
 			mr);
 
-	if (atmel_spi_is_v2() || spi->chip_select != 0)
+	if (atmel_spi_is_v2(as) || spi->chip_select != 0)
 		gpio_set_value(asd->npcs_pin, !active);
 }
 
@@ -1266,7 +1278,7 @@ static int atmel_spi_setup(struct spi_device *spi)
 	}
 
 	/* see notes above re chipselect */
-	if (!atmel_spi_is_v2()
+	if (atmel_spi_is_v1(as)
 			&& spi->chip_select == 0
 			&& (spi->mode & SPI_CS_HIGH)) {
 		dev_dbg(&spi->dev, "setup: can't be active-high\n");
@@ -1275,7 +1287,7 @@ static int atmel_spi_setup(struct spi_device *spi)
 
 	/* v1 chips start out at half the peripheral bus speed. */
 	bus_hz = clk_get_rate(as->clk);
-	if (!atmel_spi_is_v2())
+	if (atmel_spi_is_v1(as))
 		bus_hz /= 2;
 
 	if (spi->max_speed_hz) {
@@ -1347,7 +1359,7 @@ static int atmel_spi_setup(struct spi_device *spi)
 		"setup: %lu Hz bpw %u mode 0x%x -> csr%d %08x\n",
 		bus_hz / scbr, bits, spi->mode, spi->chip_select, csr);
 
-	if (!atmel_spi_is_v2())
+	if (atmel_spi_is_v1(as))
 		spi_writel(as, CSR0 + 4 * spi->chip_select, csr);
 
 	return 0;
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 11/17] spi/atmel_spi: add DT support
From: Wenyou Yang @ 2012-11-12  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352710357-3265-1-git-send-email-wenyou.yang@atmel.com>

From: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>

The atmel_spi use only gpio for chip select.

Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Cc: devicetree-discuss at lists.ozlabs.org
Cc: spi-devel-general at lists.sourceforge.net
Cc: grant.likely at secretlab.ca
Cc: rob.herring at calxeda.com
Cc: rob at landley.net
Cc: linux-doc at vger.kernel.org
Cc: richard.genoud at gmail.com
---
Hi, Richard,

This patches is based on the original patch from Jean-Christophe
	[PATCH] spi/atmel: add DT support
and merged the patch from Richard Genoud
	[PATCH] spi-atmel OF: complete documentation

Could you sign your signature in this patch?

Best Regards,
Wenyou Yang

 .../devicetree/bindings/spi/spi_atmel.txt          |   23 ++++++++++++++++++++
 drivers/spi/spi-atmel.c                            |   21 ++++++++++++++----
 2 files changed, 40 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/spi/spi_atmel.txt

diff --git a/Documentation/devicetree/bindings/spi/spi_atmel.txt b/Documentation/devicetree/bindings/spi/spi_atmel.txt
new file mode 100644
index 0000000..20cdc91
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/spi_atmel.txt
@@ -0,0 +1,23 @@
+Atmel SPI device
+
+Required properties:
+- compatible : should be "atmel,at91rm9200-spi".
+- reg: Address and length of the register set for the device
+- interrupts: Should contain macb interrupt
+- cs-gpio: Should contain the GPIOs used for chipselect.
+- dma-mask: device coherent dma mask.
+
+spi0: spi at f0000000 {
+	#address-cells = <1>;
+	#size-cells = <0>;
+	compatible = "atmel,at91rm9200-spi";
+	reg = <0xf0000000 0x100>;
+	interrupts = <13 4>;
+	cs-gpios = <&pioA 14 0
+		    &pioA 7 0 /* conflicts with TXD2 */
+		    &pioA 1 0 /* conflicts with RXD0 */
+		    &pioB 3 0 /* conflicts with ERXDV */
+		   >;
+	dma-mask = <0xffffffff>;
+	status = "disabled";
+};
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 7a3613d..0cc347c 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -20,6 +20,7 @@
 #include <linux/interrupt.h>
 #include <linux/spi/spi.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 
 #include <asm/io.h>
 #include <mach/board.h>
@@ -1242,7 +1243,7 @@ static int atmel_spi_setup(struct spi_device *spi)
 	u32			scbr, csr;
 	unsigned int		bits = spi->bits_per_word;
 	unsigned long		bus_hz;
-	unsigned int		npcs_pin;
+	int			npcs_pin;
 	int			ret;
 
 	as = spi_master_get_devdata(spi->master);
@@ -1314,7 +1315,9 @@ static int atmel_spi_setup(struct spi_device *spi)
 	csr |= SPI_BF(DLYBCT, 0);
 
 	/* chipselect must have been muxed as GPIO (e.g. in board setup) */
-	npcs_pin = (unsigned int)spi->controller_data;
+	if (!gpio_is_valid(spi->cs_gpio))
+		spi->cs_gpio = (int)spi->controller_data;
+	npcs_pin = spi->cs_gpio;
 	asd = spi->controller_state;
 	if (!asd) {
 		asd = kzalloc(sizeof(struct atmel_spi_device), GFP_KERNEL);
@@ -1435,7 +1438,7 @@ static void atmel_spi_cleanup(struct spi_device *spi)
 {
 	struct atmel_spi	*as = spi_master_get_devdata(spi->master);
 	struct atmel_spi_device	*asd = spi->controller_state;
-	unsigned		gpio = (unsigned) spi->controller_data;
+	unsigned		gpio = spi->cs_gpio;
 
 	if (!asd)
 		return;
@@ -1485,7 +1488,8 @@ static int __devinit atmel_spi_probe(struct platform_device *pdev)
 	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
 
 	master->bus_num = pdev->id;
-	master->num_chipselect = 4;
+	master->dev.of_node = pdev->dev.of_node;
+	master->num_chipselect = master->dev.of_node ? 0 : 4;
 	master->setup = atmel_spi_setup;
 	master->transfer = atmel_spi_transfer;
 	master->cleanup = atmel_spi_cleanup;
@@ -1637,11 +1641,20 @@ static int atmel_spi_resume(struct platform_device *pdev)
 #define	atmel_spi_resume	NULL
 #endif
 
+#if defined(CONFIG_OF)
+static const struct of_device_id atmel_spi_dt_ids[] = {
+	{ .compatible = "atmel,at91rm9200-spi" },
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_spi_dt_ids);
+#endif
 
 static struct platform_driver atmel_spi_driver = {
 	.driver		= {
 		.name	= "atmel_spi",
 		.owner	= THIS_MODULE,
+		.of_match_table	= of_match_ptr(atmel_spi_dt_ids),
 	},
 	.suspend	= atmel_spi_suspend,
 	.resume		= atmel_spi_resume,
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 10/17] spi/atmel_spi: correct 16 bits transfer with DMA
From: Wenyou Yang @ 2012-11-12  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352710357-3265-1-git-send-email-wenyou.yang@atmel.com>

From: Richard Genoud <richard.genoud@gmail.com>

Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
Cc: grant.likely at secretlab.ca
Cc: spi-devel-general at lists.sourceforge.net
---
 drivers/spi/spi-atmel.c |   53 +++++++++++++++++++++++++++++++----------------
 1 file changed, 35 insertions(+), 18 deletions(-)

diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 0007a53..7a3613d 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -249,6 +249,8 @@ struct atmel_spi_device {
 #define BUFFER_SIZE		PAGE_SIZE
 #define INVALID_DMA_ADDRESS	0xffffffff
 
+static struct dma_slave_config slave_config;
+
 /*
  * Version 2 of the SPI controller has
  *  - CR.LASTXFER
@@ -406,17 +408,41 @@ static bool filter(struct dma_chan *chan, void *slave)
 	}
 }
 
+static int atmel_spi_set_dma_xfer_width(struct atmel_spi *as, u8 bits_per_word)
+{
+	int err = 0;
+
+	if (bits_per_word > 8) {
+		slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+		slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+	} else {
+		slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+		slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	}
+
+	slave_config.direction = DMA_TO_DEVICE;
+	if (dmaengine_slave_config(as->dma.chan_tx, &slave_config)) {
+		dev_err(&as->pdev->dev,
+			"failed to configure tx dma channel\n");
+		err = -EINVAL;
+	}
+
+	slave_config.direction = DMA_FROM_DEVICE;
+	if (dmaengine_slave_config(as->dma.chan_rx, &slave_config)) {
+		dev_err(&as->pdev->dev,
+			"failed to configure rx dma channel\n");
+		err = -EINVAL;
+	}
+	return err;
+}
+
 static int __devinit atmel_spi_configure_dma(struct atmel_spi *as)
 {
 	struct at_dma_slave *sdata = (struct at_dma_slave *)&as->data.dma_slave;
-	struct dma_slave_config	slave_config;
 	int err;
 
-	memset(&slave_config, 0, sizeof(slave_config));
 	slave_config.dst_addr = (dma_addr_t)as->phybase + SPI_TDR;
 	slave_config.src_addr = (dma_addr_t)as->phybase + SPI_RDR;
-	slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
-	slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
 	slave_config.src_maxburst = 1;
 	slave_config.dst_maxburst = 1;
 	slave_config.device_fc = false;
@@ -439,21 +465,9 @@ static int __devinit atmel_spi_configure_dma(struct atmel_spi *as)
 		goto error;
 	}
 
-	slave_config.direction = DMA_TO_DEVICE;
-	if (dmaengine_slave_config(as->dma.chan_tx, &slave_config)) {
-		dev_err(&as->pdev->dev,
-			"failed to configure tx dma channel\n");
-		err = -EINVAL;
+	err = atmel_spi_set_dma_xfer_width(as, 8);
+	if (err)
 		goto error;
-	}
-
-	slave_config.direction = DMA_FROM_DEVICE;
-	if (dmaengine_slave_config(as->dma.chan_rx, &slave_config)) {
-		dev_err(&as->pdev->dev,
-			"failed to configure rx dma channel\n");
-		err = -EINVAL;
-		goto error;
-	}
 
 	dev_info(&as->pdev->dev, "Using %s (tx) and " \
 				" %s (rx) for DMA transfers\n",
@@ -575,6 +589,9 @@ static int atmel_spi_next_xfer_dma(struct spi_master *master,
 		memset(as->buffer, 0, xfer->len);
 	}
 
+	if (atmel_spi_set_dma_xfer_width(as, xfer->bits_per_word))
+		goto err_dma;
+
 	/* Send both scatterlists */
 	rxdesc = rxchan->device->device_prep_slave_sg(rxchan,
 					&as->dma.sgrx,
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 09/17] spi/atmel_spi: correct 16 bits transfers using PIO
From: Wenyou Yang @ 2012-11-12  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352710357-3265-1-git-send-email-wenyou.yang@atmel.com>

From: Richard Genoud <richard.genoud@gmail.com>

Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
Cc: grant.likely at secretlab.ca
Cc: spi-devel-general at lists.sourceforge.net
---
 drivers/spi/spi-atmel.c |   46 +++++++++++++++++++++++++++++++++++++---------
 1 file changed, 37 insertions(+), 9 deletions(-)

diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 00e729b..0007a53 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -518,13 +518,17 @@ static void atmel_spi_next_xfer_pio(struct spi_master *master,
 	}
 
 	if (xfer->tx_buf)
-		spi_writel(as, TDR, *(u8 *)(xfer->tx_buf));
+		if (xfer->bits_per_word > 8)
+			spi_writel(as, TDR, *(u16 *)(xfer->tx_buf));
+		else
+			spi_writel(as, TDR, *(u8 *)(xfer->tx_buf));
 	else
 		spi_writel(as, TDR, 0);
 
 	dev_dbg(master->dev.parent,
-		"  start pio xfer %p: len %u tx %p rx %p\n",
-		xfer, xfer->len, xfer->tx_buf, xfer->rx_buf);
+		"  start pio xfer %p: len %u tx %p rx %p bitpw %d\n",
+		xfer, xfer->len, xfer->tx_buf, xfer->rx_buf,
+		xfer->bits_per_word);
 
 	/* Enable relevant interrupts */
 	spi_writel(as, IER, SPI_BIT(RDRF) | SPI_BIT(OVRES));
@@ -918,21 +922,39 @@ atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
 {
 	u8		*txp;
 	u8		*rxp;
+	u16		*txp16;
+	u16		*rxp16;
 	unsigned long	xfer_pos = xfer->len - as->current_remaining_bytes;
 
 	if (xfer->rx_buf) {
-		rxp = ((u8 *)xfer->rx_buf) + xfer_pos;
-		*rxp = spi_readl(as, RDR);
+		if (xfer->bits_per_word > 8) {
+			rxp16 = (u16 *)(((u8 *)xfer->rx_buf) + xfer_pos);
+			*rxp16 = spi_readl(as, RDR);
+		} else {
+			rxp = ((u8 *)xfer->rx_buf) + xfer_pos;
+			*rxp = spi_readl(as, RDR);
+		}
 	} else {
 		spi_readl(as, RDR);
 	}
-
-	as->current_remaining_bytes--;
+	if (xfer->bits_per_word > 8) {
+		as->current_remaining_bytes -= 2;
+		if (as->current_remaining_bytes < 0)
+			as->current_remaining_bytes = 0;
+	} else {
+		as->current_remaining_bytes--;
+	}
 
 	if (as->current_remaining_bytes) {
 		if (xfer->tx_buf) {
-			txp = ((u8 *)xfer->tx_buf) + xfer_pos + 1;
-			spi_writel(as, TDR, *txp);
+			if (xfer->bits_per_word > 8) {
+				txp16 = (u16 *)(((u8 *)xfer->tx_buf)
+							+ xfer_pos + 2);
+				spi_writel(as, TDR, *txp16);
+			} else {
+				txp = ((u8 *)xfer->tx_buf) + xfer_pos + 1;
+				spi_writel(as, TDR, *txp);
+			}
 		} else {
 			spi_writel(as, TDR, 0);
 		}
@@ -1345,6 +1367,12 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg)
 				return -ENOPROTOOPT;
 			}
 		}
+		if (xfer->bits_per_word > 8) {
+			if (xfer->len % 2) {
+				dev_dbg(&spi->dev, "buffer len should be 16 bits aligned\n");
+				return -EINVAL;
+			}
+		}
 
 		/* FIXME implement these protocol options!! */
 		if (xfer->speed_hz) {
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 08/17] spi/atmel_spi: Fix spi-atmel driver to adapt to slave_config changes
From: Wenyou Yang @ 2012-11-12  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352710357-3265-1-git-send-email-wenyou.yang@atmel.com>

From: Richard Genoud <richard.genoud@gmail.com>

This is the following of the patch e2b35f3dbfc080f15b72834d08f04f0269dbe9be

Signed-off-by: Richard Genoud <richard.genoud@gmail.com>
Cc: grant.likely at secretlab.ca
Cc: spi-devel-general at lists.sourceforge.net
---
 drivers/spi/spi-atmel.c |   44 +++++++++++++++++++++++++++++++++++---------
 1 file changed, 35 insertions(+), 9 deletions(-)

diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 4cb5f05..00e729b 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -409,14 +409,21 @@ static bool filter(struct dma_chan *chan, void *slave)
 static int __devinit atmel_spi_configure_dma(struct atmel_spi *as)
 {
 	struct at_dma_slave *sdata = (struct at_dma_slave *)&as->data.dma_slave;
+	struct dma_slave_config	slave_config;
+	int err;
+
+	memset(&slave_config, 0, sizeof(slave_config));
+	slave_config.dst_addr = (dma_addr_t)as->phybase + SPI_TDR;
+	slave_config.src_addr = (dma_addr_t)as->phybase + SPI_RDR;
+	slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	slave_config.src_maxburst = 1;
+	slave_config.dst_maxburst = 1;
+	slave_config.device_fc = false;
 
 	if (sdata && sdata->dma_dev) {
 		dma_cap_mask_t mask;
 
-		/* setup DMA addresses */
-		sdata->rx_reg = (dma_addr_t)as->phybase + SPI_RDR;
-		sdata->tx_reg = (dma_addr_t)as->phybase + SPI_TDR;
-
 		/* Try to grab two DMA channels */
 		dma_cap_zero(mask);
 		dma_cap_set(DMA_SLAVE, mask);
@@ -426,13 +433,26 @@ static int __devinit atmel_spi_configure_dma(struct atmel_spi *as)
 				dma_request_channel(mask, filter, sdata);
 	}
 	if (!as->dma.chan_rx || !as->dma.chan_tx) {
-		if (as->dma.chan_rx)
-			dma_release_channel(as->dma.chan_rx);
-		if (as->dma.chan_tx)
-			dma_release_channel(as->dma.chan_tx);
 		dev_err(&as->pdev->dev, "DMA channel not available, " \
 					"unable to use SPI\n");
-		return -EBUSY;
+		err = -EBUSY;
+		goto error;
+	}
+
+	slave_config.direction = DMA_TO_DEVICE;
+	if (dmaengine_slave_config(as->dma.chan_tx, &slave_config)) {
+		dev_err(&as->pdev->dev,
+			"failed to configure tx dma channel\n");
+		err = -EINVAL;
+		goto error;
+	}
+
+	slave_config.direction = DMA_FROM_DEVICE;
+	if (dmaengine_slave_config(as->dma.chan_rx, &slave_config)) {
+		dev_err(&as->pdev->dev,
+			"failed to configure rx dma channel\n");
+		err = -EINVAL;
+		goto error;
 	}
 
 	dev_info(&as->pdev->dev, "Using %s (tx) and " \
@@ -441,6 +461,12 @@ static int __devinit atmel_spi_configure_dma(struct atmel_spi *as)
 				dma_chan_name(as->dma.chan_rx));
 
 	return 0;
+error:
+	if (as->dma.chan_rx)
+		dma_release_channel(as->dma.chan_rx);
+	if (as->dma.chan_tx)
+		dma_release_channel(as->dma.chan_tx);
+	return err;
 }
 
 static void atmel_spi_stop_dma(struct atmel_spi *as)
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 07/17] spi/atmel_spi: add dmaengine support
From: Wenyou Yang @ 2012-11-12  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352710357-3265-1-git-send-email-wenyou.yang@atmel.com>

From: Nicolas Ferre <nicolas.ferre@atmel.com>

Add dmaengine support.
According to the SoC dma type, to select the SPI xfer mode: PDC or dmaengine.

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
[wenyou.yang at atmel.com: the SoC dma type for selecting the SPI xfer mode]
[wenyou.yang at atmel.com: fix not support NPCS1,2,3 chip select]
Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
Cc: grant.likely at secretlab.ca
Cc: spi-devel-general at lists.sourceforge.net
Cc: richard.genoud at gmail.com
---
Hi, Richard,

This patch is based on the original patch from Nicolas
	[PATCH] spi/atmel_spi: add dmaengine support
and merged the patches from Richard Genoud,
	[PATCH] spi-atmel: update with dmaengine interface
	[PATCH] spi-atmel: fix __init/__devinit sections mismatch
and Wenyou Yang add the code to support DTS section for selecting the SPI xfer mode,
	and fix not supporting NPCS1,2,3 chip select only NPCS0 BUG.

Could you sign your signature in this patch?

Best Regards,
Wenyou Yang

 drivers/spi/spi-atmel.c |  530 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 509 insertions(+), 21 deletions(-)

diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 37f54c3..4cb5f05 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -15,6 +15,7 @@
 #include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/spi/spi.h>
@@ -24,6 +25,7 @@
 #include <mach/board.h>
 #include <asm/gpio.h>
 #include <mach/cpu.h>
+#include <linux/platform_data/dma-atmel.h>
 
 /* SPI register offsets */
 #define SPI_CR					0x0000
@@ -179,6 +181,27 @@
 #define spi_writel(port,reg,value) \
 	__raw_writel((value), (port)->regs + SPI_##reg)
 
+/* dma_type: the dma type supported by the spi.
+ *		dma_type = 0 (no used), = 1 (pdc), = 2 (dma)
+ */
+struct atmel_spi_data {
+	u8			dma_type;
+	struct at_dma_slave	dma_slave;
+};
+
+/* use PIO for small transfers, avoiding DMA setup/teardown overhead and
+ * cache operations; better heuristics consider wordsize and bitrate.
+ */
+#define DMA_MIN_BYTES	16
+
+struct atmel_spi_dma {
+	struct dma_chan			*chan_rx;
+	struct dma_chan			*chan_tx;
+	struct scatterlist		sgrx;
+	struct scatterlist		sgtx;
+	struct dma_async_tx_descriptor	*data_desc_rx;
+	struct dma_async_tx_descriptor	*data_desc_tx;
+};
 
 /*
  * The core SPI transfer engine just talks to a register bank to set up
@@ -198,14 +221,23 @@ struct atmel_spi {
 
 	u8			stopping;
 	struct list_head	queue;
+	struct tasklet_struct	tasklet;
 	struct spi_transfer	*current_transfer;
 	unsigned long		current_remaining_bytes;
 	struct spi_transfer	*next_transfer;
 	unsigned long		next_remaining_bytes;
 	int			done_status;
+	struct atmel_spi_data	data;
+
+	bool			use_dma;
+	bool			use_pdc;
 
+	/* scratch buffer */
 	void			*buffer;
 	dma_addr_t		buffer_dma;
+
+	/* dmaengine data */
+	struct atmel_spi_dma	dma;
 };
 
 /* Controller-specific per-slave state */
@@ -271,9 +303,9 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
 		 * switches to the correct idle polarity before we
 		 * toggle the CS.
 		 */
-		spi_writel(as, CSR0, asd->csr);
-		spi_writel(as, MR, SPI_BF(PCS, 0x0e) | SPI_BIT(MODFDIS)
-				| SPI_BIT(MSTR));
+		spi_writel(as, CSR0 + 4 * spi->chip_select, asd->csr);
+		spi_writel(as, MR, SPI_BF(PCS, ~(0x01 << spi->chip_select))
+				| SPI_BIT(MODFDIS) | SPI_BIT(MSTR));
 		mr = spi_readl(as, MR);
 		gpio_set_value(asd->npcs_pin, active);
 	} else {
@@ -334,6 +366,23 @@ static void atmel_spi_unlock(struct atmel_spi *as)
 		spin_unlock_irqrestore(&as->lock, as->flags);
 }
 
+static inline bool atmel_spi_use_dma(struct atmel_spi *as,
+				struct spi_transfer *xfer)
+{
+	if ((as->use_dma) && (xfer->len >= DMA_MIN_BYTES))
+		return true;
+	else
+		return false;
+}
+
+static inline bool atmel_spi_use_pdc(struct atmel_spi *as)
+{
+	if (as->use_pdc)
+		return true;
+	else
+		return false;
+}
+
 static inline int atmel_spi_xfer_is_last(struct spi_message *msg,
 					struct spi_transfer *xfer)
 {
@@ -345,6 +394,209 @@ static inline int atmel_spi_xfer_can_be_chained(struct spi_transfer *xfer)
 	return xfer->delay_usecs == 0 && !xfer->cs_change;
 }
 
+static bool filter(struct dma_chan *chan, void *slave)
+{
+	struct	at_dma_slave *sl = slave;
+
+	if (sl->dma_dev == chan->device->dev) {
+		chan->private = sl;
+		return true;
+	} else {
+		return false;
+	}
+}
+
+static int __devinit atmel_spi_configure_dma(struct atmel_spi *as)
+{
+	struct at_dma_slave *sdata = (struct at_dma_slave *)&as->data.dma_slave;
+
+	if (sdata && sdata->dma_dev) {
+		dma_cap_mask_t mask;
+
+		/* setup DMA addresses */
+		sdata->rx_reg = (dma_addr_t)as->phybase + SPI_RDR;
+		sdata->tx_reg = (dma_addr_t)as->phybase + SPI_TDR;
+
+		/* Try to grab two DMA channels */
+		dma_cap_zero(mask);
+		dma_cap_set(DMA_SLAVE, mask);
+		as->dma.chan_tx = dma_request_channel(mask, filter, sdata);
+		if (as->dma.chan_tx)
+			as->dma.chan_rx =
+				dma_request_channel(mask, filter, sdata);
+	}
+	if (!as->dma.chan_rx || !as->dma.chan_tx) {
+		if (as->dma.chan_rx)
+			dma_release_channel(as->dma.chan_rx);
+		if (as->dma.chan_tx)
+			dma_release_channel(as->dma.chan_tx);
+		dev_err(&as->pdev->dev, "DMA channel not available, " \
+					"unable to use SPI\n");
+		return -EBUSY;
+	}
+
+	dev_info(&as->pdev->dev, "Using %s (tx) and " \
+				" %s (rx) for DMA transfers\n",
+				dma_chan_name(as->dma.chan_tx),
+				dma_chan_name(as->dma.chan_rx));
+
+	return 0;
+}
+
+static void atmel_spi_stop_dma(struct atmel_spi *as)
+{
+	if (as->dma.chan_rx)
+		as->dma.chan_rx->device->device_control(as->dma.chan_rx,
+							DMA_TERMINATE_ALL, 0);
+	if (as->dma.chan_tx)
+		as->dma.chan_tx->device->device_control(as->dma.chan_tx,
+							DMA_TERMINATE_ALL, 0);
+}
+
+static void atmel_spi_release_dma(struct atmel_spi *as)
+{
+	if (as->dma.chan_rx)
+		dma_release_channel(as->dma.chan_rx);
+	if (as->dma.chan_tx)
+		dma_release_channel(as->dma.chan_tx);
+}
+
+/* This function is called by the DMA driver from tasklet context */
+static void dma_callback(void *data)
+{
+	struct spi_master	*master = data;
+	struct atmel_spi	*as = spi_master_get_devdata(master);
+
+	/* trigger SPI tasklet */
+	tasklet_schedule(&as->tasklet);
+}
+
+/*
+ * Next transfer using PIO.
+ * lock is held, spi tasklet is blocked
+ */
+static void atmel_spi_next_xfer_pio(struct spi_master *master,
+				struct spi_transfer *xfer)
+{
+	struct atmel_spi	*as = spi_master_get_devdata(master);
+
+	dev_vdbg(master->dev.parent, "atmel_spi_next_xfer_pio\n");
+
+	as->current_remaining_bytes = xfer->len;
+
+	/* Make sure data is not remaining in RDR */
+	spi_readl(as, RDR);
+	while (spi_readl(as, SR) & SPI_BIT(RDRF)) {
+		spi_readl(as, RDR);
+		cpu_relax();
+	}
+
+	if (xfer->tx_buf)
+		spi_writel(as, TDR, *(u8 *)(xfer->tx_buf));
+	else
+		spi_writel(as, TDR, 0);
+
+	dev_dbg(master->dev.parent,
+		"  start pio xfer %p: len %u tx %p rx %p\n",
+		xfer, xfer->len, xfer->tx_buf, xfer->rx_buf);
+
+	/* Enable relevant interrupts */
+	spi_writel(as, IER, SPI_BIT(RDRF) | SPI_BIT(OVRES));
+}
+
+/*
+ * Submit next transfer for DMA.
+ * lock is held, spi tasklet is blocked
+ */
+static int atmel_spi_next_xfer_dma(struct spi_master *master,
+				struct spi_transfer *xfer)
+{
+	struct atmel_spi	*as = spi_master_get_devdata(master);
+	struct dma_chan		*rxchan = as->dma.chan_rx;
+	struct dma_chan		*txchan = as->dma.chan_tx;
+	struct dma_async_tx_descriptor *rxdesc;
+	struct dma_async_tx_descriptor *txdesc;
+	dma_cookie_t		cookie;
+
+	dev_vdbg(master->dev.parent, "atmel_spi_next_xfer_dma\n");
+
+	/* Check that the channels are available */
+	if (!rxchan || !txchan)
+		return -ENODEV;
+
+	/* release lock for DMA operations */
+	atmel_spi_unlock(as);
+
+	/* prepare the RX dma transfer */
+	sg_init_table(&as->dma.sgrx, 1);
+	sg_dma_len(&as->dma.sgrx) = xfer->len;
+	if (xfer->rx_buf)
+		as->dma.sgrx.dma_address = xfer->rx_dma;
+	else
+		as->dma.sgrx.dma_address = as->buffer_dma;
+
+	/* prepare the TX dma transfer */
+	sg_init_table(&as->dma.sgtx, 1);
+	sg_dma_len(&as->dma.sgtx) = xfer->len;
+	if (xfer->tx_buf) {
+		as->dma.sgtx.dma_address = xfer->tx_dma;
+	} else {
+		as->dma.sgtx.dma_address = as->buffer_dma;
+		memset(as->buffer, 0, xfer->len);
+	}
+
+	/* Send both scatterlists */
+	rxdesc = rxchan->device->device_prep_slave_sg(rxchan,
+					&as->dma.sgrx,
+					1,
+					DMA_FROM_DEVICE,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK,
+					NULL);
+	if (!rxdesc)
+		goto err_dma;
+
+	txdesc = txchan->device->device_prep_slave_sg(txchan,
+					&as->dma.sgtx,
+					1,
+					DMA_TO_DEVICE,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK,
+					NULL);
+	if (!txdesc)
+		goto err_dma;
+
+	dev_dbg(master->dev.parent,
+		"  start dma xfer %p: len %u tx %p/%08x rx %p/%08x\n",
+		xfer, xfer->len, xfer->tx_buf, xfer->tx_dma,
+		xfer->rx_buf, xfer->rx_dma);
+
+	/* Enable relevant interrupts */
+	spi_writel(as, IER, SPI_BIT(OVRES));
+
+	/* Put the callback on the RX transfer only, that should finish last */
+	rxdesc->callback = dma_callback;
+	rxdesc->callback_param = master;
+
+	/* Submit and fire RX and TX with TX last so we're ready to read! */
+	cookie = rxdesc->tx_submit(rxdesc);
+	if (dma_submit_error(cookie))
+		goto err_dma;
+	cookie = txdesc->tx_submit(txdesc);
+	if (dma_submit_error(cookie))
+		goto err_dma;
+	rxchan->device->device_issue_pending(rxchan);
+	txchan->device->device_issue_pending(txchan);
+
+	/* take back lock */
+	atmel_spi_lock(as);
+	return 0;
+
+err_dma:
+	spi_writel(as, IDR, SPI_BIT(OVRES));
+	atmel_spi_stop_dma(as);
+	atmel_spi_lock(as);
+	return -ENOMEM;
+}
+
 static void atmel_spi_next_xfer_data(struct spi_master *master,
 				struct spi_transfer *xfer,
 				dma_addr_t *tx_dma,
@@ -377,10 +629,10 @@ static void atmel_spi_next_xfer_data(struct spi_master *master,
 }
 
 /*
- * Submit next transfer for DMA.
+ * Submit next transfer for PDC.
  * lock is held, spi irq is blocked
  */
-static void atmel_spi_next_xfer(struct spi_master *master,
+static void atmel_spi_next_xfer_pdc(struct spi_master *master,
 				struct spi_message *msg)
 {
 	struct atmel_spi	*as = spi_master_get_devdata(master);
@@ -477,6 +729,44 @@ static void atmel_spi_next_xfer(struct spi_master *master,
 	spi_writel(as, PTCR, SPI_BIT(TXTEN) | SPI_BIT(RXTEN));
 }
 
+/*
+ * Choose way to submit next transfer and start it.
+ * lock is held, spi tasklet is blocked
+ */
+static void atmel_spi_next_xfer(struct spi_master *master,
+				struct spi_message *msg)
+{
+	struct atmel_spi	*as = spi_master_get_devdata(master);
+	struct spi_transfer	*xfer;
+
+	dev_vdbg(&msg->spi->dev, "atmel_spi_next_xfer\n");
+
+	if (as->use_pdc)
+		atmel_spi_next_xfer_pdc(master, msg);
+	else {
+		if (!as->current_transfer)
+			xfer = list_entry(msg->transfers.next,
+				struct spi_transfer, transfer_list);
+		else
+			xfer = list_entry(
+				as->current_transfer->transfer_list.next,
+				struct spi_transfer, transfer_list);
+
+		as->current_transfer = xfer;
+
+		/* quick (and *really* not optimal) workaround for DMA BUG */
+		if (atmel_spi_use_dma(as, xfer)) {
+			if (!atmel_spi_next_xfer_dma(master, xfer))
+				return;
+			else
+				dev_err(&msg->spi->dev, "unable to use DMA, fallback to PIO\n");
+		}
+
+		/* use PIO if xfer is short or error appened using DMA */
+		atmel_spi_next_xfer_pio(master, xfer);
+	}
+}
+
 static void atmel_spi_next_message(struct spi_master *master)
 {
 	struct atmel_spi	*as = spi_master_get_devdata(master);
@@ -554,6 +844,11 @@ static void atmel_spi_dma_unmap_xfer(struct spi_master *master,
 				 xfer->len, DMA_FROM_DEVICE);
 }
 
+static void atmel_spi_disable_pdc_transfer(struct atmel_spi *as)
+{
+	spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
+}
+
 static void
 atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as,
 		struct spi_message *msg, int stay)
@@ -579,19 +874,170 @@ atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as,
 	as->done_status = 0;
 
 	/* continue if needed */
-	if (list_empty(&as->queue) || as->stopping)
-		spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
-	else
+	if (list_empty(&as->queue) || as->stopping) {
+		if (as->use_pdc)
+			atmel_spi_disable_pdc_transfer(as);
+	} else
 		atmel_spi_next_message(master);
 }
 
-static irqreturn_t
-atmel_spi_interrupt(int irq, void *dev_id)
+/* Called from IRQ
+ * lock is held
+ *
+ * Must update "current_remaining_bytes" to keep track of data
+ * to transfer.
+ */
+static void
+atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
 {
-	struct spi_master	*master = dev_id;
+	u8		*txp;
+	u8		*rxp;
+	unsigned long	xfer_pos = xfer->len - as->current_remaining_bytes;
+
+	if (xfer->rx_buf) {
+		rxp = ((u8 *)xfer->rx_buf) + xfer_pos;
+		*rxp = spi_readl(as, RDR);
+	} else {
+		spi_readl(as, RDR);
+	}
+
+	as->current_remaining_bytes--;
+
+	if (as->current_remaining_bytes) {
+		if (xfer->tx_buf) {
+			txp = ((u8 *)xfer->tx_buf) + xfer_pos + 1;
+			spi_writel(as, TDR, *txp);
+		} else {
+			spi_writel(as, TDR, 0);
+		}
+	}
+}
+
+/* Tasklet
+ * Called from DMA callback + pio transfer and overrun IRQ.
+ */
+static void atmel_spi_tasklet_func(unsigned long data)
+{
+	struct spi_master	*master = (struct spi_master *)data;
 	struct atmel_spi	*as = spi_master_get_devdata(master);
 	struct spi_message	*msg;
 	struct spi_transfer	*xfer;
+
+	dev_vdbg(master->dev.parent, "atmel_spi_tasklet_func\n");
+
+	atmel_spi_lock(as);
+
+	xfer = as->current_transfer;
+
+	if (xfer == NULL)
+		/* already been there */
+		goto tasklet_out;
+
+	msg = list_entry(as->queue.next, struct spi_message, queue);
+
+	if (as->done_status < 0) {
+		/* error happened (overrun) */
+		if (atmel_spi_use_dma(as, xfer))
+			atmel_spi_stop_dma(as);
+	} else {
+		/* only update length if no error */
+		msg->actual_length += xfer->len;
+	}
+
+	if (atmel_spi_use_dma(as, xfer)) {
+		if (!msg->is_dma_mapped)
+			atmel_spi_dma_unmap_xfer(master, xfer);
+	}
+
+	if (xfer->delay_usecs)
+		udelay(xfer->delay_usecs);
+
+	if (atmel_spi_xfer_is_last(msg, xfer) || as->done_status < 0) {
+		/* report completed (or erroneous) message */
+		atmel_spi_msg_done(master, as, msg, xfer->cs_change);
+	} else {
+		if (xfer->cs_change) {
+			cs_deactivate(as, msg->spi);
+			udelay(1);
+			cs_activate(as, msg->spi);
+		}
+
+		/*
+		 * Not done yet. Submit the next transfer.
+		 *
+		 * FIXME handle protocol options for xfer
+		 */
+		atmel_spi_next_xfer(master, msg);
+	}
+
+tasklet_out:
+	atmel_spi_unlock(as);
+}
+
+static int atmel_spi_interrupt_dma(struct atmel_spi *as,
+				struct spi_master *master)
+{
+	u32			status, pending, imr;
+	struct spi_transfer	*xfer;
+	int			ret = IRQ_NONE;
+
+	imr = spi_readl(as, IMR);
+	status = spi_readl(as, SR);
+	pending = status & imr;
+
+	if (pending & SPI_BIT(OVRES)) {
+		ret = IRQ_HANDLED;
+		spi_writel(as, IDR, SPI_BIT(OVRES));
+		dev_warn(master->dev.parent, "overrun\n");
+
+		/*
+		 * When we get an overrun, we disregard the current
+		 * transfer. Data will not be copied back from any
+		 * bounce buffer and msg->actual_len will not be
+		 * updated with the last xfer.
+		 *
+		 * We will also not process any remaning transfers in
+		 * the message.
+		 *
+		 * All actions are done in tasklet with done_status indication
+		 */
+		as->done_status = -EIO;
+		smp_wmb();
+
+		/* Clear any overrun happening while cleaning up */
+		spi_readl(as, SR);
+
+		tasklet_schedule(&as->tasklet);
+
+	} else if (pending & SPI_BIT(RDRF)) {
+		atmel_spi_lock(as);
+
+		if (as->current_remaining_bytes) {
+			ret = IRQ_HANDLED;
+			xfer = as->current_transfer;
+			atmel_spi_pump_pio_data(as, xfer);
+			if (!as->current_remaining_bytes) {
+				/* no more data to xfer, kick tasklet */
+				spi_writel(as, IDR, pending);
+				tasklet_schedule(&as->tasklet);
+			}
+		}
+
+		atmel_spi_unlock(as);
+	} else {
+		WARN_ONCE(pending, "IRQ not handled, pending = %x\n", pending);
+		ret = IRQ_HANDLED;
+		spi_writel(as, IDR, pending);
+	}
+
+	return ret;
+}
+
+static int atmel_spi_interrupt_pdc(struct atmel_spi *as,
+				struct spi_master *master)
+{
+	struct spi_message	*msg;
+	struct spi_transfer	*xfer;
 	u32			status, pending, imr;
 	int			ret = IRQ_NONE;
 
@@ -703,6 +1149,27 @@ atmel_spi_interrupt(int irq, void *dev_id)
 	return ret;
 }
 
+/* Interrupt
+ *
+ * No need for locking in this Interrupt handler: done_status is the
+ * only information modified. What we need is the update of this field
+ * before tasklet runs. This is ensured by using barrier.
+ */
+static irqreturn_t
+atmel_spi_interrupt(int irq, void *dev_id)
+{
+	struct spi_master	*master = dev_id;
+	struct atmel_spi	*as = spi_master_get_devdata(master);
+	int ret;
+
+	if (as->use_pdc)
+		ret = atmel_spi_interrupt_pdc(as, master);
+	else
+		ret = atmel_spi_interrupt_dma(as, master);
+
+	return ret;
+}
+
 static int atmel_spi_setup(struct spi_device *spi)
 {
 	struct atmel_spi	*as;
@@ -861,13 +1328,11 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg)
 
 		/*
 		 * DMA map early, for performance (empties dcache ASAP) and
-		 * better fault reporting.  This is a DMA-only driver.
-		 *
-		 * NOTE that if dma_unmap_single() ever starts to do work on
-		 * platforms supported by this driver, we would need to clean
-		 * up mappings for previously-mapped transfers.
+		 * better fault reporting.
 		 */
-		if (!msg->is_dma_mapped) {
+		if (!msg->is_dma_mapped
+			&& (atmel_spi_use_dma(as, xfer)
+				|| atmel_spi_use_pdc(as))) {
 			if (atmel_spi_dma_map_xfer(as, xfer) < 0)
 				return -ENOMEM;
 		}
@@ -968,6 +1433,8 @@ static int __devinit atmel_spi_probe(struct platform_device *pdev)
 
 	spin_lock_init(&as->lock);
 	INIT_LIST_HEAD(&as->queue);
+	tasklet_init(&as->tasklet, atmel_spi_tasklet_func,
+					(unsigned long)master);
 	as->pdev = pdev;
 	as->regs = ioremap(regs->start, resource_size(regs));
 	if (!as->regs)
@@ -986,7 +1453,16 @@ static int __devinit atmel_spi_probe(struct platform_device *pdev)
 	spi_writel(as, CR, SPI_BIT(SWRST));
 	spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */
 	spi_writel(as, MR, SPI_BIT(MSTR) | SPI_BIT(MODFDIS));
-	spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
+
+	as->use_dma = false;
+	as->use_pdc = false;
+
+	if (as->data.dma_type == 2) {
+		if (atmel_spi_configure_dma(as) == 0)
+			as->use_dma = true;
+	} else if (as->data.dma_type == 1)
+		as->use_pdc = true;
+
 	spi_writel(as, CR, SPI_BIT(SPIEN));
 
 	/* go! */
@@ -995,11 +1471,14 @@ static int __devinit atmel_spi_probe(struct platform_device *pdev)
 
 	ret = spi_register_master(master);
 	if (ret)
-		goto out_reset_hw;
+		goto out_free_dma;
 
 	return 0;
 
-out_reset_hw:
+out_free_dma:
+	if (as->use_dma)
+		atmel_spi_release_dma(as);
+
 	spi_writel(as, CR, SPI_BIT(SWRST));
 	spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */
 	clk_disable(clk);
@@ -1007,6 +1486,7 @@ out_reset_hw:
 out_unmap_regs:
 	iounmap(as->regs);
 out_free_buffer:
+	tasklet_kill(&as->tasklet);
 	dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer,
 			as->buffer_dma);
 out_free:
@@ -1025,6 +1505,11 @@ static int __devexit atmel_spi_remove(struct platform_device *pdev)
 	/* reset the hardware and block queue progress */
 	spin_lock_irq(&as->lock);
 	as->stopping = 1;
+	if (as->use_dma) {
+		atmel_spi_stop_dma(as);
+		atmel_spi_release_dma(as);
+	}
+
 	spi_writel(as, CR, SPI_BIT(SWRST));
 	spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */
 	spi_readl(as, SR);
@@ -1033,13 +1518,16 @@ static int __devexit atmel_spi_remove(struct platform_device *pdev)
 	/* Terminate remaining queued transfers */
 	list_for_each_entry(msg, &as->queue, queue) {
 		list_for_each_entry(xfer, &msg->transfers, transfer_list) {
-			if (!msg->is_dma_mapped)
+			if (!msg->is_dma_mapped
+				&& (atmel_spi_use_dma(as, xfer)
+					|| atmel_spi_use_pdc(as)))
 				atmel_spi_dma_unmap_xfer(master, xfer);
 		}
 		msg->status = -ESHUTDOWN;
 		msg->complete(msg->context);
 	}
 
+	tasklet_kill(&as->tasklet);
 	dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer,
 			as->buffer_dma);
 
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 06/17] spi/atmel_spi: add flag to controller data for lock operations
From: Wenyou Yang @ 2012-11-12  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352710357-3265-1-git-send-email-wenyou.yang@atmel.com>

From: Nicolas Ferre <nicolas.ferre@atmel.com>

Will allow to drop the lock during DMA operations.

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: grant.likely at secretlab.ca
Cc: spi-devel-general at lists.sourceforge.net
---
 drivers/spi/spi-atmel.c |   31 +++++++++++++++++++------------
 1 file changed, 19 insertions(+), 12 deletions(-)

diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 76a1baf..37f54c3 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -187,6 +187,7 @@
  */
 struct atmel_spi {
 	spinlock_t		lock;
+	unsigned long		flags;
 
 	resource_size_t		phybase;
 	void __iomem		*regs;
@@ -323,6 +324,16 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi)
 		gpio_set_value(asd->npcs_pin, !active);
 }
 
+static void atmel_spi_lock(struct atmel_spi *as)
+{
+		spin_lock_irqsave(&as->lock, as->flags);
+}
+
+static void atmel_spi_unlock(struct atmel_spi *as)
+{
+		spin_unlock_irqrestore(&as->lock, as->flags);
+}
+
 static inline int atmel_spi_xfer_is_last(struct spi_message *msg,
 					struct spi_transfer *xfer)
 {
@@ -559,9 +570,9 @@ atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as,
 		"xfer complete: %u bytes transferred\n",
 		msg->actual_length);
 
-	spin_unlock(&as->lock);
+	atmel_spi_unlock(as);
 	msg->complete(msg->context);
-	spin_lock(&as->lock);
+	atmel_spi_lock(as);
 
 	as->current_transfer = NULL;
 	as->next_transfer = NULL;
@@ -788,13 +799,11 @@ static int atmel_spi_setup(struct spi_device *spi)
 		spi->controller_state = asd;
 		gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH));
 	} else {
-		unsigned long		flags;
-
-		spin_lock_irqsave(&as->lock, flags);
+		atmel_spi_lock(as);
 		if (as->stay == spi)
 			as->stay = NULL;
 		cs_deactivate(as, spi);
-		spin_unlock_irqrestore(&as->lock, flags);
+		atmel_spi_unlock(as);
 	}
 
 	asd->csr = csr;
@@ -813,7 +822,6 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg)
 {
 	struct atmel_spi	*as;
 	struct spi_transfer	*xfer;
-	unsigned long		flags;
 	struct device		*controller = spi->master->dev.parent;
 	u8			bits;
 	struct atmel_spi_device	*asd;
@@ -878,11 +886,11 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg)
 	msg->status = -EINPROGRESS;
 	msg->actual_length = 0;
 
-	spin_lock_irqsave(&as->lock, flags);
+	atmel_spi_lock(as);
 	list_add_tail(&msg->queue, &as->queue);
 	if (!as->current_transfer)
 		atmel_spi_next_message(spi->master);
-	spin_unlock_irqrestore(&as->lock, flags);
+	atmel_spi_unlock(as);
 
 	return 0;
 }
@@ -892,17 +900,16 @@ static void atmel_spi_cleanup(struct spi_device *spi)
 	struct atmel_spi	*as = spi_master_get_devdata(spi->master);
 	struct atmel_spi_device	*asd = spi->controller_state;
 	unsigned		gpio = (unsigned) spi->controller_data;
-	unsigned long		flags;
 
 	if (!asd)
 		return;
 
-	spin_lock_irqsave(&as->lock, flags);
+	atmel_spi_lock(as);
 	if (as->stay == spi) {
 		as->stay = NULL;
 		cs_deactivate(as, spi);
 	}
-	spin_unlock_irqrestore(&as->lock, flags);
+	atmel_spi_unlock(as);
 
 	spi->controller_state = NULL;
 	gpio_free(gpio);
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 05/17] spi/atmel_spi: status information passed through controller data
From: Wenyou Yang @ 2012-11-12  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352710357-3265-1-git-send-email-wenyou.yang@atmel.com>

From: Nicolas Ferre <nicolas.ferre@atmel.com>

The status of transfer is stored in controller data structure
so that it can be used not only by atmel_spi_msg_done() function.
This will be useful for upcoming dmaengine enabled driver.

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: grant.likely at secretlab.ca
Cc: spi-devel-general at lists.sourceforge.net
---
 drivers/spi/spi-atmel.c |   13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index fc1222a..76a1baf 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -201,6 +201,7 @@ struct atmel_spi {
 	unsigned long		current_remaining_bytes;
 	struct spi_transfer	*next_transfer;
 	unsigned long		next_remaining_bytes;
+	int			done_status;
 
 	void			*buffer;
 	dma_addr_t		buffer_dma;
@@ -544,15 +545,15 @@ static void atmel_spi_dma_unmap_xfer(struct spi_master *master,
 
 static void
 atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as,
-		struct spi_message *msg, int status, int stay)
+		struct spi_message *msg, int stay)
 {
-	if (!stay || status < 0)
+	if (!stay || as->done_status < 0)
 		cs_deactivate(as, msg->spi);
 	else
 		as->stay = msg->spi;
 
 	list_del(&msg->queue);
-	msg->status = status;
+	msg->status = as->done_status;
 
 	dev_dbg(master->dev.parent,
 		"xfer complete: %u bytes transferred\n",
@@ -564,6 +565,7 @@ atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as,
 
 	as->current_transfer = NULL;
 	as->next_transfer = NULL;
+	as->done_status = 0;
 
 	/* continue if needed */
 	if (list_empty(&as->queue) || as->stopping)
@@ -641,7 +643,8 @@ atmel_spi_interrupt(int irq, void *dev_id)
 		/* Clear any overrun happening while cleaning up */
 		spi_readl(as, SR);
 
-		atmel_spi_msg_done(master, as, msg, -EIO, 0);
+		as->done_status = -EIO;
+		atmel_spi_msg_done(master, as, msg, 0);
 	} else if (pending & (SPI_BIT(RXBUFF) | SPI_BIT(ENDRX))) {
 		ret = IRQ_HANDLED;
 
@@ -659,7 +662,7 @@ atmel_spi_interrupt(int irq, void *dev_id)
 
 			if (atmel_spi_xfer_is_last(msg, xfer)) {
 				/* report completed message */
-				atmel_spi_msg_done(master, as, msg, 0,
+				atmel_spi_msg_done(master, as, msg,
 						xfer->cs_change);
 			} else {
 				if (xfer->cs_change) {
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 04/17] spi/atmel_spi: call unmapping on transfers buffers
From: Wenyou Yang @ 2012-11-12  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352710357-3265-1-git-send-email-wenyou.yang@atmel.com>

From: Nicolas Ferre <nicolas.ferre@atmel.com>

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: grant.likely at secretlab.ca
Cc: spi-devel-general at lists.sourceforge.net
---
 drivers/spi/spi-atmel.c |    8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 2e040be..fc1222a 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -1010,6 +1010,7 @@ static int __devexit atmel_spi_remove(struct platform_device *pdev)
 	struct spi_master	*master = platform_get_drvdata(pdev);
 	struct atmel_spi	*as = spi_master_get_devdata(master);
 	struct spi_message	*msg;
+	struct spi_transfer	*xfer;
 
 	/* reset the hardware and block queue progress */
 	spin_lock_irq(&as->lock);
@@ -1021,9 +1022,10 @@ static int __devexit atmel_spi_remove(struct platform_device *pdev)
 
 	/* Terminate remaining queued transfers */
 	list_for_each_entry(msg, &as->queue, queue) {
-		/* REVISIT unmapping the dma is a NOP on ARM and AVR32
-		 * but we shouldn't depend on that...
-		 */
+		list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+			if (!msg->is_dma_mapped)
+				atmel_spi_dma_unmap_xfer(master, xfer);
+		}
 		msg->status = -ESHUTDOWN;
 		msg->complete(msg->context);
 	}
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 03/17] spi/atmel_spi: add physical base address
From: Wenyou Yang @ 2012-11-12  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1352710357-3265-1-git-send-email-wenyou.yang@atmel.com>

From: Nicolas Ferre <nicolas.ferre@atmel.com>

Needed for future use with dmaengine enabled driver.

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: grant.likely at secretlab.ca
Cc: spi-devel-general at lists.sourceforge.net
---
 drivers/spi/spi-atmel.c |    2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 16d6a83..2e040be 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -188,6 +188,7 @@
 struct atmel_spi {
 	spinlock_t		lock;
 
+	resource_size_t		phybase;
 	void __iomem		*regs;
 	int			irq;
 	struct clk		*clk;
@@ -961,6 +962,7 @@ static int __devinit atmel_spi_probe(struct platform_device *pdev)
 	as->regs = ioremap(regs->start, resource_size(regs));
 	if (!as->regs)
 		goto out_free_buffer;
+	as->phybase = regs->start;
 	as->irq = irq;
 	as->clk = clk;
 
-- 
1.7.9.5

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox