Linux Input/HID development
 help / color / mirror / Atom feed
* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Chen Gang @ 2014-07-17 11:32 UTC (permalink / raw)
  To: Richard Weinberger, Arnd Bergmann
  Cc: Guenter Roeck, Lars-Peter Clausen, Greg Kroah-Hartman,
	dmitry.torokhov, linux-iio, Benjamin Herrenschmidt, teg,
	Thierry Reding, Lennox Wu, Marek Vasut, Liqin Chen, msalter,
	linux-pwm, devel, linux-watchdog, linux-input,
	linux-kernel@vger.kernel.org, knaack.h, Martin Schwidefsky,
	Mischa.Jonker, jic23, Geert Uytterhoeven
In-Reply-To: <53C7AC6F.1070001@nod.at>



On 07/17/2014 06:58 PM, Richard Weinberger wrote:
> Am 17.07.2014 12:28, schrieb Arnd Bergmann:
>> On Thursday 17 July 2014 11:26:57 Richard Weinberger wrote:
>>> Am 17.07.2014 11:20, schrieb Arnd Bergmann:
>>>> On Thursday 17 July 2014 09:27:58 Chen Gang wrote:
>>>>>                                          gfp_t gfp_mask, unsigned int order);
>>>>>  extern void devm_free_pages(struct device *dev, unsigned long addr);
>>>>>  
>>>>> +#ifdef CONFIG_HAS_IOMEM
>>>>>  void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
>>>>> +#elif defined(CONFIG_COMPILE_TEST)
>>>>> +static inline void __iomem *devm_ioremap_resource(struct device *dev,
>>>>> +                                               struct resource *res)
>>>>> +{
>>>>> +       pr_warn("no hardware io memory, only for COMPILE_TEST\n");
>>>>> +       return (__force void __iomem *)ERR_PTR(-ENXIO);
>>>>> +}
>>>>> +#endif /* CONFIG_HAS_IOMEM || CONFIG_COMPILE_TEST */
>>>>>  
>>>>>  /* allows to add/remove a custom action to devres stack */
>>>>
>>>> To be honest, I think it's a bad idea to introduce wrappers functions
>>>> that are only available when CONFIG_COMPILE_TEST is set.
>>>>
>>>> COMPILE_TEST is a great tool in general, but it has its limits.
>>>> In particular, the case for !CONFIG_IOMEM is completely obscure
>>>> and we won't find any bugs by allowing more drivers to be built
>>>> in those configurations, but attempting to do it would cause
>>>> endless churn by changing each instance of 'depends on HAS_IOMEM'
>>>> to 'depends on HAS_IOMEM || COMPILE_TEST'.
>>>>
>>>> Note that s390 no has gained support for IOMEM, tile has it most
>>>> of the time (when PCI is enabled, so you get it in half the
>>>> test builds already), score should set HAS_IOMEM and doesn't
>>>> even have public compilers, and uml doesn't even compile in
>>>> latest mainline. Nothing else ever sets NO_IOMEM.
>>>
>>> Huh? UML (v3.16-rc5-143-gb6603fe) builds fine here. What build issue are you facing?
>>
>> This is what I got upon trying earlier. I have not attempted to look into
>> why this is happening. Note this is on linux-next from yesterday,
>> not mainline as I incorrectly stated above.
>>
>> In file included from ../arch/um/include/asm/fixmap.h:58:0,
>>                  from ../arch/um/include/asm/pgtable.h:11,
>>                  from ../include/linux/mm.h:51,
>>                  from ../kernel/uid16.c:6:
>> ../include/asm-generic/fixmap.h: In function 'fix_to_virt':
>> ../include/asm-generic/fixmap.h:31:2: error: size of unnamed array is negative
>>   BUILD_BUG_ON(idx >= __end_of_fixed_addresses);
> 
> So, this is next-20140716?
> I don't see the fixmap issue you're reporting, also not on the most recent next.
> 
> All I see is the known ext4 issue with CONFIG_SMP=n:
> 
> fs/ext4/super.c: In function ‘ext4_commit_super’:
> fs/ext4/super.c:4547:41: error: ‘struct percpu_counter’ has no member named ‘counters’
>   if (EXT4_SB(sb)->s_freeclusters_counter.counters)
>                                          ^
> fs/ext4/super.c:4551:39: error: ‘struct percpu_counter’ has no member named ‘counters’
>   if (EXT4_SB(sb)->s_freeinodes_counter.counters)
> 

Yeah, and I have notified to ext4 related maintainer, yesterday, and he
has already send fix patch for it, I guess it will be integrated into
main line soon.


Thanks.
-- 
Chen Gang

Open, share, and attitude like air, water, and life which God blessed
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Chen Gang @ 2014-07-17 11:28 UTC (permalink / raw)
  To: Arnd Bergmann, Lars-Peter Clausen
  Cc: Guenter Roeck, Richard Weinberger, Greg Kroah-Hartman,
	dmitry.torokhov, linux-iio, Benjamin Herrenschmidt, teg,
	Thierry Reding, Lennox Wu, Marek Vasut, Liqin Chen, msalter,
	linux-pwm, devel, linux-watchdog, linux-input,
	linux-kernel@vger.kernel.org, knaack.h, Martin Schwidefsky,
	Mischa.Jonker, jic23, Geert Uytterhoeven, cmetcalf
In-Reply-To: <6485666.LVFO2YCEQx@wuerfel>

On 07/17/2014 06:48 PM, Arnd Bergmann wrote:
> On Thursday 17 July 2014 12:40:25 Lars-Peter Clausen wrote:
>> On 07/17/2014 11:20 AM, Arnd Bergmann wrote:
>>> On Thursday 17 July 2014 09:27:58 Chen Gang wrote:
>>>>                                           gfp_t gfp_mask, unsigned int order);
>>>>   extern void devm_free_pages(struct device *dev, unsigned long addr);
>>>>
>>>> +#ifdef CONFIG_HAS_IOMEM
>>>>   void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
>>>> +#elif defined(CONFIG_COMPILE_TEST)
>>>> +static inline void __iomem *devm_ioremap_resource(struct device *dev,
>>>> +                                               struct resource *res)
>>>> +{
>>>> +       pr_warn("no hardware io memory, only for COMPILE_TEST\n");
>>>> +       return (__force void __iomem *)ERR_PTR(-ENXIO);
>>>> +}
>>>> +#endif /* CONFIG_HAS_IOMEM || CONFIG_COMPILE_TEST */
>>>>
>>>>   /* allows to add/remove a custom action to devres stack */
>>>
>>> To be honest, I think it's a bad idea to introduce wrappers functions
>>> that are only available when CONFIG_COMPILE_TEST is set.
>>>
>>> COMPILE_TEST is a great tool in general, but it has its limits.
>>> In particular, the case for !CONFIG_IOMEM is completely obscure
>>> and we won't find any bugs by allowing more drivers to be built
>>> in those configurations, but attempting to do it would cause
>>> endless churn by changing each instance of 'depends on HAS_IOMEM'
>>> to 'depends on HAS_IOMEM || COMPILE_TEST'.
>>
>> The point of this exercise is that we do not have to replace a good chunk of 
>> 'depends on COMPILE_TEST' with 'depends on COMPILE_TEST && HAS_IOMEM'
> 
> Ok, I see.
> 
>> E.g. the typical Kconfig entry for your random SoC peripheral driver looks like
>>
>> config ARCH_FOOBAR_DRIVER
>>         depends on ARCH_FOOBAR || COMPILE_TEST
>>         ...
>>
>> Now when COMPILE_TEST is not set there is a implicit dependency on HAS_IOMEM 
>> since the architecture will provide it. If COMPILE_TEST is selected the 
>> driver will also be build-able on architectures that do no have HAS_IOMEM 
>> and hence linking the driver fails. One way to fix this is of course to 
>> replace the COMPILE_TEST with (COMPILE_TEST && HAS_IOMEM). But this is very 
>> often overlooked and only noticed later on when somebody actually builds a 
>> allyesconfig on an architecture that does not provide HAS_IOMEM. To avoid 
>> these kinds of build errors and tedious fixup patches the idea is to provide 
>> a stub function when HAS_IOMEM is not enabled, but COMPILE_TEST is enabled.
> 
> AFAICT, NO_IOMEM only has a real purpose on UML these days. Could we take
> a shortcut here and make COMPILE_TEST depend on !UML? Getting random stuff
> to build on UML seems pointless to me and we special-case it in a number of
> places already.
> 

According to current source code, tile still has chance to choose
NO_IOMEM, for me, welcome the tile's maintainer's ideas or suggestions.


Thanks.
-- 
Chen Gang

Open, share, and attitude like air, water, and life which God blessed

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Arnd Bergmann @ 2014-07-17 11:24 UTC (permalink / raw)
  To: Richard Weinberger
  Cc: linux-iio, Benjamin Herrenschmidt, teg, Thierry Reding, Lennox Wu,
	linux-watchdog, Marek Vasut, Liqin Chen, Lars-Peter Clausen,
	Geert Uytterhoeven, msalter, Guenter Roeck, linux-pwm, devel,
	Chen Gang, linux-input, Greg Kroah-Hartman, dmitry.torokhov,
	linux-kernel@vger.kernel.org, knaack.h, Martin Schwidefsky,
	Mischa.Jonker, jic23
In-Reply-To: <53C7AC6F.1070001@nod.at>

On Thursday 17 July 2014 12:58:55 Richard Weinberger wrote:
> > This is what I got upon trying earlier. I have not attempted to look into
> > why this is happening. Note this is on linux-next from yesterday,
> > not mainline as I incorrectly stated above.
> > 
> > In file included from ../arch/um/include/asm/fixmap.h:58:0,
> >                  from ../arch/um/include/asm/pgtable.h:11,
> >                  from ../include/linux/mm.h:51,
> >                  from ../kernel/uid16.c:6:
> > ../include/asm-generic/fixmap.h: In function 'fix_to_virt':
> > ../include/asm-generic/fixmap.h:31:2: error: size of unnamed array is negative
> >   BUILD_BUG_ON(idx >= __end_of_fixed_addresses);
> 
> So, this is next-20140716?
> I don't see the fixmap issue you're reporting, also not on the most recent next.

Sorry, nevermind. I had a workaround for a gcc-4.9 bug applied that
turned off optimization for uid16.c, which fixed the build for ARM for
me but happened to break x86 including uml.

Without that patch, I don't see this problem.

	Arnd

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Chen Gang @ 2014-07-17 11:20 UTC (permalink / raw)
  To: Thierry Reding, Arnd Bergmann
  Cc: Guenter Roeck, Richard Weinberger, Lars-Peter Clausen,
	Greg Kroah-Hartman, dmitry.torokhov, linux-iio,
	Benjamin Herrenschmidt, teg, Lennox Wu, Marek Vasut, Liqin Chen,
	msalter, linux-pwm, devel, linux-watchdog, linux-input,
	linux-kernel@vger.kernel.org, knaack.h, Martin Schwidefsky,
	Mischa.Jonker, jic23, Geert Uytterhoeven
In-Reply-To: <20140717105549.GH17877@ulmo>



On 07/17/2014 06:55 PM, Thierry Reding wrote:
> On Thu, Jul 17, 2014 at 12:33:32PM +0200, Arnd Bergmann wrote:
>> On Thursday 17 July 2014 11:56:58 Thierry Reding wrote:
>>> On Thu, Jul 17, 2014 at 11:20:36AM +0200, Arnd Bergmann wrote:
>>> [...]
>>>>                       score should set HAS_IOMEM and doesn't
>>>> even have public compilers
>>>
>>> This begs an interesting question. Should it be made a requirement to
>>> have publicly available compilers for new architectures so that they can
>>> at least be compile-tested? Preferably this would of course be in source
>>> form so that there aren't any dependencies on the distribution.
>>
>> The question has come up a few times. I wouldn't mandate that the port
>> has an upstream gcc (you've got to start mainlining one of them first
>> after all), but having compilers available for download should probably be
>> required. It's hard to ask for a particular quality of that gcc port
>> though, or to expect it to stay available online.
>>
>> Where did you find the gcc port for score?
> 
> It's upstream, though marked obsolete and to be removed in the next
> release... =)
> 

For me, I get the latest gcc version and binutils source code, and fix 2
bugs (one for gas, which always generate core dump, the other for gcc
c-decl, fix it together with the other gcc members).

And I only finish compiling raw cross-compiler (--without-headers),
after make some patches, can let score pass allmodconfig.

At present, it seems the score cross-compiler still contents some issue
which I shall try to analyse (it is about link symbols), and maybe need
communicate with gcc/binutils members.


At present, the related score maintainers are still active in upstream
kernel, so we also need the related maintainers' ideas and suggestions.


Thanks
-- 
Chen Gang

Open, share, and attitude like air, water, and life which God blessed

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Richard Weinberger @ 2014-07-17 10:58 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Chen Gang, Guenter Roeck, Lars-Peter Clausen, Greg Kroah-Hartman,
	dmitry.torokhov, linux-iio, Benjamin Herrenschmidt, teg,
	Thierry Reding, Lennox Wu, Marek Vasut, Liqin Chen, msalter,
	linux-pwm, devel, linux-watchdog, linux-input,
	linux-kernel@vger.kernel.org, knaack.h, Martin Schwidefsky,
	Mischa.Jonker, jic23, Geert Uytterhoeven
In-Reply-To: <8453588.VVyODXRso0@wuerfel>

Am 17.07.2014 12:28, schrieb Arnd Bergmann:
> On Thursday 17 July 2014 11:26:57 Richard Weinberger wrote:
>> Am 17.07.2014 11:20, schrieb Arnd Bergmann:
>>> On Thursday 17 July 2014 09:27:58 Chen Gang wrote:
>>>>                                          gfp_t gfp_mask, unsigned int order);
>>>>  extern void devm_free_pages(struct device *dev, unsigned long addr);
>>>>  
>>>> +#ifdef CONFIG_HAS_IOMEM
>>>>  void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
>>>> +#elif defined(CONFIG_COMPILE_TEST)
>>>> +static inline void __iomem *devm_ioremap_resource(struct device *dev,
>>>> +                                               struct resource *res)
>>>> +{
>>>> +       pr_warn("no hardware io memory, only for COMPILE_TEST\n");
>>>> +       return (__force void __iomem *)ERR_PTR(-ENXIO);
>>>> +}
>>>> +#endif /* CONFIG_HAS_IOMEM || CONFIG_COMPILE_TEST */
>>>>  
>>>>  /* allows to add/remove a custom action to devres stack */
>>>
>>> To be honest, I think it's a bad idea to introduce wrappers functions
>>> that are only available when CONFIG_COMPILE_TEST is set.
>>>
>>> COMPILE_TEST is a great tool in general, but it has its limits.
>>> In particular, the case for !CONFIG_IOMEM is completely obscure
>>> and we won't find any bugs by allowing more drivers to be built
>>> in those configurations, but attempting to do it would cause
>>> endless churn by changing each instance of 'depends on HAS_IOMEM'
>>> to 'depends on HAS_IOMEM || COMPILE_TEST'.
>>>
>>> Note that s390 no has gained support for IOMEM, tile has it most
>>> of the time (when PCI is enabled, so you get it in half the
>>> test builds already), score should set HAS_IOMEM and doesn't
>>> even have public compilers, and uml doesn't even compile in
>>> latest mainline. Nothing else ever sets NO_IOMEM.
>>
>> Huh? UML (v3.16-rc5-143-gb6603fe) builds fine here. What build issue are you facing?
> 
> This is what I got upon trying earlier. I have not attempted to look into
> why this is happening. Note this is on linux-next from yesterday,
> not mainline as I incorrectly stated above.
> 
> In file included from ../arch/um/include/asm/fixmap.h:58:0,
>                  from ../arch/um/include/asm/pgtable.h:11,
>                  from ../include/linux/mm.h:51,
>                  from ../kernel/uid16.c:6:
> ../include/asm-generic/fixmap.h: In function 'fix_to_virt':
> ../include/asm-generic/fixmap.h:31:2: error: size of unnamed array is negative
>   BUILD_BUG_ON(idx >= __end_of_fixed_addresses);

So, this is next-20140716?
I don't see the fixmap issue you're reporting, also not on the most recent next.

All I see is the known ext4 issue with CONFIG_SMP=n:

fs/ext4/super.c: In function ‘ext4_commit_super’:
fs/ext4/super.c:4547:41: error: ‘struct percpu_counter’ has no member named ‘counters’
  if (EXT4_SB(sb)->s_freeclusters_counter.counters)
                                         ^
fs/ext4/super.c:4551:39: error: ‘struct percpu_counter’ has no member named ‘counters’
  if (EXT4_SB(sb)->s_freeinodes_counter.counters)

Thanks,
//richard

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Thierry Reding @ 2014-07-17 10:55 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-iio, Benjamin Herrenschmidt, teg, Lennox Wu, linux-watchdog,
	Marek Vasut, Liqin Chen, Lars-Peter Clausen, Richard Weinberger,
	Geert Uytterhoeven, msalter, Guenter Roeck, linux-pwm, devel,
	Chen Gang, linux-input, Greg Kroah-Hartman, dmitry.torokhov,
	linux-kernel@vger.kernel.org, knaack.h, Martin Schwidefsky,
	Mischa.Jonker, jic23
In-Reply-To: <6350114.L77bTv76Ul@wuerfel>


[-- Attachment #1.1: Type: text/plain, Size: 1111 bytes --]

On Thu, Jul 17, 2014 at 12:33:32PM +0200, Arnd Bergmann wrote:
> On Thursday 17 July 2014 11:56:58 Thierry Reding wrote:
> > On Thu, Jul 17, 2014 at 11:20:36AM +0200, Arnd Bergmann wrote:
> > [...]
> > >                       score should set HAS_IOMEM and doesn't
> > > even have public compilers
> > 
> > This begs an interesting question. Should it be made a requirement to
> > have publicly available compilers for new architectures so that they can
> > at least be compile-tested? Preferably this would of course be in source
> > form so that there aren't any dependencies on the distribution.
> 
> The question has come up a few times. I wouldn't mandate that the port
> has an upstream gcc (you've got to start mainlining one of them first
> after all), but having compilers available for download should probably be
> required. It's hard to ask for a particular quality of that gcc port
> though, or to expect it to stay available online.
> 
> Where did you find the gcc port for score?

It's upstream, though marked obsolete and to be removed in the next
release... =)

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 169 bytes --]

_______________________________________________
devel mailing list
devel@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Arnd Bergmann @ 2014-07-17 10:48 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Chen Gang, Guenter Roeck, Richard Weinberger, Greg Kroah-Hartman,
	dmitry.torokhov, linux-iio, Benjamin Herrenschmidt, teg,
	Thierry Reding, Lennox Wu, Marek Vasut, Liqin Chen, msalter,
	linux-pwm, devel, linux-watchdog, linux-input,
	linux-kernel@vger.kernel.org, knaack.h, Martin Schwidefsky,
	Mischa.Jonker, jic23, Geert Uytterhoeven
In-Reply-To: <53C7A819.40403@metafoo.de>

On Thursday 17 July 2014 12:40:25 Lars-Peter Clausen wrote:
> On 07/17/2014 11:20 AM, Arnd Bergmann wrote:
> > On Thursday 17 July 2014 09:27:58 Chen Gang wrote:
> >>                                           gfp_t gfp_mask, unsigned int order);
> >>   extern void devm_free_pages(struct device *dev, unsigned long addr);
> >>
> >> +#ifdef CONFIG_HAS_IOMEM
> >>   void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
> >> +#elif defined(CONFIG_COMPILE_TEST)
> >> +static inline void __iomem *devm_ioremap_resource(struct device *dev,
> >> +                                               struct resource *res)
> >> +{
> >> +       pr_warn("no hardware io memory, only for COMPILE_TEST\n");
> >> +       return (__force void __iomem *)ERR_PTR(-ENXIO);
> >> +}
> >> +#endif /* CONFIG_HAS_IOMEM || CONFIG_COMPILE_TEST */
> >>
> >>   /* allows to add/remove a custom action to devres stack */
> >
> > To be honest, I think it's a bad idea to introduce wrappers functions
> > that are only available when CONFIG_COMPILE_TEST is set.
> >
> > COMPILE_TEST is a great tool in general, but it has its limits.
> > In particular, the case for !CONFIG_IOMEM is completely obscure
> > and we won't find any bugs by allowing more drivers to be built
> > in those configurations, but attempting to do it would cause
> > endless churn by changing each instance of 'depends on HAS_IOMEM'
> > to 'depends on HAS_IOMEM || COMPILE_TEST'.
> 
> The point of this exercise is that we do not have to replace a good chunk of 
> 'depends on COMPILE_TEST' with 'depends on COMPILE_TEST && HAS_IOMEM'

Ok, I see.

> E.g. the typical Kconfig entry for your random SoC peripheral driver looks like
> 
> config ARCH_FOOBAR_DRIVER
>         depends on ARCH_FOOBAR || COMPILE_TEST
>         ...
> 
> Now when COMPILE_TEST is not set there is a implicit dependency on HAS_IOMEM 
> since the architecture will provide it. If COMPILE_TEST is selected the 
> driver will also be build-able on architectures that do no have HAS_IOMEM 
> and hence linking the driver fails. One way to fix this is of course to 
> replace the COMPILE_TEST with (COMPILE_TEST && HAS_IOMEM). But this is very 
> often overlooked and only noticed later on when somebody actually builds a 
> allyesconfig on an architecture that does not provide HAS_IOMEM. To avoid 
> these kinds of build errors and tedious fixup patches the idea is to provide 
> a stub function when HAS_IOMEM is not enabled, but COMPILE_TEST is enabled.

AFAICT, NO_IOMEM only has a real purpose on UML these days. Could we take
a shortcut here and make COMPILE_TEST depend on !UML? Getting random stuff
to build on UML seems pointless to me and we special-case it in a number of
places already.

	Arnd

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Lars-Peter Clausen @ 2014-07-17 10:40 UTC (permalink / raw)
  To: Arnd Bergmann, Chen Gang
  Cc: Guenter Roeck, Richard Weinberger, Greg Kroah-Hartman,
	dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Benjamin Herrenschmidt,
	teg-B22kvLQNl6c, Thierry Reding, Lennox Wu, Marek Vasut,
	Liqin Chen, msalter-H+wXaHxf7aLQT0dZR+AlfA,
	linux-pwm-u79uwXL29TY76Z2rM5mHXA,
	devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	knaack.h-Mmb7MZpHnFY, Martin Schwidefsky,
	Mischa.Jonker-HKixBCOQz3hWk0Htik3J/w,
	jic23-DgEjT+Ai2ygdnm+yROfE0A, Geert Uytterhoeven
In-Reply-To: <6823014.2plXDE9VA9@wuerfel>

On 07/17/2014 11:20 AM, Arnd Bergmann wrote:
> On Thursday 17 July 2014 09:27:58 Chen Gang wrote:
>>                                           gfp_t gfp_mask, unsigned int order);
>>   extern void devm_free_pages(struct device *dev, unsigned long addr);
>>
>> +#ifdef CONFIG_HAS_IOMEM
>>   void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
>> +#elif defined(CONFIG_COMPILE_TEST)
>> +static inline void __iomem *devm_ioremap_resource(struct device *dev,
>> +                                               struct resource *res)
>> +{
>> +       pr_warn("no hardware io memory, only for COMPILE_TEST\n");
>> +       return (__force void __iomem *)ERR_PTR(-ENXIO);
>> +}
>> +#endif /* CONFIG_HAS_IOMEM || CONFIG_COMPILE_TEST */
>>
>>   /* allows to add/remove a custom action to devres stack */
>
> To be honest, I think it's a bad idea to introduce wrappers functions
> that are only available when CONFIG_COMPILE_TEST is set.
>
> COMPILE_TEST is a great tool in general, but it has its limits.
> In particular, the case for !CONFIG_IOMEM is completely obscure
> and we won't find any bugs by allowing more drivers to be built
> in those configurations, but attempting to do it would cause
> endless churn by changing each instance of 'depends on HAS_IOMEM'
> to 'depends on HAS_IOMEM || COMPILE_TEST'.

The point of this exercise is that we do not have to replace a good chunk of 
'depends on COMPILE_TEST' with 'depends on COMPILE_TEST && HAS_IOMEM'

E.g. the typical Kconfig entry for your random SoC peripheral driver looks like

config ARCH_FOOBAR_DRIVER
	depends on ARCH_FOOBAR || COMPILE_TEST
	...

Now when COMPILE_TEST is not set there is a implicit dependency on HAS_IOMEM 
since the architecture will provide it. If COMPILE_TEST is selected the 
driver will also be build-able on architectures that do no have HAS_IOMEM 
and hence linking the driver fails. One way to fix this is of course to 
replace the COMPILE_TEST with (COMPILE_TEST && HAS_IOMEM). But this is very 
often overlooked and only noticed later on when somebody actually builds a 
allyesconfig on an architecture that does not provide HAS_IOMEM. To avoid 
these kinds of build errors and tedious fixup patches the idea is to provide 
a stub function when HAS_IOMEM is not enabled, but COMPILE_TEST is enabled.

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Arnd Bergmann @ 2014-07-17 10:38 UTC (permalink / raw)
  To: Chen Gang
  Cc: linux-iio, Benjamin Herrenschmidt, teg, Thierry Reding, Lennox Wu,
	Marek Vasut, Liqin Chen, Lars-Peter Clausen, Richard Weinberger,
	Geert Uytterhoeven, msalter, Guenter Roeck, linux-pwm, devel,
	linux-watchdog, linux-input, Greg Kroah-Hartman, dmitry.torokhov,
	linux-kernel@vger.kernel.org, knaack.h, Martin Schwidefsky,
	Mischa.Jonker, jic23
In-Reply-To: <53C7977B.9090903@gmail.com>

On Thursday 17 July 2014 17:29:31 Chen Gang wrote:
> > 
> > COMPILE_TEST is a great tool in general, but it has its limits.
> > In particular, the case for !CONFIG_IOMEM is completely obscure
> > and we won't find any bugs by allowing more drivers to be built
> > in those configurations, but attempting to do it would cause
> > endless churn by changing each instance of 'depends on HAS_IOMEM'
> > to 'depends on HAS_IOMEM || COMPILE_TEST'.
> >
> 
> Architecture members and driver members really have different tastes,
> they are different roles. It really need additional discussion.
> 
> For me, I only want to change devm_io*map*, not touch so much.

But what do you gain from that? All drivers that need these
functions should already 'depends on HAS_IOMEM' and if they don't,
we should fix /that/ instead. I don't see this dependency as any
different from a lot of others (PCI, DMAENGINE, HAVE_CLK, ...)
that we use to intentionally annotate drivers that need a particular
feature to be present for compilation. Do you want to do the
same hack to those?

> Welcome any other members' idea or suggestions.

> > Note that s390 no has gained support for IOMEM, tile has it most
> > of the time (when PCI is enabled, so you get it in half the
> > test builds already), score should set HAS_IOMEM and doesn't
> > even have public compilers, and uml doesn't even compile in
> > latest mainline. Nothing else ever sets NO_IOMEM.
> > 
> 
> In latest gcc and binutils, can compile score cross compiler
> successfully for building kernel (but I am not quite sure whether the
> compiling result are really OK, but I guess so).

Ok. Would you mind sending a patch that enables HAS_IOMEM on
score?
 
> And next (maybe after finish allmodconfig for microblaze), I shall try
> to let uml pass allmodconfig for linux-next tree.

That is a fair goal, but it seems better to do that by ensuring
we don't build any code that tries to call the MMIO functions
rather than trying to make them build.

	Arnd

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Arnd Bergmann @ 2014-07-17 10:33 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Chen Gang, Guenter Roeck, Richard Weinberger, Lars-Peter Clausen,
	Greg Kroah-Hartman, dmitry.torokhov, linux-iio,
	Benjamin Herrenschmidt, teg, Lennox Wu, Marek Vasut, Liqin Chen,
	msalter, linux-pwm, devel, linux-watchdog, linux-input,
	linux-kernel@vger.kernel.org, knaack.h, Martin Schwidefsky,
	Mischa.Jonker, jic23, Geert Uytterhoeven
In-Reply-To: <20140717095657.GC17877@ulmo>

On Thursday 17 July 2014 11:56:58 Thierry Reding wrote:
> On Thu, Jul 17, 2014 at 11:20:36AM +0200, Arnd Bergmann wrote:
> [...]
> >                       score should set HAS_IOMEM and doesn't
> > even have public compilers
> 
> This begs an interesting question. Should it be made a requirement to
> have publicly available compilers for new architectures so that they can
> at least be compile-tested? Preferably this would of course be in source
> form so that there aren't any dependencies on the distribution.

The question has come up a few times. I wouldn't mandate that the port
has an upstream gcc (you've got to start mainlining one of them first
after all), but having compilers available for download should probably be
required. It's hard to ask for a particular quality of that gcc port
though, or to expect it to stay available online.

Where did you find the gcc port for score?

	Arnd

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Arnd Bergmann @ 2014-07-17 10:28 UTC (permalink / raw)
  To: Richard Weinberger
  Cc: linux-iio, Benjamin Herrenschmidt, teg, Thierry Reding, Lennox Wu,
	linux-watchdog, Marek Vasut, Liqin Chen, Lars-Peter Clausen,
	Geert Uytterhoeven, msalter, Guenter Roeck, linux-pwm, devel,
	Chen Gang, linux-input, Greg Kroah-Hartman, dmitry.torokhov,
	linux-kernel@vger.kernel.org, knaack.h, Martin Schwidefsky,
	Mischa.Jonker, jic23
In-Reply-To: <53C796E1.8080708@nod.at>

On Thursday 17 July 2014 11:26:57 Richard Weinberger wrote:
> Am 17.07.2014 11:20, schrieb Arnd Bergmann:
> > On Thursday 17 July 2014 09:27:58 Chen Gang wrote:
> >>                                          gfp_t gfp_mask, unsigned int order);
> >>  extern void devm_free_pages(struct device *dev, unsigned long addr);
> >>  
> >> +#ifdef CONFIG_HAS_IOMEM
> >>  void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
> >> +#elif defined(CONFIG_COMPILE_TEST)
> >> +static inline void __iomem *devm_ioremap_resource(struct device *dev,
> >> +                                               struct resource *res)
> >> +{
> >> +       pr_warn("no hardware io memory, only for COMPILE_TEST\n");
> >> +       return (__force void __iomem *)ERR_PTR(-ENXIO);
> >> +}
> >> +#endif /* CONFIG_HAS_IOMEM || CONFIG_COMPILE_TEST */
> >>  
> >>  /* allows to add/remove a custom action to devres stack */
> > 
> > To be honest, I think it's a bad idea to introduce wrappers functions
> > that are only available when CONFIG_COMPILE_TEST is set.
> > 
> > COMPILE_TEST is a great tool in general, but it has its limits.
> > In particular, the case for !CONFIG_IOMEM is completely obscure
> > and we won't find any bugs by allowing more drivers to be built
> > in those configurations, but attempting to do it would cause
> > endless churn by changing each instance of 'depends on HAS_IOMEM'
> > to 'depends on HAS_IOMEM || COMPILE_TEST'.
> > 
> > Note that s390 no has gained support for IOMEM, tile has it most
> > of the time (when PCI is enabled, so you get it in half the
> > test builds already), score should set HAS_IOMEM and doesn't
> > even have public compilers, and uml doesn't even compile in
> > latest mainline. Nothing else ever sets NO_IOMEM.
> 
> Huh? UML (v3.16-rc5-143-gb6603fe) builds fine here. What build issue are you facing?

This is what I got upon trying earlier. I have not attempted to look into
why this is happening. Note this is on linux-next from yesterday,
not mainline as I incorrectly stated above.

In file included from ../arch/um/include/asm/fixmap.h:58:0,
                 from ../arch/um/include/asm/pgtable.h:11,
                 from ../include/linux/mm.h:51,
                 from ../kernel/uid16.c:6:
../include/asm-generic/fixmap.h: In function 'fix_to_virt':
../include/asm-generic/fixmap.h:31:2: error: size of unnamed array is negative
  BUILD_BUG_ON(idx >= __end_of_fixed_addresses);


	Arnd

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Thierry Reding @ 2014-07-17  9:56 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-iio, Benjamin Herrenschmidt, teg, Lennox Wu, linux-watchdog,
	Marek Vasut, Liqin Chen, Lars-Peter Clausen, Richard Weinberger,
	Geert Uytterhoeven, msalter, Guenter Roeck, linux-pwm, devel,
	Chen Gang, linux-input, Greg Kroah-Hartman, dmitry.torokhov,
	linux-kernel@vger.kernel.org, knaack.h, Martin Schwidefsky,
	Mischa.Jonker, jic23
In-Reply-To: <6823014.2plXDE9VA9@wuerfel>


[-- Attachment #1.1: Type: text/plain, Size: 451 bytes --]

On Thu, Jul 17, 2014 at 11:20:36AM +0200, Arnd Bergmann wrote:
[...]
>                       score should set HAS_IOMEM and doesn't
> even have public compilers

This begs an interesting question. Should it be made a requirement to
have publicly available compilers for new architectures so that they can
at least be compile-tested? Preferably this would of course be in source
form so that there aren't any dependencies on the distribution.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 169 bytes --]

_______________________________________________
devel mailing list
devel@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Thierry Reding @ 2014-07-17  9:51 UTC (permalink / raw)
  To: Chen Gang
  Cc: Arnd Bergmann, Guenter Roeck, Richard Weinberger,
	Lars-Peter Clausen, Greg Kroah-Hartman, dmitry.torokhov,
	linux-iio, Benjamin Herrenschmidt, teg, Lennox Wu, Marek Vasut,
	Liqin Chen, msalter, linux-pwm, devel, linux-watchdog,
	linux-input, linux-kernel@vger.kernel.org, knaack.h,
	Martin Schwidefsky, Mischa.Jonker, jic23, Geert Uytterhoeven
In-Reply-To: <53C7977B.9090903@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 2645 bytes --]

On Thu, Jul 17, 2014 at 05:29:31PM +0800, Chen Gang wrote:
> 
> 
> On 07/17/2014 05:20 PM, Arnd Bergmann wrote:
> > On Thursday 17 July 2014 09:27:58 Chen Gang wrote:
> >>                                          gfp_t gfp_mask, unsigned int order);
> >>  extern void devm_free_pages(struct device *dev, unsigned long addr);
> >>  
> >> +#ifdef CONFIG_HAS_IOMEM
> >>  void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
> >> +#elif defined(CONFIG_COMPILE_TEST)
> >> +static inline void __iomem *devm_ioremap_resource(struct device *dev,
> >> +                                               struct resource *res)
> >> +{
> >> +       pr_warn("no hardware io memory, only for COMPILE_TEST\n");
> >> +       return (__force void __iomem *)ERR_PTR(-ENXIO);
> >> +}
> >> +#endif /* CONFIG_HAS_IOMEM || CONFIG_COMPILE_TEST */
> >>  
> >>  /* allows to add/remove a custom action to devres stack */
> > 
> > To be honest, I think it's a bad idea to introduce wrappers functions
> > that are only available when CONFIG_COMPILE_TEST is set.
> > 
> > COMPILE_TEST is a great tool in general, but it has its limits.
> > In particular, the case for !CONFIG_IOMEM is completely obscure
> > and we won't find any bugs by allowing more drivers to be built
> > in those configurations, but attempting to do it would cause
> > endless churn by changing each instance of 'depends on HAS_IOMEM'
> > to 'depends on HAS_IOMEM || COMPILE_TEST'.
> >
> 
> Architecture members and driver members really have different tastes,
> they are different roles. It really need additional discussion.
> 
> For me, I only want to change devm_io*map*, not touch so much.
> 
> Welcome any other members' idea or suggestions.
> 
> > Note that s390 no has gained support for IOMEM, tile has it most
> > of the time (when PCI is enabled, so you get it in half the
> > test builds already), score should set HAS_IOMEM and doesn't
> > even have public compilers, and uml doesn't even compile in
> > latest mainline. Nothing else ever sets NO_IOMEM.
> > 
> 
> In latest gcc and binutils, can compile score cross compiler
> successfully for building kernel (but I am not quite sure whether the
> compiling result are really OK, but I guess so).

I was only able to ever build a partial bare-metal toolchain for score.
There's no uClibc, glibc or newlib support, so it becomes rather useless
as a Linux architecture.

Also when I run the cross-compiler on a score defconfig build, I get a
bunch of these:

	score-unknown-elf-gcc-4.9.0: internal compiler error: Segmentation fault (program as)

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Chen Gang @ 2014-07-17  9:29 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Guenter Roeck, Richard Weinberger, Lars-Peter Clausen,
	Greg Kroah-Hartman, dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Benjamin Herrenschmidt,
	teg-B22kvLQNl6c, Thierry Reding, Lennox Wu, Marek Vasut,
	Liqin Chen, msalter-H+wXaHxf7aLQT0dZR+AlfA,
	linux-pwm-u79uwXL29TY76Z2rM5mHXA,
	devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	knaack.h-Mmb7MZpHnFY, Martin Schwidefsky,
	Mischa.Jonker-HKixBCOQz3hWk0Htik3J/w,
	jic23-DgEjT+Ai2ygdnm+yROfE0A, Geert Uytterhoeven
In-Reply-To: <6823014.2plXDE9VA9@wuerfel>



On 07/17/2014 05:20 PM, Arnd Bergmann wrote:
> On Thursday 17 July 2014 09:27:58 Chen Gang wrote:
>>                                          gfp_t gfp_mask, unsigned int order);
>>  extern void devm_free_pages(struct device *dev, unsigned long addr);
>>  
>> +#ifdef CONFIG_HAS_IOMEM
>>  void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
>> +#elif defined(CONFIG_COMPILE_TEST)
>> +static inline void __iomem *devm_ioremap_resource(struct device *dev,
>> +                                               struct resource *res)
>> +{
>> +       pr_warn("no hardware io memory, only for COMPILE_TEST\n");
>> +       return (__force void __iomem *)ERR_PTR(-ENXIO);
>> +}
>> +#endif /* CONFIG_HAS_IOMEM || CONFIG_COMPILE_TEST */
>>  
>>  /* allows to add/remove a custom action to devres stack */
> 
> To be honest, I think it's a bad idea to introduce wrappers functions
> that are only available when CONFIG_COMPILE_TEST is set.
> 
> COMPILE_TEST is a great tool in general, but it has its limits.
> In particular, the case for !CONFIG_IOMEM is completely obscure
> and we won't find any bugs by allowing more drivers to be built
> in those configurations, but attempting to do it would cause
> endless churn by changing each instance of 'depends on HAS_IOMEM'
> to 'depends on HAS_IOMEM || COMPILE_TEST'.
>

Architecture members and driver members really have different tastes,
they are different roles. It really need additional discussion.

For me, I only want to change devm_io*map*, not touch so much.

Welcome any other members' idea or suggestions.

> Note that s390 no has gained support for IOMEM, tile has it most
> of the time (when PCI is enabled, so you get it in half the
> test builds already), score should set HAS_IOMEM and doesn't
> even have public compilers, and uml doesn't even compile in
> latest mainline. Nothing else ever sets NO_IOMEM.
> 

In latest gcc and binutils, can compile score cross compiler
successfully for building kernel (but I am not quite sure whether the
compiling result are really OK, but I guess so).

And next (maybe after finish allmodconfig for microblaze), I shall try
to let uml pass allmodconfig for linux-next tree.

Thanks.
-- 
Chen Gang

Open, share, and attitude like air, water, and life which God blessed

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Richard Weinberger @ 2014-07-17  9:26 UTC (permalink / raw)
  To: Arnd Bergmann, Chen Gang
  Cc: Guenter Roeck, Lars-Peter Clausen, Greg Kroah-Hartman,
	dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Benjamin Herrenschmidt,
	teg-B22kvLQNl6c, Thierry Reding, Lennox Wu, Marek Vasut,
	Liqin Chen, msalter-H+wXaHxf7aLQT0dZR+AlfA,
	linux-pwm-u79uwXL29TY76Z2rM5mHXA,
	devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	knaack.h-Mmb7MZpHnFY, Martin Schwidefsky,
	Mischa.Jonker-HKixBCOQz3hWk0Htik3J/w,
	jic23-DgEjT+Ai2ygdnm+yROfE0A, Geert Uytterhoeven
In-Reply-To: <6823014.2plXDE9VA9@wuerfel>

Am 17.07.2014 11:20, schrieb Arnd Bergmann:
> On Thursday 17 July 2014 09:27:58 Chen Gang wrote:
>>                                          gfp_t gfp_mask, unsigned int order);
>>  extern void devm_free_pages(struct device *dev, unsigned long addr);
>>  
>> +#ifdef CONFIG_HAS_IOMEM
>>  void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
>> +#elif defined(CONFIG_COMPILE_TEST)
>> +static inline void __iomem *devm_ioremap_resource(struct device *dev,
>> +                                               struct resource *res)
>> +{
>> +       pr_warn("no hardware io memory, only for COMPILE_TEST\n");
>> +       return (__force void __iomem *)ERR_PTR(-ENXIO);
>> +}
>> +#endif /* CONFIG_HAS_IOMEM || CONFIG_COMPILE_TEST */
>>  
>>  /* allows to add/remove a custom action to devres stack */
> 
> To be honest, I think it's a bad idea to introduce wrappers functions
> that are only available when CONFIG_COMPILE_TEST is set.
> 
> COMPILE_TEST is a great tool in general, but it has its limits.
> In particular, the case for !CONFIG_IOMEM is completely obscure
> and we won't find any bugs by allowing more drivers to be built
> in those configurations, but attempting to do it would cause
> endless churn by changing each instance of 'depends on HAS_IOMEM'
> to 'depends on HAS_IOMEM || COMPILE_TEST'.
> 
> Note that s390 no has gained support for IOMEM, tile has it most
> of the time (when PCI is enabled, so you get it in half the
> test builds already), score should set HAS_IOMEM and doesn't
> even have public compilers, and uml doesn't even compile in
> latest mainline. Nothing else ever sets NO_IOMEM.

Huh? UML (v3.16-rc5-143-gb6603fe) builds fine here. What build issue are you facing?

Thanks,
//richard

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Arnd Bergmann @ 2014-07-17  9:20 UTC (permalink / raw)
  To: Chen Gang
  Cc: Guenter Roeck, Richard Weinberger, Lars-Peter Clausen,
	Greg Kroah-Hartman, dmitry.torokhov, linux-iio,
	Benjamin Herrenschmidt, teg, Thierry Reding, Lennox Wu,
	Marek Vasut, Liqin Chen, msalter, linux-pwm, devel,
	linux-watchdog, linux-input, linux-kernel@vger.kernel.org,
	knaack.h, Martin Schwidefsky, Mischa.Jonker, jic23,
	Geert Uytterhoeven
In-Reply-To: <53C7269E.4010702@gmail.com>

On Thursday 17 July 2014 09:27:58 Chen Gang wrote:
>                                          gfp_t gfp_mask, unsigned int order);
>  extern void devm_free_pages(struct device *dev, unsigned long addr);
>  
> +#ifdef CONFIG_HAS_IOMEM
>  void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
> +#elif defined(CONFIG_COMPILE_TEST)
> +static inline void __iomem *devm_ioremap_resource(struct device *dev,
> +                                               struct resource *res)
> +{
> +       pr_warn("no hardware io memory, only for COMPILE_TEST\n");
> +       return (__force void __iomem *)ERR_PTR(-ENXIO);
> +}
> +#endif /* CONFIG_HAS_IOMEM || CONFIG_COMPILE_TEST */
>  
>  /* allows to add/remove a custom action to devres stack */

To be honest, I think it's a bad idea to introduce wrappers functions
that are only available when CONFIG_COMPILE_TEST is set.

COMPILE_TEST is a great tool in general, but it has its limits.
In particular, the case for !CONFIG_IOMEM is completely obscure
and we won't find any bugs by allowing more drivers to be built
in those configurations, but attempting to do it would cause
endless churn by changing each instance of 'depends on HAS_IOMEM'
to 'depends on HAS_IOMEM || COMPILE_TEST'.

Note that s390 no has gained support for IOMEM, tile has it most
of the time (when PCI is enabled, so you get it in half the
test builds already), score should set HAS_IOMEM and doesn't
even have public compilers, and uml doesn't even compile in
latest mainline. Nothing else ever sets NO_IOMEM.

	Arnd

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Chen Gang @ 2014-07-17  9:19 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Thierry Reding, linux-iio, Benjamin Herrenschmidt, teg, Lennox Wu,
	Marek Vasut, Liqin Chen, Lars-Peter Clausen, Richard Weinberger,
	Geert Uytterhoeven, msalter, Guenter Roeck, linux-pwm, devel,
	linux-watchdog, arnd, linux-input, Greg Kroah-Hartman,
	dmitry.torokhov, linux-kernel@vger.kernel.org, knaack.h,
	Martin Schwidefsky, Mischa.Jonker, jic23
In-Reply-To: <20140717091601.GF25880@mwanda>



On 07/17/2014 05:16 PM, Dan Carpenter wrote:
> On Thu, Jul 17, 2014 at 04:59:09PM +0800, Chen Gang wrote:
>>>> +	return (__force void __iomem *)ERR_PTR(-ENXIO);
>>>
>>> There's apparently an IOMEM_ERR_PTR() for this nowadays...
>>>
>>
>> IOMEM_ERR_PTR() is defined within "lib/devres.c", not in "./include".
>> But may we move it from "lib/devres.c" to "./include/linux/err.h"?
>>
>> For me, I am not quite sure, it may need additional discussion, but at
>> least, that will be another patch.
> 
> Yes.  Move it there.  That is the right place.
> 

OK, thanks, if no another objections within 2 days, I shall send another
related patch for it.


Thanks.
-- 
Chen Gang

Open, share, and attitude like air, water, and life which God blessed

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Dan Carpenter @ 2014-07-17  9:16 UTC (permalink / raw)
  To: Chen Gang
  Cc: linux-iio, Benjamin Herrenschmidt, teg, Thierry Reding, Lennox Wu,
	Marek Vasut, Liqin Chen, Lars-Peter Clausen, Richard Weinberger,
	Geert Uytterhoeven, msalter, Guenter Roeck, linux-pwm, devel,
	linux-watchdog, arnd, linux-input, Greg Kroah-Hartman,
	dmitry.torokhov, linux-kernel@vger.kernel.org, knaack.h,
	Martin Schwidefsky, Mischa.Jonker, jic23
In-Reply-To: <53C7905D.9090003@gmail.com>

On Thu, Jul 17, 2014 at 04:59:09PM +0800, Chen Gang wrote:
> >> +	return (__force void __iomem *)ERR_PTR(-ENXIO);
> > 
> > There's apparently an IOMEM_ERR_PTR() for this nowadays...
> > 
> 
> IOMEM_ERR_PTR() is defined within "lib/devres.c", not in "./include".
> But may we move it from "lib/devres.c" to "./include/linux/err.h"?
> 
> For me, I am not quite sure, it may need additional discussion, but at
> least, that will be another patch.

Yes.  Move it there.  That is the right place.

regards,
dan carpenter

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Chen Gang @ 2014-07-17  8:59 UTC (permalink / raw)
  To: Thierry Reding
  Cc: linux-iio, Benjamin Herrenschmidt, teg, Lennox Wu, Marek Vasut,
	Liqin Chen, Lars-Peter Clausen, Richard Weinberger,
	Geert Uytterhoeven, msalter, Guenter Roeck, linux-pwm, devel,
	linux-watchdog, arnd, linux-input, Greg Kroah-Hartman,
	dmitry.torokhov, linux-kernel@vger.kernel.org, knaack.h,
	Martin Schwidefsky, Mischa.Jonker, jic23
In-Reply-To: <20140717083756.GA19686@ulmo>



On 07/17/2014 04:37 PM, Thierry Reding wrote:
> On Thu, Jul 17, 2014 at 09:27:58AM +0800, Chen Gang wrote:
> [...]
>> diff --git a/include/linux/device.h b/include/linux/device.h
>> index c2421e0..a7500c3 100644
>> --- a/include/linux/device.h
>> +++ b/include/linux/device.h
>> @@ -630,7 +630,16 @@ extern unsigned long devm_get_free_pages(struct device *dev,
>>  					 gfp_t gfp_mask, unsigned int order);
>>  extern void devm_free_pages(struct device *dev, unsigned long addr);
>>  
>> +#ifdef CONFIG_HAS_IOMEM
>>  void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
>> +#elif defined(CONFIG_COMPILE_TEST)
>> +static inline void __iomem *devm_ioremap_resource(struct device *dev,
>> +						struct resource *res)
>> +{
>> +	pr_warn("no hardware io memory, only for COMPILE_TEST\n");
> 
> Maybe: "Hardware doesn't support memory-mapped I/O"? I'm not sure if
> it's useful to include the reference to COMPILE_TEST, especially since
> the #elif will be dropped in favour of a simple #else.
> 

OK, thanks. I will use the comments which you provide.

And also use #else instead of #elif, use 'dev_warn' instead of 'pr_warn'
which Guenter suggests.

>> +	return (__force void __iomem *)ERR_PTR(-ENXIO);
> 
> There's apparently an IOMEM_ERR_PTR() for this nowadays...
> 

IOMEM_ERR_PTR() is defined within "lib/devres.c", not in "./include".
But may we move it from "lib/devres.c" to "./include/linux/err.h"?

For me, I am not quite sure, it may need additional discussion, but at
least, that will be another patch.

>> +}
>> +#endif /* CONFIG_HAS_IOMEM || CONFIG_COMPILE_TEST */
>>  
>>  /* allows to add/remove a custom action to devres stack */
>>  int devm_add_action(struct device *dev, void (*action)(void *), void *data);
>> diff --git a/include/linux/io.h b/include/linux/io.h
>> index b76e6e5..59128aa 100644
>> --- a/include/linux/io.h
>> +++ b/include/linux/io.h
>> @@ -58,14 +58,42 @@ static inline void devm_ioport_unmap(struct device *dev, void __iomem *addr)
>>  }
>>  #endif
>>  
>> +#ifdef CONFIG_HAS_IOMEM
>> +
>>  void __iomem *devm_ioremap(struct device *dev, resource_size_t offset,
>>  			    unsigned long size);
>>  void __iomem *devm_ioremap_nocache(struct device *dev, resource_size_t offset,
>>  				    unsigned long size);
>>  void devm_iounmap(struct device *dev, void __iomem *addr);
>> +void devm_ioremap_release(struct device *dev, void *res);
>> +
>> +#elif defined(CONFIG_COMPILE_TEST)
>> +
>> +static inline void __iomem *devm_ioremap(struct device *dev,
>> +				resource_size_t offset, unsigned long size)
> 
> For consistency with other functions above, please keep arguments on
> subsequent lines aligned with the first argument on the first line, like
> so:
> 
> static inline void __iomem *devm_ioremap(struct device *dev,
> 					 resource_size_t offset,
> 					 unsigned long size)
> 

That sounds fine to me, thanks.

>> +{
>> +	pr_warn("no hardware io memory, only for COMPILE_TEST\n");
>> +	return NULL;
>> +}
>> +static inline void __iomem *devm_ioremap_nocache(struct device *dev,
>> +				resource_size_t offset, unsigned long size)
>> +{
>> +	pr_warn("no hardware io memory, only for COMPILE_TEST\n");
>> +	return NULL;
>> +}
> 
> Perhaps this should call devm_ioremap() so we don't have to repeat the
> same error message? Or maybe make it:
> 
> 	#define devm_ioremap_nocache devm_ioremap
> 

That sounds fine to me, thanks.

Thanks.
-- 
Chen Gang

Open, share, and attitude like air, water, and life which God blessed

^ permalink raw reply

* Re: [PATCH] drivers: Let several drivers depends on HAS_IOMEM for 'devm_ioremap_resource'
From: Thierry Reding @ 2014-07-17  8:37 UTC (permalink / raw)
  To: Chen Gang
  Cc: linux-iio, Benjamin Herrenschmidt, teg, Lennox Wu, Marek Vasut,
	Liqin Chen, Lars-Peter Clausen, Richard Weinberger,
	Geert Uytterhoeven, msalter, Guenter Roeck, linux-pwm, devel,
	linux-watchdog, arnd, linux-input, Greg Kroah-Hartman,
	dmitry.torokhov, linux-kernel@vger.kernel.org, knaack.h,
	Martin Schwidefsky, Mischa.Jonker, jic23
In-Reply-To: <53C7269E.4010702@gmail.com>


[-- Attachment #1.1: Type: text/plain, Size: 2872 bytes --]

On Thu, Jul 17, 2014 at 09:27:58AM +0800, Chen Gang wrote:
[...]
> diff --git a/include/linux/device.h b/include/linux/device.h
> index c2421e0..a7500c3 100644
> --- a/include/linux/device.h
> +++ b/include/linux/device.h
> @@ -630,7 +630,16 @@ extern unsigned long devm_get_free_pages(struct device *dev,
>  					 gfp_t gfp_mask, unsigned int order);
>  extern void devm_free_pages(struct device *dev, unsigned long addr);
>  
> +#ifdef CONFIG_HAS_IOMEM
>  void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
> +#elif defined(CONFIG_COMPILE_TEST)
> +static inline void __iomem *devm_ioremap_resource(struct device *dev,
> +						struct resource *res)
> +{
> +	pr_warn("no hardware io memory, only for COMPILE_TEST\n");

Maybe: "Hardware doesn't support memory-mapped I/O"? I'm not sure if
it's useful to include the reference to COMPILE_TEST, especially since
the #elif will be dropped in favour of a simple #else.

> +	return (__force void __iomem *)ERR_PTR(-ENXIO);

There's apparently an IOMEM_ERR_PTR() for this nowadays...

> +}
> +#endif /* CONFIG_HAS_IOMEM || CONFIG_COMPILE_TEST */
>  
>  /* allows to add/remove a custom action to devres stack */
>  int devm_add_action(struct device *dev, void (*action)(void *), void *data);
> diff --git a/include/linux/io.h b/include/linux/io.h
> index b76e6e5..59128aa 100644
> --- a/include/linux/io.h
> +++ b/include/linux/io.h
> @@ -58,14 +58,42 @@ static inline void devm_ioport_unmap(struct device *dev, void __iomem *addr)
>  }
>  #endif
>  
> +#ifdef CONFIG_HAS_IOMEM
> +
>  void __iomem *devm_ioremap(struct device *dev, resource_size_t offset,
>  			    unsigned long size);
>  void __iomem *devm_ioremap_nocache(struct device *dev, resource_size_t offset,
>  				    unsigned long size);
>  void devm_iounmap(struct device *dev, void __iomem *addr);
> +void devm_ioremap_release(struct device *dev, void *res);
> +
> +#elif defined(CONFIG_COMPILE_TEST)
> +
> +static inline void __iomem *devm_ioremap(struct device *dev,
> +				resource_size_t offset, unsigned long size)

For consistency with other functions above, please keep arguments on
subsequent lines aligned with the first argument on the first line, like
so:

static inline void __iomem *devm_ioremap(struct device *dev,
					 resource_size_t offset,
					 unsigned long size)

> +{
> +	pr_warn("no hardware io memory, only for COMPILE_TEST\n");
> +	return NULL;
> +}
> +static inline void __iomem *devm_ioremap_nocache(struct device *dev,
> +				resource_size_t offset, unsigned long size)
> +{
> +	pr_warn("no hardware io memory, only for COMPILE_TEST\n");
> +	return NULL;
> +}

Perhaps this should call devm_ioremap() so we don't have to repeat the
same error message? Or maybe make it:

	#define devm_ioremap_nocache devm_ioremap

?

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 169 bytes --]

_______________________________________________
devel mailing list
devel@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

^ permalink raw reply

* Re: [PATCH v4] Input: Add driver for Microchip's CAP1106
From: Daniel Mack @ 2014-07-17  7:55 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-input, broonie, dh.herrmann, devicetree, mark.rutland
In-Reply-To: <20140714184642.GA38980@core.coreip.homeip.net>

On 07/14/2014 08:46 PM, Dmitry Torokhov wrote:
> On Mon, Jul 14, 2014 at 04:28:02PM +0200, Daniel Mack wrote:
>> +
>> +static int cap1106_i2c_remove(struct i2c_client *i2c_client)
>> +{
>> +	struct cap1106_priv *priv = i2c_get_clientdata(i2c_client);
>> +
>> +	input_unregister_device(priv->idev);
>> +
> 
> No need to call unregister on managed input devices.

Addressed in v5, which I posted already.

> Otherwise if DT guys are happy with bindings I am happy with the patch.

That seems to be the case. I guess we're done then :)

Thanks again for the reviews, everyone.


Daniel


^ permalink raw reply

* [PATCH v4 14/14] input: cyapa: add function to monitor LID close event to off trackpad device
From: Dudley Du @ 2014-07-17  7:00 UTC (permalink / raw)
  To: Dmitry Torokhov, Rafael J. Wysocki
  Cc: Benson Leung, Patrik Fimml, linux-input, linux-kernel, Dudley Du

Add the function to monitor lid close event to suspend and resume
trackpad device.
Because system suspend takes some time to trigger from user space,
and in that time, the lid panel of the laptop may couple with the
active trackpad. This may generate stray input events, which may
in turn cancel the suspend if the drivers use pm_wakup_event(), and
those input events may trigger something unwanted in the UI.
So this patch adds the function to do off the trackpad device quickly.
When the lid is closed, as soon as possible, the trakcpad device must
be off. And furthermore, the policy on lid close is not always to
enter suspend (lid closed with external display), and at this time,
the trackpad device must be disabled as well as again to avoid the
risk of generating stray events.
TEST=test on Chromebooks.

Signed-off-by: Dudley Du <dudl@cypress.com>
---
 drivers/input/mouse/cyapa.c |  136 +++++++++++++++++++++++++++++++++++++++++++
 drivers/input/mouse/cyapa.h |    4 +-
 2 files changed, 139 insertions(+), 1 deletion(-)

diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c
index a5f6473..a681a3b 100644
--- a/drivers/input/mouse/cyapa.c
+++ b/drivers/input/mouse/cyapa.c
@@ -1072,6 +1072,140 @@ static const struct attribute_group cyapa_sysfs_group = {
 	.attrs = cyapa_sysfs_entries,
 };
 
+
+/*
+ * We rely on EV_SW and SW_LID bits to identify a LID device, and hook
+ * up our filter to listen for SW_LID events to enable/disable touchpad when
+ * LID is open/closed.
+ */
+static const struct input_device_id lid_device_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+			 INPUT_DEVICE_ID_MATCH_SWBIT,
+		.evbit = { BIT_MASK(EV_SW) },
+		.swbit = { BIT_MASK(SW_LID) },
+	},
+	{ },
+};
+
+static int lid_device_connect(struct input_handler *handler,
+			      struct input_dev *dev,
+			      const struct input_device_id *id)
+{
+	struct input_handle *lid_handle;
+	int error;
+
+	pr_info("cyapa: LID device: '%s' connected\n", dev->name);
+	lid_handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+	if (!lid_handle)
+		return -ENOMEM;
+
+	lid_handle->dev = dev;
+	lid_handle->handler = handler;
+	lid_handle->name = "lid_event_handler";
+	lid_handle->private = handler->private;
+
+	error = input_register_handle(lid_handle);
+	if (error) {
+		pr_err("Failed to register lid_event_handler, error %d\n",
+		       error);
+		goto err_free;
+	}
+
+	error = input_open_device(lid_handle);
+	if (error) {
+		pr_err("Failed to open input device, error %d\n", error);
+		goto err_unregister;
+	}
+
+	return 0;
+err_unregister:
+	input_unregister_handle(lid_handle);
+err_free:
+	kfree(lid_handle);
+	return error;
+}
+
+static void lid_device_disconnect(struct input_handle *handle)
+{
+	input_close_device(handle);
+	input_unregister_handle(handle);
+	kfree(handle);
+}
+
+static bool lid_event_filter(struct input_handle *handle,
+			     unsigned int type, unsigned int code, int value)
+{
+	struct cyapa *cyapa = handle->private;
+	struct device *dev = &cyapa->client->dev;
+
+	if (type == EV_SW && code == SW_LID) {
+		pr_info("cyapa %s: %s touch device\n",
+			dev_name(&cyapa->client->dev),
+			(value ? "disable" : "enable"));
+		if (cyapa->suspended) {
+			/*
+			 * If the lid event filter is called while suspended,
+			 * there is no guarantee that the underlying i2cs are
+			 * resumed at this point, so it is not safe to issue
+			 * the command to change power modes.
+			 * Instead, rely on cyapa_resume to set us back to
+			 * PWR_MODE_FULL_ACTIVE.
+			 */
+			pr_info("cyapa %s: skipping lid pm change in suspend\n",
+				dev_name(&cyapa->client->dev));
+			return false;
+		}
+		if (value == 0) {
+			if (cyapa->ops->set_power_mode)
+				cyapa->ops->set_power_mode(cyapa,
+						PWR_MODE_FULL_ACTIVE, 0);
+			pm_runtime_set_active(dev);
+			pm_runtime_enable(dev);
+		} else {
+			pm_runtime_disable(dev);
+			if (cyapa->ops->set_power_mode)
+				cyapa->ops->set_power_mode(cyapa,
+						PWR_MODE_OFF, 0);
+		}
+	}
+
+	return false;
+}
+
+static void lid_event_register_handler(struct cyapa *cyapa)
+{
+	int error;
+	struct input_handler *lid_handler = &cyapa->lid_handler;
+
+	if (cyapa->lid_handler_registered) {
+		pr_err("lid handler is registered already\n");
+		return;
+	}
+
+	lid_handler->filter	= lid_event_filter;
+	lid_handler->connect	= lid_device_connect;
+	lid_handler->disconnect	= lid_device_disconnect;
+	lid_handler->name	= "cyapa_lid_event_handler";
+	lid_handler->id_table	= lid_device_ids;
+	lid_handler->private	= cyapa;
+
+	error = input_register_handler(lid_handler);
+	if (error) {
+		pr_err("Failed to register lid handler(%d)\n", error);
+		return;
+	}
+	cyapa->lid_handler_registered = true;
+}
+
+static void lid_event_unregister_handler(struct cyapa *cyapa)
+{
+	if (cyapa->lid_handler_registered) {
+		input_unregister_handler(&cyapa->lid_handler);
+		cyapa->lid_handler_registered = false;
+	}
+}
+
 void cyapa_detect_async(void *data, async_cookie_t cookie)
 {
 	struct cyapa *cyapa = (struct cyapa *)data;
@@ -1091,6 +1225,7 @@ static void cyapa_detect_and_start(void *data, async_cookie_t cookie)
 	cyapa_detect_async(data, cookie);
 
 	cyapa_start_runtime(cyapa);
+	lid_event_register_handler(cyapa);
 }
 
 static int cyapa_tp_modules_init(struct cyapa *cyapa)
@@ -1239,6 +1374,7 @@ static int cyapa_remove(struct i2c_client *client)
 	mutex_destroy(&cyapa->debugfs_mutex);
 
 	input_unregister_device(cyapa->input);
+	lid_event_unregister_handler(cyapa);
 	if (cyapa->ops->set_power_mode)
 		cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0);
 	cyapa_tp_modules_uninit(cyapa);
diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h
index 728bf70..ff3ac00 100644
--- a/drivers/input/mouse/cyapa.h
+++ b/drivers/input/mouse/cyapa.h
@@ -271,8 +271,10 @@ struct cyapa {
 	size_t tp_raw_data_size;
 
 	const struct cyapa_dev_ops *ops;
-};
 
+	bool lid_handler_registered;
+	struct input_handler lid_handler;
+};
 
 
 ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len,
-- 
1.7.9.5



^ permalink raw reply related

* [PATCH v4 13/14] input: cyapa: add gen5 trackpad device read firmware image and raw data functions support
From: Dudley Du @ 2014-07-17  6:59 UTC (permalink / raw)
  To: Dmitry Torokhov, Rafael J. Wysocki
  Cc: Benson Leung, Patrik Fimml, linux-input, linux-kernel, Dudley Du

Add read firmware image function supported for gen5 trackpad device,
which its function is supplied through cyapa core read_fw interface.
Through this interface, upper layer application can read out, check
and backup the firmware image in trackpad device before updated
with new one when new firmware image may have problems.
Also add interfaces to report all sensor's raw data values to upper
layer, so it can help to find out the performance issue when users
reports problem, and also it's useful and required interface for
some customers that require sensors' raw data.
TEST=test on Chromebooks.

Signed-off-by: Dudley Du <dudl@cypress.com>
---
 drivers/input/mouse/cyapa_gen5.c |  265 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 265 insertions(+)

diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c
index efd22af..de49e8b 100644
--- a/drivers/input/mouse/cyapa_gen5.c
+++ b/drivers/input/mouse/cyapa_gen5.c
@@ -1226,6 +1226,158 @@ static int cyapa_gen5_write_fw_block(struct cyapa *cyapa,
 	return 0;
 }
 
+static int cyapa_gen5_read_fw_bytes(struct cyapa *cyapa, u16 row_num, u8 *data)
+{
+	int ret;
+	u8 cmd[16];
+	size_t cmd_len;
+	u8 resp_data[CYAPA_TSG_FW_ROW_SIZE / 2 + GEN5_MIN_BL_RESP_LENGTH];
+	int resp_len;
+	u16 offset;
+	u16 cmd_crc;
+	struct cyapa_tsg_bin_image_data_record *fw_img_record;
+
+	fw_img_record = (struct cyapa_tsg_bin_image_data_record *)data;
+
+	cmd[0] = 0x04;  /* Register address */
+	cmd[1] = 0x00;
+	cmd[2] = 0x0e;
+	cmd[3] = 0x00;
+	cmd[4] = 0x40;  /* Report id 40h */
+	cmd[5] = 0x00;
+	cmd[6] = GEN5_SOP_KEY;
+	cmd[7] = 0x3d;  /* Read application image command code */
+	cmd[8] = 0x03;
+	cmd[9] = 0x00;
+	offset = row_num * CYAPA_TSG_FW_ROW_SIZE -
+			CYAPA_TSG_START_OF_APPLICATION;
+	put_unaligned_le16(offset, &cmd[10]);
+	cmd[12] = CYAPA_TSG_IMG_READ_SIZE;
+	cmd_crc = crc_itu_t(0xffff, &cmd[6], 7);
+	put_unaligned_le16(cmd_crc, &cmd[13]);  /* CRC[15:0] */
+	cmd[15] = GEN5_EOP_KEY;  /* EOP = 17h */
+	cmd_len = 16;
+
+	resp_len = CYAPA_TSG_IMG_READ_SIZE + GEN5_MIN_BL_RESP_LENGTH;
+	ret = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, cmd_len,
+			resp_data, &resp_len,
+			50, cyapa_gen5_sort_tsg_pip_bl_resp_data);
+	if (resp_len != (CYAPA_TSG_IMG_READ_SIZE + GEN5_MIN_BL_RESP_LENGTH) ||
+			ret || resp_data[2] != GEN5_BL_RESP_REPORT_ID ||
+			!GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
+		return (ret < 0) ? ret : -EAGAIN;
+
+	/* Copy first 64 bytes in the row. */
+	memcpy(&fw_img_record->record_data[0], &resp_data[8],
+			CYAPA_TSG_IMG_READ_SIZE);
+
+	if (row_num == CYAPA_TSG_IMG_APP_INTEGRITY_ROW_NUM) {
+		/* Last row's rest 64 bytes are bootloader metadata,
+		 * it's not allowed to be read out, will respond with error. */
+		memset(&fw_img_record->record_data[CYAPA_TSG_IMG_READ_SIZE],
+			0, CYAPA_TSG_IMG_READ_SIZE);
+		goto skip_last_row;
+	}
+
+	/* Read next 64 bytes in the row. */
+	offset = offset + CYAPA_TSG_IMG_READ_SIZE;
+	put_unaligned_le16(offset, &cmd[10]);
+	cmd_crc = crc_itu_t(0xffff, &cmd[6], 7);
+	put_unaligned_le16(cmd_crc, &cmd[13]);  /* CRC[15:0] */
+	ret = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, cmd_len,
+			resp_data, &resp_len,
+			500, cyapa_gen5_sort_tsg_pip_bl_resp_data);
+	if (resp_len != (CYAPA_TSG_IMG_READ_SIZE + GEN5_MIN_BL_RESP_LENGTH) ||
+			ret || resp_data[2] != GEN5_BL_RESP_REPORT_ID ||
+			!GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
+		return (ret < 0) ? ret : -EAGAIN;
+
+	/* Copy last 64 bytes in the row. */
+	memcpy(&fw_img_record->record_data[CYAPA_TSG_IMG_READ_SIZE],
+		&resp_data[8], CYAPA_TSG_IMG_READ_SIZE);
+
+skip_last_row:
+	fw_img_record->flash_array_id = 0;
+	put_unaligned_be16(row_num, &fw_img_record->row_number);
+	put_unaligned_be16(CYAPA_TSG_FW_ROW_SIZE, &fw_img_record->record_len);
+
+	return 0;
+}
+
+static int cyapa_gen5_read_fw(struct cyapa *cyapa)
+{
+	int ret;
+	int fw_img_head_size;
+	int fw_img_record_size;
+	int row_index;
+	int array_index;
+	u32 img_start;
+	u16 img_len;
+	u16 img_start_row;
+	u16 img_end_row;
+	struct cyapa_tsg_bin_image_data_record app_integrity;
+	u8 *record_data;
+
+	ret = cyapa_gen5_bl_enter(cyapa);
+	if (ret)
+		goto err;
+
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	fw_img_head_size = sizeof(struct cyapa_tsg_bin_image_head);
+	fw_img_record_size = sizeof(struct cyapa_tsg_bin_image_data_record);
+
+	/* Read app integrity block data. */
+	row_index = CYAPA_TSG_IMG_APP_INTEGRITY_ROW_NUM;
+	ret = cyapa_gen5_read_fw_bytes(cyapa, row_index, (u8 *)&app_integrity);
+	if (ret)
+		goto err;
+	img_start = get_unaligned_le32(&app_integrity.record_data[16]);
+	img_len = get_unaligned_le16(&app_integrity.record_data[20]);
+	if ((img_start + img_len) % CYAPA_TSG_FW_ROW_SIZE)
+		goto err;
+	img_start_row = img_start / CYAPA_TSG_FW_ROW_SIZE;
+	img_end_row = (img_start + img_len) / CYAPA_TSG_FW_ROW_SIZE - 1;
+
+	/* Allocate memory for image. */
+	cyapa->fw_image_size = fw_img_head_size +
+		(img_end_row -  img_start_row + 2) * fw_img_record_size;
+	cyapa->fw_image = kmalloc(cyapa->fw_image_size, GFP_KERNEL);
+	if (!cyapa->fw_image) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	/* Set image head data. */
+	memcpy(cyapa->fw_image, &gen5_fw_img_head, fw_img_head_size);
+
+	/* Read image blocks. */
+	for (row_index = img_start_row, array_index = 0;
+			row_index <= img_end_row;
+			row_index++, array_index++) {
+		record_data = &cyapa->fw_image[fw_img_head_size +
+				array_index * fw_img_record_size];
+		ret = cyapa_gen5_read_fw_bytes(cyapa, row_index, record_data);
+		if (ret)
+			goto err;
+	}
+
+	/* Append last app integrity block data. */
+	record_data = &cyapa->fw_image[fw_img_head_size +
+				array_index * fw_img_record_size];
+	memcpy(record_data, &app_integrity, fw_img_record_size);
+
+err:
+	if (ret) {
+		kfree(cyapa->fw_image);
+		cyapa->fw_image = NULL;
+		cyapa->fw_image_size = 0;
+	}
+	return ret;
+}
+
 static int cyapa_gen5_do_fw_update(struct cyapa *cyapa,
 		const struct firmware *fw)
 {
@@ -2212,6 +2364,116 @@ resume_scanning:
 	return ret + err;
 }
 
+static int cyapa_gen5_read_raw_data(struct cyapa *cyapa)
+{
+	int ret, err;
+	int raw_cap_mutual_max, raw_cap_mutual_min, raw_cap_mutual_ave;
+	int raw_cap_self_max, raw_cap_self_min, raw_cap_self_ave;
+	int offset;
+	int data_size, max, min, ave;
+	ktime_t time_mono;
+
+	offset = 0;
+	if (!cyapa->tp_raw_data) {
+		if (cyapa->state != CYAPA_STATE_GEN5_APP ||
+			!cyapa->electrodes_x || !cyapa->electrodes_y)
+			return  -EINVAL;
+
+		cyapa->tp_raw_data_size = sizeof(s32) * (cyapa->electrodes_x *
+			cyapa->electrodes_y + cyapa->electrodes_x +
+			cyapa->electrodes_y) + GEN5_RAW_DATA_HEAD_SIZE;
+		/*
+		 * This buffer will be hold after used until the driver is
+		 * unloaded, the purpose of it is to improve the performace
+		 * to avoid frequently allocate and release the buffer.
+		 */
+		cyapa->tp_raw_data =
+			kmalloc(cyapa->tp_raw_data_size, GFP_KERNEL);
+		if (!cyapa->tp_raw_data)
+			return -ENOMEM;
+		memset(cyapa->tp_raw_data, 0, cyapa->tp_raw_data_size);
+	}
+
+
+	/*
+	 * 1. Suspend Scanning.
+	 *
+	 * After suspend scanning, the raw data will not be updated,
+	 * so the time of the raw data is before scanning suspended.
+	 */
+	time_mono = ktime_get();
+	ret = cyapa_gen5_suspend_scanning(cyapa);
+	if (ret)
+		return ret;
+
+	/* 2. Get the correct electrodes_rx number. */
+	if (cyapa->electrodes_rx == 0) {
+		/*
+		 * Through the read global idac interface to get the Rx number.
+		 * this value is useful to the raw data map.
+		 */
+		data_size = 0;
+		err = cyapa_gen5_read_idac_data(cyapa,
+				GEN5_CMD_RETRIEVE_DATA_STRUCTURE,
+				GEN5_RETRIEVE_MUTUAL_PWC_DATA,
+				&data_size, &max, &min, &ave);
+		if (err || cyapa->electrodes_rx == 0)
+			goto resume_scanning;
+	}
+
+	/* 3. Execuate panel scan. It must be executed before read data. */
+	err = cyapa_gen5_execute_panel_scan(cyapa);
+	if (err)
+		goto resume_scanning;
+
+	/* 4. Retrive panel scan, mutual cap raw data. */
+	offset = GEN5_RAW_DATA_HEAD_SIZE;
+	err = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+				GEN5_CMD_RETRIEVE_PANEL_SCAN,
+				GEN5_PANEL_SCAN_MUTUAL_DIFFCOUNT,
+				cyapa->electrodes_x * cyapa->electrodes_y,
+				&raw_cap_mutual_max, &raw_cap_mutual_min,
+				&raw_cap_mutual_ave,
+				cyapa->tp_raw_data + offset);
+	if (err)
+		goto resume_scanning;
+
+	offset += sizeof(s32) * cyapa->electrodes_x * cyapa->electrodes_y;
+
+	/* 5. Retrive panel scan, self cap raw data. */
+	err = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+				GEN5_CMD_RETRIEVE_PANEL_SCAN,
+				GEN5_PANEL_SCAN_SELF_DIFFCOUNT,
+				cyapa->electrodes_x + cyapa->electrodes_y,
+				&raw_cap_self_max, &raw_cap_self_min,
+				&raw_cap_self_ave,
+				cyapa->tp_raw_data + offset);
+	if (err)
+		goto resume_scanning;
+
+	offset += sizeof(s32) * (cyapa->electrodes_x + cyapa->electrodes_y);
+
+resume_scanning:
+	/* 6. Resume Scanning*/
+	ret = cyapa_gen5_resume_scanning(cyapa);
+	if (ret || err)
+		return ret ? ret : err;
+
+	*((struct timeval *)&cyapa->tp_raw_data[0]) =
+						ktime_to_timeval(time_mono);
+	cyapa->tp_raw_data[16] = (u8)cyapa->electrodes_x;
+	cyapa->tp_raw_data[17] = (u8)cyapa->electrodes_y;
+	cyapa->tp_raw_data[18] = (u8)cyapa->x_origin;
+	cyapa->tp_raw_data[19] = (u8)cyapa->y_origin;
+	cyapa->tp_raw_data[20] = (u8)sizeof(s32);
+	cyapa->tp_raw_data[21] = (u8)sizeof(s32);
+	cyapa->tp_raw_data[22] = (u8)cyapa->electrodes_rx;
+	cyapa->tp_raw_data[23] = 0;  /* Reserved. */
+
+	cyapa->tp_raw_data_size = offset;
+	return 0;
+}
+
 static bool cyapa_gen5_sort_system_info_data(struct cyapa *cyapa,
 		u8 *buf, int len)
 {
@@ -2616,6 +2878,9 @@ const struct cyapa_dev_ops cyapa_gen5_ops = {
 	.show_baseline = cyapa_gen5_show_baseline,
 	.calibrate_store = cyapa_gen5_do_calibrate,
 
+	.read_fw = cyapa_gen5_read_fw,
+	.read_raw_data = cyapa_gen5_read_raw_data,
+
 	.initialize = cyapa_gen5_initialize,
 	.uninitialize = cyapa_gen5_uninitialize,
 
-- 
1.7.9.5



^ permalink raw reply related

* [PATCH v4 12/14] input: cyapa: add gen5 trackpad device baseline and calibrate functions support
From: Dudley Du @ 2014-07-17  6:59 UTC (permalink / raw)
  To: Dmitry Torokhov, Rafael J. Wysocki
  Cc: Benson Leung, Patrik Fimml, linux-input, linux-kernel, Dudley Du

Add report baseline and force calibrate functions supported for gen5
trackpad device, which these functions are supplied through
cyapa core baseline and calibrate interfaces.
TEST=test on Chromebooks.

Signed-off-by: Dudley Du <dudl@cypress.com>
---
 drivers/input/mouse/cyapa_gen5.c |  670 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 670 insertions(+)

diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c
index 38c27b5..efd22af 100644
--- a/drivers/input/mouse/cyapa_gen5.c
+++ b/drivers/input/mouse/cyapa_gen5.c
@@ -1545,6 +1545,673 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
 	return ret;
 }
 
+static int cyapa_gen5_resume_scanning(struct cyapa *cyapa)
+{
+	u8 cmd[7] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x04 };
+	u8 resp_data[6];
+	int resp_len;
+	int ret;
+
+	/* Try to dump all bufferred data before doing command. */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	resp_len = 6;
+	ret = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, 7,
+			resp_data, &resp_len,
+			500, cyapa_gen5_sort_tsg_pip_app_resp_data);
+	if (ret || !VALID_CMD_RESP_HEADER(resp_data, 0x04))
+		return -EINVAL;
+
+	/* Try to dump all bufferred data when resuming scanning. */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	return 0;
+}
+
+static int cyapa_gen5_suspend_scanning(struct cyapa *cyapa)
+{
+	u8 cmd[7] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x03 };
+	u8 resp_data[6];
+	int resp_len;
+	int ret;
+
+	/* Try to dump all bufferred data before doing command. */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	resp_len = 6;
+	ret = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, 7,
+			resp_data, &resp_len,
+			500, cyapa_gen5_sort_tsg_pip_app_resp_data);
+	if (ret || !VALID_CMD_RESP_HEADER(resp_data, 0x03))
+		return -EINVAL;
+
+	/* Try to dump all bufferred data when suspending scanning. */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	return 0;
+}
+
+static int cyapa_gen5_calibrate_pwcs(struct cyapa *cyapa,
+		u8 calibrate_sensing_mode_type)
+{
+	int ret;
+	u8 cmd[8];
+	u8 resp_data[6];
+	int resp_len;
+
+	/* Try to dump all bufferred data before doing command. */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	cmd[0] = 0x04;
+	cmd[1] = 0x00;
+	cmd[2] = 0x06;
+	cmd[3] = 0x00;
+	cmd[4] = GEN5_APP_CMD_REPORT_ID;
+	cmd[5] = 0x00;
+	cmd[6] = GEN5_CMD_CALIBRATE;
+	cmd[7] = calibrate_sensing_mode_type;
+	resp_len = sizeof(resp_data);
+	ret = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			5000, cyapa_gen5_sort_tsg_pip_app_resp_data);
+	if (ret || !VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_CALIBRATE) ||
+			!GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
+		return ret < 0 ? ret : -EAGAIN;
+
+	return 0;
+}
+
+static ssize_t cyapa_gen5_do_calibrate(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	int ret, calibrate_ret;
+
+	/* 1. Suspend Scanning*/
+	ret = cyapa_gen5_suspend_scanning(cyapa);
+	if (ret)
+		return ret;
+
+	/* 2. Do mutual capacitance fine calibrate. */
+	calibrate_ret = cyapa_gen5_calibrate_pwcs(cyapa,
+				CYAPA_SENSING_MODE_MUTUAL_CAP_FINE);
+	if (calibrate_ret)
+		goto resume_scanning;
+
+	/* 3. Do self capacitance calibrate. */
+	calibrate_ret = cyapa_gen5_calibrate_pwcs(cyapa,
+				CYAPA_SENSING_MODE_SELF_CAP);
+	if (calibrate_ret)
+		goto resume_scanning;
+
+resume_scanning:
+	/* 4. Resume Scanning*/
+	ret = cyapa_gen5_resume_scanning(cyapa);
+	if (ret || calibrate_ret)
+		return ret ? ret : calibrate_ret;
+
+	return count;
+}
+
+static s32 two_complement_to_s32(s32 value, int num_bits)
+{
+	if (value >> (num_bits - 1))
+		value |=  -1 << num_bits;
+	return value;
+}
+
+static s32 cyapa_parse_structure_data(u8 data_format, u8 *buf, int buf_len)
+{
+	int data_size;
+	bool big_endian;
+	bool unsigned_type;
+	s32 value;
+
+	data_size = (data_format & 0x07);
+	big_endian = ((data_format & 0x10) == 0x00);
+	unsigned_type = ((data_format & 0x20) == 0x00);
+
+	if (buf_len < data_size)
+		return 0;
+
+	switch (data_size) {
+	case 1:
+		value  = buf[0];
+		break;
+	case 2:
+		if (big_endian)
+			value = get_unaligned_be16(buf);
+		else
+			value = get_unaligned_le16(buf);
+		break;
+	case 4:
+		if (big_endian)
+			value = get_unaligned_be32(buf);
+		else
+			value = get_unaligned_le32(buf);
+		break;
+	default:
+		/* Should not happen, just as default case here. */
+		value = 0;
+		break;
+	}
+
+	if (!unsigned_type)
+		value = two_complement_to_s32(value, data_size * 8);
+
+	return value;
+}
+
+
+/*
+ * Read all the global mutual or self idac data or mutual or self local PWC
+ * data based on the @idac_data_type.
+ * If the input value of @data_size is 0, then means read global mutual or
+ * self idac data. For read global mutual idac data, @idac_max, @idac_min and
+ * @idac_ave are in order used to return the max value of global mutual idac
+ * data, the min value of global mutual idac and the average value of the
+ * global mutual idac data. For read global self idac data, @idac_max is used
+ * to return the global self cap idac data in Rx direction, @idac_min is used
+ * to return the global self cap idac data in Tx direction. @idac_ave is not
+ * used.
+ * If the input value of @data_size is not 0, than means read the mutual or
+ * self local PWC data. The @idac_max, @idac_min and @idac_ave are used to
+ * return the max, min and average value of the mutual or self local PWC data.
+ * Note, in order to raed mutual local PWC data, must read invoke this function
+ * to read the mutual global idac data firstly to set the correct Rx number
+ * value, otherwise, the read mutual idac and PWC data may not correct.
+ */
+static int cyapa_gen5_read_idac_data(struct cyapa *cyapa,
+		u8 cmd_code, u8 idac_data_type, int *data_size,
+		int *idac_max, int *idac_min, int *idac_ave)
+{
+	int ret;
+	int i;
+	u8 cmd[12];
+	u8 resp_data[256];
+	int resp_len;
+	int read_len;
+	int value;
+	u16 offset;
+	int read_elements;
+	bool read_global_idac;
+	int sum, count, max_element_cnt;
+	int tmp_max, tmp_min, tmp_ave, tmp_sum, tmp_count, tmp_max_elements;
+	int electrodes_rx;
+
+	if (cmd_code != GEN5_CMD_RETRIEVE_DATA_STRUCTURE ||
+		(idac_data_type != GEN5_RETRIEVE_MUTUAL_PWC_DATA &&
+		idac_data_type != GEN5_RETRIEVE_SELF_CAP_PWC_DATA) ||
+		!data_size || !idac_max || !idac_min || !idac_ave)
+		return -EINVAL;
+
+	*idac_max = INT_MIN;
+	*idac_min = INT_MAX;
+	sum = count = tmp_count = 0;
+	electrodes_rx = 0;
+	tmp_max_elements = 0;
+	if (*data_size == 0) {
+		/*
+		 * Read global idac values firstly.
+		 * Currently, no idac data exceed 4 bytes.
+		 */
+		read_global_idac = true;
+		offset = 0;
+		*data_size = 4;
+
+		if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) {
+			if (cyapa->electrodes_rx == 0) {
+				if (cyapa->electrodes_y > cyapa->electrodes_x) {
+					electrodes_rx = cyapa->electrodes_y;
+					tmp_max_elements = cyapa->electrodes_x;
+				} else {
+					electrodes_rx =	cyapa->electrodes_x;
+					tmp_max_elements = cyapa->electrodes_y;
+				}
+			} else {
+				electrodes_rx = cyapa->electrodes_rx;
+				tmp_max_elements = 0;  /* Disable Rx detect. */
+			}
+			max_element_cnt = ((electrodes_rx + 7) / 8) * 8;
+			tmp_max = INT_MIN;
+			tmp_min = INT_MAX;
+			tmp_ave = tmp_sum = tmp_count = 0;
+		} else
+			max_element_cnt = 2;
+	} else {
+		read_global_idac = false;
+		if (*data_size > 4)
+			*data_size = 4;
+		/* Calculate the start offset in bytes of local PWC data. */
+		if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) {
+			offset = ((cyapa->electrodes_rx + 7) / 8) * 8
+						* (*data_size);
+			if (cyapa->electrodes_rx == cyapa->electrodes_x)
+				tmp_count = cyapa->electrodes_y;
+			else
+				tmp_count = cyapa->electrodes_x;
+			max_element_cnt = ((cyapa->electrodes_rx + 7) / 8) *
+						8 * tmp_count;
+		} else if (idac_data_type == GEN5_RETRIEVE_SELF_CAP_PWC_DATA) {
+			offset = 2;
+			max_element_cnt = cyapa->electrodes_x +
+						cyapa->electrodes_y;
+		}
+	}
+
+	do {
+		read_elements = (256 - 10) / (*data_size);
+		read_elements = min(read_elements, max_element_cnt - count);
+		read_len = read_elements * (*data_size);
+
+		cmd[0] = 0x04;
+		cmd[1] = 0x00;
+		cmd[2] = 0x0a;
+		cmd[3] = 0x00;
+		cmd[4] = GEN5_APP_CMD_REPORT_ID;
+		cmd[5] = 0x00;
+		cmd[6] = cmd_code;
+		put_unaligned_le16(offset, &cmd[7]); /* Read Offset[15:0] */
+		put_unaligned_le16(read_len, &cmd[9]); /* Read Length[15:0] */
+		cmd[11] = idac_data_type;
+		resp_len = 10 + read_len;
+		ret = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+				cmd, 12,
+				resp_data, &resp_len,
+				500, cyapa_gen5_sort_tsg_pip_app_resp_data);
+		if (ret || resp_len < 10 ||
+				!VALID_CMD_RESP_HEADER(resp_data, cmd_code) ||
+				!GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]) ||
+				resp_data[6] != idac_data_type)
+			return (ret < 0) ? ret : -EAGAIN;
+		read_len = get_unaligned_le16(&resp_data[7]);
+		if (read_len == 0)
+			break;
+
+		*data_size = (resp_data[9] & GEN5_PWC_DATA_ELEMENT_SIZE_MASK);
+		if (read_len < *data_size)
+			return -EINVAL;
+
+		if (read_global_idac &&
+			idac_data_type == GEN5_RETRIEVE_SELF_CAP_PWC_DATA) {
+			/* Rx's self global idac data. */
+			*idac_max = cyapa_parse_structure_data(
+					resp_data[9], &resp_data[10],
+					*data_size);
+			/* Tx's self global idac data. */
+			*idac_min = cyapa_parse_structure_data(
+					resp_data[9],
+					&resp_data[10 + *data_size],
+					*data_size);
+			break;
+		}
+
+		/* Read mutual global idac or local mutual/self PWC data. */
+		offset += read_len;
+		for (i = 10; i < (read_len + 10); i += *data_size) {
+			value = cyapa_parse_structure_data(resp_data[9],
+					&resp_data[i], *data_size);
+			*idac_min = min(value, *idac_min);
+			*idac_max = max(value, *idac_max);
+
+			if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA &&
+				tmp_count < tmp_max_elements &&
+				read_global_idac) {
+				tmp_min = min(value, tmp_min);
+				tmp_max = max(value, tmp_max);
+				tmp_sum += value;
+				tmp_count++;
+			}
+
+			sum += value;
+			count++;
+
+			if (count >= max_element_cnt)
+				goto out;
+		}
+	} while (true);
+
+out:
+	*idac_ave = count ? (sum / count) : 0;
+
+	if (read_global_idac &&
+		idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) {
+		if (tmp_count == 0)
+			return 0;
+		/* Algorithm to detect electrodes_rx value. */
+		tmp_ave = tmp_sum / tmp_count;
+		tmp_count = tmp_ave * 15 / 100;
+		if (abs(tmp_ave - *idac_ave) > tmp_count ||
+			(abs(tmp_ave - *idac_min) > (tmp_count * 2) &&
+				*idac_min < tmp_min) ||
+			(abs(*idac_max - tmp_ave) > (tmp_count * 2) &&
+				*idac_max > tmp_max)) {
+			/* Overcount the mutual global idac values. */
+			cyapa->electrodes_rx = tmp_max_elements;
+			*idac_min = tmp_min;
+			*idac_max = tmp_max;
+			*idac_ave = tmp_ave;
+		} else
+			cyapa->electrodes_rx = electrodes_rx;
+	}
+
+	return 0;
+}
+
+static int cyapa_gen5_read_mutual_idac_data(struct cyapa *cyapa,
+	int *gidac_mutual_max, int *gidac_mutual_min, int *gidac_mutual_ave,
+	int *lidac_mutual_max, int *lidac_mutual_min, int *lidac_mutual_ave)
+{
+	int ret;
+	int data_size;
+
+	*gidac_mutual_max = *gidac_mutual_min = *gidac_mutual_ave = 0;
+	*lidac_mutual_max = *lidac_mutual_min = *lidac_mutual_ave = 0;
+
+	data_size = 0;
+	ret = cyapa_gen5_read_idac_data(cyapa,
+		GEN5_CMD_RETRIEVE_DATA_STRUCTURE,
+		GEN5_RETRIEVE_MUTUAL_PWC_DATA,
+		&data_size,
+		gidac_mutual_max, gidac_mutual_min, gidac_mutual_ave);
+	if (ret)
+		return ret;
+
+	ret = cyapa_gen5_read_idac_data(cyapa,
+		GEN5_CMD_RETRIEVE_DATA_STRUCTURE,
+		GEN5_RETRIEVE_MUTUAL_PWC_DATA,
+		&data_size,
+		lidac_mutual_max, lidac_mutual_min, lidac_mutual_ave);
+	return ret;
+}
+
+static int cyapa_gen5_read_self_idac_data(struct cyapa *cyapa,
+		int *gidac_self_rx, int *gidac_self_tx,
+		int *lidac_self_max, int *lidac_self_min, int *lidac_self_ave)
+{
+	int ret;
+	int data_size;
+
+	*gidac_self_rx = *gidac_self_tx = 0;
+	*lidac_self_max = *lidac_self_min = *lidac_self_ave = 0;
+
+	data_size = 0;
+	ret = cyapa_gen5_read_idac_data(cyapa,
+		GEN5_CMD_RETRIEVE_DATA_STRUCTURE,
+		GEN5_RETRIEVE_SELF_CAP_PWC_DATA,
+		&data_size,
+		lidac_self_max, lidac_self_min, lidac_self_ave);
+	if (ret)
+		return ret;
+	*gidac_self_rx = *lidac_self_max;
+	*gidac_self_tx = *lidac_self_min;
+
+	ret = cyapa_gen5_read_idac_data(cyapa,
+		GEN5_CMD_RETRIEVE_DATA_STRUCTURE,
+		GEN5_RETRIEVE_SELF_CAP_PWC_DATA,
+		&data_size,
+		lidac_self_max, lidac_self_min, lidac_self_ave);
+	return ret;
+}
+
+static ssize_t cyapa_gen5_execute_panel_scan(struct cyapa *cyapa)
+{
+	int ret;
+	u8 cmd[7];
+	u8 resp_data[6];
+	int resp_len;
+
+	cmd[0] = 0x04;
+	cmd[1] = 0x00;
+	cmd[2] = 0x05;
+	cmd[3] = 0x00;
+	cmd[4] = GEN5_APP_CMD_REPORT_ID;
+	cmd[5] = 0x00;
+	cmd[6] = GEN5_CMD_EXECUTE_PANEL_SCAN;  /* Command code */
+	resp_len = 6;
+	ret = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, 7,
+			resp_data, &resp_len,
+			500, cyapa_gen5_sort_tsg_pip_app_resp_data);
+	if (ret || resp_len != 6 ||
+			!VALID_CMD_RESP_HEADER(resp_data,
+				GEN5_CMD_EXECUTE_PANEL_SCAN) ||
+			!GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) {
+		cyapa_gen5_resume_scanning(cyapa);
+		return (ret < 0) ? ret : -EAGAIN;
+	}
+
+	return 0;
+}
+
+static int cyapa_gen5_read_panel_scan_raw_data(struct cyapa *cyapa,
+		u8 cmd_code, u8 raw_data_type, int raw_data_max_num,
+		int *raw_data_max, int *raw_data_min, int *raw_data_ave,
+		u8 *buffer)
+{
+	int ret;
+	int i;
+	u8 cmd[12];
+	u8 resp_data[256];  /* Max bytes can transfer one time. */
+	int resp_len;
+	int read_elements;
+	int read_len;
+	u16 offset;
+	s32 value;
+	int sum, count;
+	int data_size;
+	s32 *intp;
+
+	if (cmd_code != GEN5_CMD_RETRIEVE_PANEL_SCAN ||
+		(raw_data_type > GEN5_PANEL_SCAN_SELF_DIFFCOUNT) ||
+		!raw_data_max || !raw_data_min || !raw_data_ave)
+		return -EINVAL;
+
+	intp = (s32 *)buffer;
+	*raw_data_max = INT_MIN;
+	*raw_data_min = INT_MAX;
+	sum = count = 0;
+	offset = 0;
+	read_elements = (256 - 10) / 4;  /* Currently, max element size is 4. */
+	read_len = read_elements * 4;
+	do {
+		cmd[0] = 0x04;
+		cmd[1] = 0x00;
+		cmd[2] = 0x0a;
+		cmd[3] = 0x00;
+		cmd[4] = GEN5_APP_CMD_REPORT_ID;
+		cmd[5] = 0x00;
+		cmd[6] = cmd_code;  /* Command code */
+		put_unaligned_le16(offset, &cmd[7]);
+		put_unaligned_le16(read_elements, &cmd[9]);
+		cmd[11] = raw_data_type;
+		resp_len = 10 + read_len;
+
+		ret = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, 12,
+			resp_data, &resp_len,
+			500, cyapa_gen5_sort_tsg_pip_app_resp_data);
+		if (ret || resp_len < 10 ||
+				!VALID_CMD_RESP_HEADER(resp_data, cmd_code) ||
+				!GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]) ||
+				resp_data[6] != raw_data_type)
+			return (ret < 0) ? ret : -EAGAIN;
+
+		read_elements = get_unaligned_le16(&resp_data[7]);
+		if (read_elements == 0)
+			break;
+
+		data_size = (resp_data[9] & GEN5_PWC_DATA_ELEMENT_SIZE_MASK);
+		offset += read_elements;
+		if (read_elements) {
+			for (i = 10;
+			     i < (read_elements * data_size + 10);
+			     i += data_size) {
+				value = cyapa_parse_structure_data(resp_data[9],
+						&resp_data[i], data_size);
+				*raw_data_min = min(value, *raw_data_min);
+				*raw_data_max = max(value, *raw_data_max);
+
+				if (intp)
+					put_unaligned_le32(value, &intp[count]);
+
+				sum += value;
+				count++;
+
+			}
+		}
+
+		if (count >= raw_data_max_num)
+			break;
+
+		read_elements = (sizeof(resp_data) - 10) / data_size;
+		read_len = read_elements * data_size;
+	} while (true);
+
+	*raw_data_ave = count ? (sum / count) : 0;
+
+	return 0;
+}
+
+static ssize_t cyapa_gen5_show_baseline(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct cyapa *cyapa = dev_get_drvdata(dev);
+	int ret, err;
+	int gidac_mutual_max, gidac_mutual_min, gidac_mutual_ave;
+	int lidac_mutual_max, lidac_mutual_min, lidac_mutual_ave;
+	int gidac_self_rx, gidac_self_tx;
+	int lidac_self_max, lidac_self_min, lidac_self_ave;
+	int raw_cap_mutual_max, raw_cap_mutual_min, raw_cap_mutual_ave;
+	int raw_cap_self_max, raw_cap_self_min, raw_cap_self_ave;
+	int mutual_diffdata_max, mutual_diffdata_min, mutual_diffdata_ave;
+	int self_diffdata_max, self_diffdata_min, self_diffdata_ave;
+	int mutual_baseline_max, mutual_baseline_min, mutual_baseline_ave;
+	int self_baseline_max, self_baseline_min, self_baseline_ave;
+
+	if (cyapa->state != CYAPA_STATE_GEN5_APP)
+		return -EBUSY;
+
+	/* 1. Suspend Scanning*/
+	ret = cyapa_gen5_suspend_scanning(cyapa);
+	if (ret)
+		return ret;
+
+	/* 2.  Read global and local mutual IDAC data. */
+	gidac_self_rx = gidac_self_tx = 0;
+	err = cyapa_gen5_read_mutual_idac_data(cyapa,
+				&gidac_mutual_max, &gidac_mutual_min,
+				&gidac_mutual_ave, &lidac_mutual_max,
+				&lidac_mutual_min, &lidac_mutual_ave);
+	if (err)
+		goto resume_scanning;
+
+	/* 3.  Read global and local self IDAC data. */
+	err = cyapa_gen5_read_self_idac_data(cyapa,
+				&gidac_self_rx, &gidac_self_tx,
+				&lidac_self_max, &lidac_self_min,
+				&lidac_self_ave);
+	if (err)
+		goto resume_scanning;
+
+	/* 4. Execuate panel scan. It must be executed before read data. */
+	err = cyapa_gen5_execute_panel_scan(cyapa);
+	if (err)
+		goto resume_scanning;
+
+	/* 5. Retrive panel scan, mutual cap raw data. */
+	err = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+				GEN5_CMD_RETRIEVE_PANEL_SCAN,
+				GEN5_PANEL_SCAN_MUTUAL_RAW_DATA,
+				cyapa->electrodes_x * cyapa->electrodes_y,
+				&raw_cap_mutual_max, &raw_cap_mutual_min,
+				&raw_cap_mutual_ave,
+				NULL);
+	if (err)
+		goto resume_scanning;
+
+	/* 6. Retrive panel scan, self cap raw data. */
+	err = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+				GEN5_CMD_RETRIEVE_PANEL_SCAN,
+				GEN5_PANEL_SCAN_SELF_RAW_DATA,
+				cyapa->electrodes_x + cyapa->electrodes_y,
+				&raw_cap_self_max, &raw_cap_self_min,
+				&raw_cap_self_ave,
+				NULL);
+	if (err)
+		goto resume_scanning;
+
+	/* 7. Retrive panel scan, mutual cap diffcount raw data. */
+	err = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+				GEN5_CMD_RETRIEVE_PANEL_SCAN,
+				GEN5_PANEL_SCAN_MUTUAL_DIFFCOUNT,
+				cyapa->electrodes_x * cyapa->electrodes_y,
+				&mutual_diffdata_max, &mutual_diffdata_min,
+				&mutual_diffdata_ave,
+				NULL);
+	if (err)
+		goto resume_scanning;
+
+	/* 8. Retrive panel scan, self cap diffcount raw data. */
+	err = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+				GEN5_CMD_RETRIEVE_PANEL_SCAN,
+				GEN5_PANEL_SCAN_SELF_DIFFCOUNT,
+				cyapa->electrodes_x + cyapa->electrodes_y,
+				&self_diffdata_max, &self_diffdata_min,
+				&self_diffdata_ave,
+				NULL);
+	if (err)
+		goto resume_scanning;
+
+	/* 9. Retrive panel scan, mutual cap baseline raw data. */
+	err = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+				GEN5_CMD_RETRIEVE_PANEL_SCAN,
+				GEN5_PANEL_SCAN_MUTUAL_BASELINE,
+				cyapa->electrodes_x * cyapa->electrodes_y,
+				&mutual_baseline_max, &mutual_baseline_min,
+				&mutual_baseline_ave,
+				NULL);
+	if (err)
+		goto resume_scanning;
+
+	/* 10. Retrive panel scan, self cap baseline raw data. */
+	err = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+				GEN5_CMD_RETRIEVE_PANEL_SCAN,
+				GEN5_PANEL_SCAN_SELF_BASELINE,
+				cyapa->electrodes_x + cyapa->electrodes_y,
+				&self_baseline_max, &self_baseline_min,
+				&self_baseline_ave,
+				NULL);
+	if (err)
+		goto resume_scanning;
+
+resume_scanning:
+	/* 11. Resume Scanning*/
+	ret = cyapa_gen5_resume_scanning(cyapa);
+	if (ret || err)
+		return ret ? ret : err;
+
+	/* 12. Output data strings */
+	ret = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d %d %d %d %d %d ",
+		gidac_mutual_min, gidac_mutual_max, gidac_mutual_ave,
+		lidac_mutual_min, lidac_mutual_max, lidac_mutual_ave,
+		gidac_self_rx, gidac_self_tx,
+		lidac_self_min, lidac_self_max, lidac_self_ave);
+	err = scnprintf(buf + ret, PAGE_SIZE - ret,
+		"%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+		raw_cap_mutual_min, raw_cap_mutual_max, raw_cap_mutual_ave,
+		raw_cap_self_min, raw_cap_self_max, raw_cap_self_ave,
+		mutual_diffdata_min, mutual_diffdata_max, mutual_diffdata_ave,
+		self_diffdata_min, self_diffdata_max, self_diffdata_ave,
+		mutual_baseline_min, mutual_baseline_max, mutual_baseline_ave,
+		self_baseline_min, self_baseline_max, self_baseline_ave);
+	return ret + err;
+}
+
 static bool cyapa_gen5_sort_system_info_data(struct cyapa *cyapa,
 		u8 *buf, int len)
 {
@@ -1946,6 +2613,9 @@ const struct cyapa_dev_ops cyapa_gen5_ops = {
 	.bl_initiate = cyapa_gen5_bl_initiate,
 	.update_fw = cyapa_gen5_do_fw_update,
 
+	.show_baseline = cyapa_gen5_show_baseline,
+	.calibrate_store = cyapa_gen5_do_calibrate,
+
 	.initialize = cyapa_gen5_initialize,
 	.uninitialize = cyapa_gen5_uninitialize,
 
-- 
1.7.9.5



^ permalink raw reply related

* [PATCH v4 11/14] input: cyapa: add gen5 trackpad device firmware update function support
From: Dudley Du @ 2014-07-17  6:58 UTC (permalink / raw)
  To: Dmitry Torokhov, Rafael J. Wysocki
  Cc: Benson Leung, Patrik Fimml, linux-input, linux-kernel, Dudley Du

Add firmware image update function supported for gen5 trackpad device,
which its function is supplied through cyapa core update_fw interface.
TEST=test on Chromebooks.

Signed-off-by: Dudley Du <dudl@cypress.com>
---
 drivers/input/mouse/Kconfig      |    2 +-
 drivers/input/mouse/cyapa_gen5.c |  294 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 295 insertions(+), 1 deletion(-)

diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 366fc7a..005d69b 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -205,7 +205,7 @@ config MOUSE_BCM5974
 
 config MOUSE_CYAPA
 	tristate "Cypress APA I2C Trackpad support"
-	depends on I2C
+	depends on I2C && CRC_ITU_T
 	help
 	  This driver adds support for Cypress All Points Addressable (APA)
 	  I2C Trackpads, including the ones used in 2012 Samsung Chromebooks.
diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c
index f311b5b..38c27b5 100644
--- a/drivers/input/mouse/cyapa_gen5.c
+++ b/drivers/input/mouse/cyapa_gen5.c
@@ -18,6 +18,7 @@
 #include <linux/completion.h>
 #include <linux/slab.h>
 #include <linux/unaligned/access_ok.h>
+#include <linux/crc-itu-t.h>
 #include "cyapa.h"
 
 
@@ -923,6 +924,86 @@ static int cyapa_gen5_state_parse(struct cyapa *cyapa, u8 *reg_data, int len)
 	return -EAGAIN;
 }
 
+static int cyapa_gen5_bl_initiate(struct cyapa *cyapa,
+		const struct firmware *fw)
+{
+	int ret = 0;
+	u16 length = 0;
+	u16 data_len = 0;
+	u16 meta_data_crc = 0;
+	u16 cmd_crc = 0;
+	u8 bl_gen5_activate[18 + CYAPA_TSG_FLASH_MAP_BLOCK_SIZE + 3];
+	int bl_gen5_activate_size = 0;
+	u8 resp_data[11];
+	int resp_len;
+	struct cyapa_tsg_bin_image *image;
+	int records_num;
+	u8 *data;
+
+	/* Try to dump all bufferred report data before send any command. */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	bl_gen5_activate_size = sizeof(bl_gen5_activate);
+	memset(bl_gen5_activate, 0, bl_gen5_activate_size);
+
+	/* Output Report Register Address[15:0] = 0004h */
+	bl_gen5_activate[0] = 0x04;
+	bl_gen5_activate[1] = 0x00;
+
+	/* Total command length[15:0] */
+	length = bl_gen5_activate_size - 2;
+	put_unaligned_le16(length, &bl_gen5_activate[2]);
+	bl_gen5_activate[4] = 0x40;  /* Report ID = 40h */
+	bl_gen5_activate[5] = 0x00;  /* RSVD = 00h */
+
+	bl_gen5_activate[6] = GEN5_SOP_KEY;  /* SOP = 01h */
+	bl_gen5_activate[7] = 0x48;  /* Command Code = 48h */
+
+	/* 8 Key bytes and block size */
+	data_len = CYAPA_TSG_BL_KEY_SIZE + CYAPA_TSG_FLASH_MAP_BLOCK_SIZE;
+	/* Data Length[15:0] */
+	put_unaligned_le16(data_len, &bl_gen5_activate[8]);
+	bl_gen5_activate[10] = 0xa5;  /* Key Byte 0 */
+	bl_gen5_activate[11] = 0x01;
+	bl_gen5_activate[12] = 0x02;  /*     .      */
+	bl_gen5_activate[13] = 0x03;  /*     .      */
+	bl_gen5_activate[14] = 0xff;  /*     .      */
+	bl_gen5_activate[15] = 0xfe;
+	bl_gen5_activate[16] = 0xfd;
+	bl_gen5_activate[17] = 0x5a;  /* Key Byte 7 */
+
+	/* Copy 60 bytes Meta Data Row Parameters */
+	image = (struct cyapa_tsg_bin_image *)fw->data;
+	records_num = (fw->size - sizeof(struct cyapa_tsg_bin_image_head)) /
+				sizeof(struct cyapa_tsg_bin_image_data_record);
+	/* APP_INTEGRITY row is always the last row block */
+	data = image->records[records_num - 1].record_data;
+	memcpy(&bl_gen5_activate[18], data, CYAPA_TSG_FLASH_MAP_METADATA_SIZE);
+
+	meta_data_crc = crc_itu_t(0xffff, &bl_gen5_activate[18],
+				CYAPA_TSG_FLASH_MAP_METADATA_SIZE);
+	/* Meta Data CRC[15:0] */
+	put_unaligned_le16(meta_data_crc,
+		&bl_gen5_activate[18 + CYAPA_TSG_FLASH_MAP_METADATA_SIZE]);
+
+	cmd_crc = crc_itu_t(0xffff, &bl_gen5_activate[6], 4 + data_len);
+	put_unaligned_le16(cmd_crc,
+		&bl_gen5_activate[bl_gen5_activate_size - 3]);  /* CRC[15:0] */
+	bl_gen5_activate[bl_gen5_activate_size - 1] = GEN5_EOP_KEY;
+
+	resp_len = sizeof(resp_data);
+	ret = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			bl_gen5_activate, sizeof(bl_gen5_activate),
+			resp_data, &resp_len, 12000,
+			cyapa_gen5_sort_tsg_pip_bl_resp_data);
+	if (ret || resp_len != GEN5_BL_INITIATE_RESP_LEN ||
+			resp_data[2] != GEN5_BL_RESP_REPORT_ID ||
+			!GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
+		return (ret < 0) ? ret : -EAGAIN;
+
+	return 0;
+}
+
 bool cyapa_gen5_sort_bl_exit_data(struct cyapa *cyapa, u8 *buf, int len)
 {
 	if (buf == NULL || len < GEN5_RESP_LENGTH_SIZE)
@@ -971,6 +1052,214 @@ static int cyapa_gen5_bl_exit(struct cyapa *cyapa)
 	return -EAGAIN;
 }
 
+static int cyapa_gen5_bl_enter(struct cyapa *cyapa)
+{
+	int ret;
+	u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2F, 0x00, 0x01 };
+	u8 resp_data[2];
+	int resp_len;
+
+	if (cyapa->input) {
+		input_unregister_device(cyapa->input);
+		cyapa->input = NULL;
+	}
+
+	ret = cyapa_poll_state(cyapa, 500);
+	if (ret < 0)
+		return ret;
+	if (cyapa->gen != CYAPA_GEN5)
+		return -EINVAL;
+
+	/* Already in Gen5 BL. Skipping exit. */
+	if (cyapa->state == CYAPA_STATE_GEN5_BL)
+		return 0;
+
+	if (cyapa->state != CYAPA_STATE_GEN5_APP)
+		return -EAGAIN;
+
+	/* Try to dump all bufferred report data before send any command. */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	/*
+	 * Send bootloader enter command to trackpad device,
+	 * after enter bootloader, the response data is two bytes of 0x00 0x00.
+	 */
+	resp_len = sizeof(resp_data);
+	memset(resp_data, 0, resp_len);
+	ret = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			5000, cyapa_gen5_sort_application_launch_data);
+	if (ret || resp_data[0] != 0x00 || resp_data[1] != 0x00)
+		return (ret < 0) ? ret : -EAGAIN;
+
+	cyapa->state = CYAPA_STATE_GEN5_BL;
+	return 0;
+}
+
+static int cyapa_gen5_check_fw(struct cyapa *cyapa, const struct firmware *fw)
+{
+	int i;
+	struct cyapa_tsg_bin_image *image;
+	int flash_records_count;
+	u16 expected_app_crc;
+	u16 expected_app_integrity_crc;
+	u16 app_crc = 0;
+	u16 app_integrity_crc = 0;
+	u16 row_num;
+	u8 *data;
+	u32 app_start;
+	u16 app_len;
+	u32 img_start;
+	u16 img_len;
+	int record_index;
+	struct device *dev = &cyapa->client->dev;
+
+	image = (struct cyapa_tsg_bin_image *)fw->data;
+	flash_records_count = (fw->size -
+			sizeof(struct cyapa_tsg_bin_image_head)) /
+			sizeof(struct cyapa_tsg_bin_image_data_record);
+
+	/* APP_INTEGRITY row is always the last row block,
+	 * and the row id must be 0x01ff */
+	row_num = get_unaligned_be16(
+			&image->records[flash_records_count - 1].row_number);
+	if (&image->records[flash_records_count - 1].flash_array_id != 0x00 &&
+			row_num != 0x01ff) {
+		dev_err(dev, "%s: invaid app_integrity data.\n", __func__);
+		return -EINVAL;
+	}
+	data = image->records[flash_records_count - 1].record_data;
+	app_start = get_unaligned_le32(&data[4]);
+	app_len = get_unaligned_le16(&data[8]);
+	expected_app_crc = get_unaligned_le16(&data[10]);
+	img_start = get_unaligned_le32(&data[16]);
+	img_len = get_unaligned_le16(&data[20]);
+	expected_app_integrity_crc = get_unaligned_le16(&data[60]);
+
+	if ((app_start + app_len + img_start + img_len) %
+			CYAPA_TSG_FW_ROW_SIZE) {
+		dev_err(dev, "%s: invaid image alignment.\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Verify app_integrity crc */
+	app_integrity_crc = crc_itu_t(0xffff, data,
+			CYAPA_TSG_APP_INTEGRITY_SIZE);
+	if (app_integrity_crc != expected_app_integrity_crc) {
+		dev_err(dev, "%s: invaid app_integrity crc.\n", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * Verify application image CRC
+	 */
+	record_index = app_start / CYAPA_TSG_FW_ROW_SIZE -
+				CYAPA_TSG_IMG_START_ROW_NUM;
+	data = (u8 *)&image->records[record_index].record_data;
+	app_crc = crc_itu_t(0xffff, data, CYAPA_TSG_FW_ROW_SIZE);
+	for (i = 1; i < (app_len / CYAPA_TSG_FW_ROW_SIZE); i++) {
+		data = (u8 *)&image->records[++record_index].record_data;
+		app_crc = crc_itu_t(app_crc, data, CYAPA_TSG_FW_ROW_SIZE);
+	}
+
+	if (app_crc != expected_app_crc) {
+		dev_err(dev, "%s: invaid firmware app crc check.\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cyapa_gen5_write_fw_block(struct cyapa *cyapa,
+		struct cyapa_tsg_bin_image_data_record *flash_record)
+{
+	u8 flash_array_id;
+	u16 flash_row_id;
+	u16 record_len;
+	u8 *record_data;
+	u8 cmd[144];  /* 13 + 128+ 3 */
+	u16 cmd_len;
+	u16 data_len;
+	u16 crc;
+	u8 resp_data[11];
+	int resp_len;
+	int ret;
+
+	flash_array_id = flash_record->flash_array_id;
+	flash_row_id = get_unaligned_be16(&flash_record->row_number);
+	record_len = get_unaligned_be16(&flash_record->record_len);
+	record_data = flash_record->record_data;
+
+	cmd_len = sizeof(cmd) - 2; /* Not include 2 bytes regisetr address. */
+	memset(cmd, 0, cmd_len + 2);
+	cmd[0] = 0x04;  /* Register address */
+	cmd[1] = 0x00;
+
+	put_unaligned_le16(cmd_len, &cmd[2]);
+	cmd[4] = 0x40;  /* Report id 40h */
+	cmd[5] = 0x00;
+
+	cmd[6] = GEN5_SOP_KEY;  /* SOP = 01h */
+	cmd[7] = 0x39;  /* Command code = 39h */
+	/* 1 (Flash Array ID) + 2 (Flash Row ID) + 128 (flash data) */
+	data_len = 3 + record_len;
+	put_unaligned_le16(data_len, &cmd[8]);
+	cmd[10] = flash_array_id;  /* Flash Array ID = 00h */
+	put_unaligned_le16(flash_row_id, &cmd[11]);
+
+	memcpy(&cmd[13], record_data, record_len);
+	crc = crc_itu_t(0xffff, &cmd[6], 4 + data_len);
+	put_unaligned_le16(crc, &cmd[2 + cmd_len - 3]);
+	cmd[2 + cmd_len - 1] = GEN5_EOP_KEY;
+
+	resp_len = sizeof(resp_data);
+	ret = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+			cmd, sizeof(cmd),
+			resp_data, &resp_len,
+			500, cyapa_gen5_sort_tsg_pip_bl_resp_data);
+	if (ret || resp_len != GEN5_BL_BLOCK_WRITE_RESP_LEN ||
+			resp_data[2] != GEN5_BL_RESP_REPORT_ID ||
+			!GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
+		return ret < 0 ? ret : -EAGAIN;
+
+	return 0;
+}
+
+static int cyapa_gen5_do_fw_update(struct cyapa *cyapa,
+		const struct firmware *fw)
+{
+	struct device *dev = &cyapa->client->dev;
+	struct cyapa_tsg_bin_image *image =
+		(struct cyapa_tsg_bin_image *)fw->data;
+	struct cyapa_tsg_bin_image_data_record *flash_record;
+	int flash_records_count;
+	int i;
+	int ret;
+
+	/* Try to dump all bufferred data if exists before send commands. */
+	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+	flash_records_count =
+		(fw->size - sizeof(struct cyapa_tsg_bin_image_head)) /
+			sizeof(struct cyapa_tsg_bin_image_data_record);
+	/*
+	 * The last flash row 0x01ff has been written through bl_initiate
+	 *  command, so DO NOT write flash 0x01ff to trackpad device.
+	 */
+	for (i = 0; i < (flash_records_count - 1); i++) {
+		flash_record = &image->records[i];
+		ret = cyapa_gen5_write_fw_block(cyapa, flash_record);
+		if (ret) {
+			dev_err(dev, "%s: Gen5 FW update aborted, %d\n",
+				__func__, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 static int cyapa_gen5_change_power_state(struct cyapa *cyapa, u8 power_state)
 {
 	int ret;
@@ -1652,6 +1941,11 @@ static void cyapa_gen5_irq_handler(struct cyapa *cyapa)
 }
 
 const struct cyapa_dev_ops cyapa_gen5_ops = {
+	.check_fw = cyapa_gen5_check_fw,
+	.bl_enter = cyapa_gen5_bl_enter,
+	.bl_initiate = cyapa_gen5_bl_initiate,
+	.update_fw = cyapa_gen5_do_fw_update,
+
 	.initialize = cyapa_gen5_initialize,
 	.uninitialize = cyapa_gen5_uninitialize,
 
-- 
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