* Re: [PATCH] serial: 8250_dw: Avoid overflow in dw8250_set_termios
From: Ed Blake @ 2018-01-15 11:27 UTC (permalink / raw)
To: Nuno Gonçalves; +Cc: gregkh, linux-kernel, linux-serial
In-Reply-To: <CAEXMXLR3HYcgm9iknfq3d31TN=Fx-bYy9dHOSouHR5Y9gsN__w@mail.gmail.com>
On 13/01/18 11:59, Nuno Gonçalves wrote:
> Dear Ed,
>
> Thanks.
>
> Tested-by: Nuno Goncalves <nunojpg@gmail.com>
>
> I just would like to report a aditional issue I find, which I am not
> sure if it is intend behaviour or not. If I set bauds 1152000,
> 1500000, 2000000, 2500000, 3000000, I always get a actually set baud
> of 1500000, because it appears to be the closest baud by my hardware,
> but if I set 3500000, then the port gets disabled.
>
> This is because up to 3000000 the closest integer divider is 1, but
> then it becomes 0, which is invalid.
>
> If we still should return the closest baud, then we must return a
> minimum of 1, and never 0, at uart_get_divisor, or
> serial8250_get_divisor.
>
> Thanks,
> Nuno
Yes, returning a divisor of zero doesn't sound very sensible. Care to
submit a patch?
--
Ed
^ permalink raw reply
* Re: [PATCH v6 02/36] openrisc: add ioremap_nocache declaration before include asm-generic/io.h and sync ioremap prototype with it.
From: Stafford Horne @ 2018-01-15 13:07 UTC (permalink / raw)
To: Greentime Hu
Cc: greentime, linux-kernel, arnd, linux-arch, tglx, jason,
marc.zyngier, robh+dt, netdev, deanbo422, devicetree, viro,
dhowells, will.deacon, daniel.lezcano, linux-serial,
geert.uytterhoeven, linus.walleij, mark.rutland, greg, ren_guo,
rdunlap, davem, jonas, stefan.kristiansson
In-Reply-To: <3e5ba33674a883b56e20b35ea9ae34990ea838c8.1515766253.git.green.hu@gmail.com>
On Mon, Jan 15, 2018 at 01:53:10PM +0800, Greentime Hu wrote:
> From: Greentime Hu <greentime@andestech.com>
>
> It will be built failed if commit id: d25ea659 is selected. This patch can fix this
> build error.
Hello,
As I mentioned last time, can you you change this to not mention the commit id?
The ID will probably change when this is actually committed. You might want to
do something like:
1. Move this commit before the ioremap_nocache change. This is because we want
each commit to result in a buildable kernel.
2. The text here and other changes could read:
A future commit for the nds32 architecture bootstrap ("asm-generic/io.h: move
ioremap_nocache/ioremap_uc/ioremap_wc/ioremap_wt out of ifndef CONFIG_MMU")
will move the ioremap_nocache out of the CONFIG_MMU ifdef. This means that
in order to suppress re-definition errors we need to setup #define's before
importing asm-generic/io.h.
Also, the change adds a prototype for ioremap where size is size_t so fix that
as well.
I hope that helps. But for the change:
Acked-by: Stafford Horne <shorne@gmail.com>
> Signed-off-by: Greentime Hu <greentime@andestech.com>
> ---
> arch/openrisc/include/asm/io.h | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/arch/openrisc/include/asm/io.h b/arch/openrisc/include/asm/io.h
> index 7c69139..6709b28 100644
> --- a/arch/openrisc/include/asm/io.h
> +++ b/arch/openrisc/include/asm/io.h
> @@ -29,13 +29,14 @@
> #define PIO_OFFSET 0
> #define PIO_MASK 0
>
> +#define ioremap_nocache ioremap_nocache
> #include <asm-generic/io.h>
> #include <asm/pgtable.h>
>
> extern void __iomem *__ioremap(phys_addr_t offset, unsigned long size,
> pgprot_t prot);
>
> -static inline void __iomem *ioremap(phys_addr_t offset, unsigned long size)
> +static inline void __iomem *ioremap(phys_addr_t offset, size_t size)
> {
> return __ioremap(offset, size, PAGE_KERNEL);
> }
> --
> 1.7.9.5
>
^ permalink raw reply
* Re: [PATCH v6 02/36] openrisc: add ioremap_nocache declaration before include asm-generic/io.h and sync ioremap prototype with it.
From: Greentime Hu @ 2018-01-15 13:28 UTC (permalink / raw)
To: Stafford Horne
Cc: Greentime, Linux Kernel Mailing List, Arnd Bergmann, linux-arch,
Thomas Gleixner, Jason Cooper, Marc Zyngier, Rob Herring, netdev,
Vincent Chen, DTML, Al Viro, David Howells, Will Deacon,
Daniel Lezcano, linux-serial, Geert Uytterhoeven, Linus Walleij,
Mark Rutland, Greg KH
In-Reply-To: <20180115130728.GG13019@lianli.shorne-pla.net>
2018-01-15 21:07 GMT+08:00 Stafford Horne <shorne@gmail.com>:
> On Mon, Jan 15, 2018 at 01:53:10PM +0800, Greentime Hu wrote:
>> From: Greentime Hu <greentime@andestech.com>
>>
>> It will be built failed if commit id: d25ea659 is selected. This patch can fix this
>> build error.
>
> Hello,
>
> As I mentioned last time, can you you change this to not mention the commit id?
> The ID will probably change when this is actually committed. You might want to
> do something like:
>
> 1. Move this commit before the ioremap_nocache change. This is because we want
> each commit to result in a buildable kernel.
>
> 2. The text here and other changes could read:
>
> A future commit for the nds32 architecture bootstrap ("asm-generic/io.h: move
> ioremap_nocache/ioremap_uc/ioremap_wc/ioremap_wt out of ifndef CONFIG_MMU")
> will move the ioremap_nocache out of the CONFIG_MMU ifdef. This means that
> in order to suppress re-definition errors we need to setup #define's before
> importing asm-generic/io.h.
>
> Also, the change adds a prototype for ioremap where size is size_t so fix that
> as well.
>
> I hope that helps. But for the change:
>
> Acked-by: Stafford Horne <shorne@gmail.com>
>
Hi, Stafford:
Thanks for the feedback.
I will move this commit before the ioremap_nocache change and use
these commit messages.
^ permalink raw reply
* Re: [PATCH] serial: 8250_dw: Avoid overflow in dw8250_set_termios
From: Andy Shevchenko @ 2018-01-15 20:30 UTC (permalink / raw)
To: Ed Blake
Cc: Greg Kroah-Hartman, nunojpg, Linux Kernel Mailing List,
linux-serial
In-Reply-To: <20180112134507.9030-1-ed.blake@sondrel.com>
On Fri, Jan 12, 2018 at 3:45 PM, Ed Blake <ed.blake@sondrel.com> wrote:
> When searching for an achievable input clock rate that is within
> +/-1.6% of an integer multiple of the target baudx16 rate, there is the
> potential to overflow the i * rate calculations.
>
> For example, on a 32-bit system with a baud rate of 4000000, the
> i * max_rate calculation will overflow if i reaches 67 without finding
> an acceptable rate.
>
> Fix this by setting the upper boundary of the loop appropriately to
> avoid overflow.
>
It seems the change broke Bluetooth on some Intel platforms. I'm not
sure yet, but
see here:
https://github.com/Dunedan/mbp-2016-linux/issues/29#issuecomment-357583782
And for me the change is odd.
Why by the way neither Heikki, nor me had been Cc'ed with the change?
Perhaps I need to submit something to maintainer data base.
For now I would rather revert it and give another try.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [patch v16 3/4] Documentation: jtag: Add bindings for Aspeed SoC 24xx and 25xx families JTAG master driver
From: Joel Stanley @ 2018-01-15 20:31 UTC (permalink / raw)
To: Oleksandr Shamray
Cc: Greg KH, Arnd Bergmann, Linux Kernel Mailing List, Linux ARM,
devicetree, OpenBMC Maillist, Jiří Pírko,
Tobias Klauser, linux-serial-u79uwXL29TY76Z2rM5mHXA,
Vadim Pasternak, system-sw-low-level-VPRAkNaXOzVWk0Htik3J/w,
Rob Herring, openocd-devel-owner-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
linux-api-u79uwXL29TY76Z2rM5mHXA, David S . Miller,
mchehab-DgEjT+Ai2ygdnm+yROfE0A, Jiri Pirko
In-Reply-To: <1515776909-29894-4-git-send-email-oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
On Fri, Jan 12, 2018 at 11:08 AM, Oleksandr Shamray
<oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org> wrote:
> It has been tested on Mellanox system with BMC equipped with
> Aspeed 2520 SoC for programming CPLD devices.
>
> Signed-off-by: Oleksandr Shamray <oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
> Signed-off-by: Jiri Pirko <jiri-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
For the device tree bindings:
Acked-by: Joel Stanley <joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>
> diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c
> index 9cbd6da..f679041 100644
> --- a/drivers/jtag/jtag-aspeed.c
> +++ b/drivers/jtag/jtag-aspeed.c
> @@ -12,6 +12,7 @@
> #include <linux/module.h>
> #include <linux/of_address.h>
> #include <linux/platform_device.h>
> +#include <linux/reset.h>
Oops, this is in the wrong patch.
> #include <linux/slab.h>
> #include <linux/types.h>
> #include <uapi/linux/jtag.h>
^ permalink raw reply
* Re: [patch v16 1/4] drivers: jtag: Add JTAG core driver
From: Julia Cartwright @ 2018-01-15 20:51 UTC (permalink / raw)
To: Oleksandr Shamray
Cc: system-sw-low-level, devicetree, jiri, arnd, vadimp, gregkh,
openbmc, linux-kernel, openocd-devel-owner, Jiri Pirko, robh+dt,
joel, linux-serial, linux-api, tklauser, mchehab, davem,
linux-arm-kernel
In-Reply-To: <1515776909-29894-2-git-send-email-oleksandrs@mellanox.com>
Hello Oleksandr-
I have a few comments below.
On Fri, Jan 12, 2018 at 07:08:26PM +0200, Oleksandr Shamray wrote:
> Initial patch for JTAG driver
> JTAG class driver provide infrastructure to support hardware/software
> JTAG platform drivers. It provide user layer API interface for flashing
> and debugging external devices which equipped with JTAG interface
> using standard transactions.
>
> Driver exposes set of IOCTL to user space for:
> - XFER:
> - SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
> - SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
> - RUNTEST (Forces the IEEE 1149.1 bus to a run state for a specified
> number of clocks).
> - SIOCFREQ/GIOCFREQ for setting and reading JTAG frequency.
>
> Driver core provides set of internal APIs for allocation and
> registration:
> - jtag_register;
> - jtag_unregister;
> - jtag_alloc;
> - jtag_free;
>
> Platform driver on registration with jtag-core creates the next
> entry in dev folder:
> /dev/jtagX
>
> Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
> Signed-off-by: Jiri Pirko <jiri@mellanox.com>
> Acked-by: Philippe Ombredanne <pombredanne@nexb.com>
[..]
> +static long jtag_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> +{
> + struct jtag *jtag = file->private_data;
> + struct jtag_run_test_idle idle;
> + struct jtag_xfer xfer;
> + u8 *xfer_data;
> + u32 data_size;
> + u32 value;
> + int err;
> +
> + if (!arg)
> + return -EINVAL;
> +
> + switch (cmd) {
> + case JTAG_GIOCFREQ:
> + if (!jtag->ops->freq_get)
> + err = -EOPNOTSUPP;
> +
> + err = jtag->ops->freq_get(jtag, &value);
> + if (err)
> + break;
> +
> + if (put_user(value, (__u32 *)arg))
> + err = -EFAULT;
> + break;
> +
> + case JTAG_SIOCFREQ:
> + if (!jtag->ops->freq_set)
> + return -EOPNOTSUPP;
> +
> + if (get_user(value, (__u32 *)arg))
> + return -EFAULT;
> + if (value == 0)
> + return -EINVAL;
> +
> + err = jtag->ops->freq_set(jtag, value);
> + break;
> +
> + case JTAG_IOCRUNTEST:
> + if (!jtag->ops->idle)
> + return -EOPNOTSUPP;
> +
> + if (copy_from_user(&idle, (void *)arg,
> + sizeof(struct jtag_run_test_idle)))
> + return -EFAULT;
> +
> + if (idle.endstate > JTAG_STATE_PAUSEDR)
> + return -EINVAL;
> +
> + err = jtag->ops->idle(jtag, &idle);
> + break;
> +
> + case JTAG_IOCXFER:
> + if (!jtag->ops->xfer)
Are all ops optional? That seems bizarre. I would have expected at
least one callback to be required.
[..]
> +static int jtag_open(struct inode *inode, struct file *file)
> +{
> + struct jtag *jtag = container_of(file->private_data, struct jtag,
> + miscdev);
> +
> + if (mutex_lock_interruptible(&jtag->open_lock))
> + return -ERESTARTSYS;
> +
> + if (jtag->opened) {
> + mutex_unlock(&jtag->open_lock);
> + return -EBUSY;
> + }
> +
> + nonseekable_open(inode, file);
> + file->private_data = jtag;
These two can be moved out of the lock.
> + jtag->opened = true;
> + mutex_unlock(&jtag->open_lock);
> + return 0;
> +}
> +
> +static int jtag_release(struct inode *inode, struct file *file)
> +{
> + struct jtag *jtag = file->private_data;
> +
> + mutex_lock(&jtag->open_lock);
> + jtag->opened = false;
> + mutex_unlock(&jtag->open_lock);
> + return 0;
> +}
> +
> +static const struct file_operations jtag_fops = {
> + .owner = THIS_MODULE,
> + .open = jtag_open,
> + .release = jtag_release,
> + .llseek = noop_llseek,
> + .unlocked_ioctl = jtag_ioctl,
> +};
> +
> +struct jtag *jtag_alloc(size_t priv_size, const struct jtag_ops *ops)
> +{
> + struct jtag *jtag;
> +
> + jtag = kzalloc(sizeof(*jtag), GFP_KERNEL);
Did you mean to allocate: sizeof(*jtag) + priv_size?
> + if (!jtag)
> + return NULL;
> +
> + jtag->ops = ops;
> + return jtag;
> +}
> +EXPORT_SYMBOL_GPL(jtag_alloc);
> +
> +void jtag_free(struct jtag *jtag)
> +{
> + kfree(jtag);
> +}
> +EXPORT_SYMBOL_GPL(jtag_free);
> +
> +int jtag_register(struct jtag *jtag)
> +{
> + char *name;
> + int err;
> + int id;
> +
> + id = ida_simple_get(&jtag_ida, 0, 0, GFP_KERNEL);
> + if (id < 0)
> + return id;
> +
> + jtag->id = id;
> +
> + name = kzalloc(MAX_JTAG_NAME_LEN, GFP_KERNEL);
> + if (!name) {
> + err = -ENOMEM;
> + goto err_jtag_alloc;
> + }
> +
> + err = snprintf(name, MAX_JTAG_NAME_LEN, "jtag%d", id);
> + if (err < 0)
> + goto err_jtag_name;
> +
> + mutex_init(&jtag->open_lock);
> + jtag->miscdev.fops = &jtag_fops;
> + jtag->miscdev.minor = MISC_DYNAMIC_MINOR;
> + jtag->miscdev.name = name;
> +
> + err = misc_register(&jtag->miscdev);
> + if (err)
> + dev_err(jtag->dev, "Unable to register device\n");
> + else
> + return 0;
> + jtag->opened = false;
Well, this code flow is just confusing.
I suggest a redo with:
err = misc_register(&jtag->miscdev);
if (err) {
dev_err(jtag->dev, "Unable to register device\n");
goto err_jtag_name;
}
If you need to initialize 'opened', do it prior to misc_register.
Thanks,
Julia
^ permalink raw reply
* RE: [patch v16 1/4] drivers: jtag: Add JTAG core driver
From: Oleksandr Shamray @ 2018-01-16 7:04 UTC (permalink / raw)
To: Julia Cartwright
Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org,
arnd-r2nGTMty4D4@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
openbmc-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org,
joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org,
jiri-rHqAuBHg3fBzbRFIqnYvSA@public.gmane.org,
tklauser-93Khv+1bN0NyDzI6CaY1VQ@public.gmane.org,
linux-serial-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Vadim Pasternak, system-sw-low-level,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
openocd-devel-owner-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org,
linux-ap
In-Reply-To: <20180115205142.GD2818-DmwFwOShA74B9AHHLWeGtNQXobZC6xk2@public.gmane.org>
Hi Julia
> -----Original Message-----
> From: Julia Cartwright [mailto:juliac-SPobxgzVcRdvFhSgSjN+lA@public.gmane.org]
> Sent: 15 января 2018 г. 22:52
> To: Oleksandr Shamray <oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
> Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org; arnd-r2nGTMty4D4@public.gmane.org; linux-
> kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org;
> devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; openbmc-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org; joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org;
> jiri-rHqAuBHg3fBzbRFIqnYvSA@public.gmane.org; tklauser-93Khv+1bN0NyDzI6CaY1VQ@public.gmane.org; linux-serial-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; Vadim
> Pasternak <vadimp-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>; system-sw-low-level <system-sw-low-
> level-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>; robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org; openocd-devel-
> owner-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org; linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org;
> davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org; mchehab-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org; Jiri Pirko
> <jiri-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
> Subject: Re: [patch v16 1/4] drivers: jtag: Add JTAG core driver
>
> Hello Oleksandr-
>
> I have a few comments below.
>
> On Fri, Jan 12, 2018 at 07:08:26PM +0200, Oleksandr Shamray wrote:
> > Initial patch for JTAG driver
> > JTAG class driver provide infrastructure to support hardware/software
> > JTAG platform drivers. It provide user layer API interface for
> > flashing and debugging external devices which equipped with JTAG
> > interface using standard transactions.
> >
[..]
> > + break;
> > +
> > + case JTAG_IOCXFER:
> > + if (!jtag->ops->xfer)
>
> Are all ops optional? That seems bizarre. I would have expected at least one
> callback to be required.
You right. But I'm preferred to check pointers before use it.
>
> [..]
> > +static int jtag_open(struct inode *inode, struct file *file) {
> > + struct jtag *jtag = container_of(file->private_data, struct jtag,
> > + miscdev);
> > +
> > + if (mutex_lock_interruptible(&jtag->open_lock))
> > + return -ERESTARTSYS;
> > +
> > + if (jtag->opened) {
> > + mutex_unlock(&jtag->open_lock);
> > + return -EBUSY;
> > + }
> > +
> > + nonseekable_open(inode, file);
> > + file->private_data = jtag;
>
> These two can be moved out of the lock.
Agree.
>
> > + jtag->opened = true;
> > + mutex_unlock(&jtag->open_lock);
> > + return 0;
> > +}
> > +
> > +static int jtag_release(struct inode *inode, struct file *file) {
> > + struct jtag *jtag = file->private_data;
> > +
> > + mutex_lock(&jtag->open_lock);
> > + jtag->opened = false;
> > + mutex_unlock(&jtag->open_lock);
> > + return 0;
> > +}
> > +
> > +static const struct file_operations jtag_fops = {
> > + .owner = THIS_MODULE,
> > + .open = jtag_open,
> > + .release = jtag_release,
> > + .llseek = noop_llseek,
> > + .unlocked_ioctl = jtag_ioctl,
> > +};
> > +
> > +struct jtag *jtag_alloc(size_t priv_size, const struct jtag_ops *ops)
> > +{
> > + struct jtag *jtag;
> > +
> > + jtag = kzalloc(sizeof(*jtag), GFP_KERNEL);
>
> Did you mean to allocate: sizeof(*jtag) + priv_size?
Thank, it's a mistake.
>
> > + if (!jtag)
> > + return NULL;
> > +
> > + jtag->ops = ops;
> > + return jtag;
> > +}
> > +EXPORT_SYMBOL_GPL(jtag_alloc);
> > +
> > +void jtag_free(struct jtag *jtag)
> > +{
> > + kfree(jtag);
> > +}
> > +EXPORT_SYMBOL_GPL(jtag_free);
> > +
> > +int jtag_register(struct jtag *jtag)
> > +{
> > + char *name;
> > + int err;
> > + int id;
> > +
> > + id = ida_simple_get(&jtag_ida, 0, 0, GFP_KERNEL);
> > + if (id < 0)
> > + return id;
> > +
> > + jtag->id = id;
> > +
> > + name = kzalloc(MAX_JTAG_NAME_LEN, GFP_KERNEL);
> > + if (!name) {
> > + err = -ENOMEM;
> > + goto err_jtag_alloc;
> > + }
> > +
> > + err = snprintf(name, MAX_JTAG_NAME_LEN, "jtag%d", id);
> > + if (err < 0)
> > + goto err_jtag_name;
> > +
> > + mutex_init(&jtag->open_lock);
> > + jtag->miscdev.fops = &jtag_fops;
> > + jtag->miscdev.minor = MISC_DYNAMIC_MINOR;
> > + jtag->miscdev.name = name;
> > +
> > + err = misc_register(&jtag->miscdev);
> > + if (err)
> > + dev_err(jtag->dev, "Unable to register device\n");
> > + else
> > + return 0;
> > + jtag->opened = false;
>
> Well, this code flow is just confusing.
>
> I suggest a redo with:
>
> err = misc_register(&jtag->miscdev);
> if (err) {
> dev_err(jtag->dev, "Unable to register device\n");
> goto err_jtag_name;
> }
>
> If you need to initialize 'opened', do it prior to misc_register.
>
Thanks
> Thanks,
> Julia
Thanks for Review.
Br,
Oleksandr
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [patch v17 0/4] JTAG driver introduction
From: Oleksandr Shamray @ 2018-01-16 7:18 UTC (permalink / raw)
To: gregkh, arnd
Cc: system-sw-low-level, devicetree, jiri, vadimp, linux-api, openbmc,
linux-kernel, openocd-devel-owner, robh+dt, joel, linux-serial,
Oleksandr Shamray, tklauser, mchehab, davem, linux-arm-kernel
When a need raise up to use JTAG interface for system's devices
programming or CPU debugging, usually the user layer
application implements jtag protocol by bit-bang or using a
proprietary connection to vendor hardware.
This method can be slow and not generic.
We propose to implement general JTAG interface and infrastructure
to communicate with user layer application. In such way, we can
have the standard JTAG interface core part and separation from
specific HW implementation.
This allow new capability to debug the CPU or program system's
device via BMC without additional devices nor cost.
This patch purpose is to add JTAG master core infrastructure by
defining new JTAG class and provide generic JTAG interface
to allow hardware specific drivers to connect this interface.
This will enable all JTAG drivers to use the common interface
part and will have separate for hardware implementation.
The JTAG (Joint Test Action Group) core driver provides minimal generic
JTAG interface, which can be used by hardware specific JTAG master
controllers. By providing common interface for the JTAG controllers,
user space device programing is hardware independent.
Modern SoC which in use for embedded system' equipped with
internal JTAG master interface.
This interface is used for programming and debugging system's
hardware components, like CPLD, FPGA, CPU, voltage and
industrial controllers.
Firmware for such devices can be upgraded through JTAG interface during
Runtime. The JTAG standard support for multiple devices programming,
is in case their lines are daisy-chained together.
For example, systems which equipped with host CPU, BMC SoC or/and
number of programmable devices are capable to connect a pin and
select system components dynamically for programming and debugging,
This is using by the BMC which is equipped with internal SoC master
controller.
For example:
BMC JTAG master --> pin selected to CPLDs chain for programming (filed
upgrade, production)
BMC JTAG master --> pin selected to voltage monitors for programming
(field upgrade, production)
BMC JTAG master --> pin selected to host CPU (on-site debugging
and developers debugging)
For example, we can have application in user space which using calls
to JTAG driver executes CPLD programming directly from SVF file
The JTAG standard (IEEE 1149.1) defines the next connector pins:
- TDI (Test Data In);
- TDO (Test Data Out);
- TCK (Test Clock);
- TMS (Test Mode Select);
- TRST (Test Reset) (Optional);
The SoC equipped with JTAG master controller, performs
device programming on command or vector level. For example
a file in a standard SVF (Serial Vector Format) that contains
boundary scan vectors, can be used by sending each vector
to the JTAG interface and the JTAG controller will execute
the programming.
Initial version provides the system calls set for:
- SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
- SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
- RUNTEST (Forces the IEEE 1149.1 bus to a run state for a specified
number of clocks.
SoC which are not equipped with JTAG master interface, can be built
on top of JTAG core driver infrastructure, by applying bit-banging of
TDI, TDO, TCK and TMS pins within the hardware specific driver.
Oleksandr Shamray (4):
drivers: jtag: Add JTAG core driver
drivers: jtag: Add Aspeed SoC 24xx and 25xx families JTAG master
driver
Documentation: jtag: Add bindings for Aspeed SoC 24xx and 25xx
families JTAG master driver
Documentation: jtag: Add ABI documentation
Documentation/ABI/testing/jtag-dev | 27 +
.../devicetree/bindings/jtag/aspeed-jtag.txt | 22 +
Documentation/ioctl/ioctl-number.txt | 2 +
MAINTAINERS | 10 +
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/jtag/Kconfig | 30 +
drivers/jtag/Makefile | 2 +
drivers/jtag/jtag-aspeed.c | 786 ++++++++++++++++++++
drivers/jtag/jtag.c | 284 +++++++
include/linux/jtag.h | 41 +
include/uapi/linux/jtag.h | 104 +++
12 files changed, 1311 insertions(+), 0 deletions(-)
create mode 100644 Documentation/ABI/testing/jtag-dev
create mode 100644 Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
create mode 100644 drivers/jtag/Kconfig
create mode 100644 drivers/jtag/Makefile
create mode 100644 drivers/jtag/jtag-aspeed.c
create mode 100644 drivers/jtag/jtag.c
create mode 100644 include/linux/jtag.h
create mode 100644 include/uapi/linux/jtag.h
^ permalink raw reply
* [patch v17 1/4] drivers: jtag: Add JTAG core driver
From: Oleksandr Shamray @ 2018-01-16 7:18 UTC (permalink / raw)
To: gregkh, arnd
Cc: linux-kernel, linux-arm-kernel, devicetree, openbmc, joel, jiri,
tklauser, linux-serial, vadimp, system-sw-low-level, robh+dt,
openocd-devel-owner, linux-api, davem, mchehab, Oleksandr Shamray,
Jiri Pirko
In-Reply-To: <1516087139-7510-1-git-send-email-oleksandrs@mellanox.com>
Initial patch for JTAG driver
JTAG class driver provide infrastructure to support hardware/software
JTAG platform drivers. It provide user layer API interface for flashing
and debugging external devices which equipped with JTAG interface
using standard transactions.
Driver exposes set of IOCTL to user space for:
- XFER:
- SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
- SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
- RUNTEST (Forces the IEEE 1149.1 bus to a run state for a specified
number of clocks).
- SIOCFREQ/GIOCFREQ for setting and reading JTAG frequency.
Driver core provides set of internal APIs for allocation and
registration:
- jtag_register;
- jtag_unregister;
- jtag_alloc;
- jtag_free;
Platform driver on registration with jtag-core creates the next
entry in dev folder:
/dev/jtagX
Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Acked-by: Philippe Ombredanne <pombredanne@nexb.com>
---
v16->v17
Comments pointed by Julia Cartwright <juliac@eso.teric.us>
- Fix memory allocation on jtag alloc
- Move out unnecessary form lock on jtag open
- Rework jtag register behavior
v15->v16
Commen ts pointed by Florian Fainelli <f.fainelli@gmail.com>
- move check jtag->ops->* in ioctl before get_user()
- change error type -EINVAL --> -EBUSY on open already opened jtag
- remove unnecessary ARCH_DMA_MINALIGN flag from kzalloc
- remove define ARCH_DMA_MINALIGN
v14->v15
v13->v14
Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
- Change style of head block comment from /**/ to //
v12->v13
Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
- Change jtag.c licence type to
SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
and reorder line with license in description
v11->v12
Comments pointed by Greg KH <gregkh@linuxfoundation.org>
- Change jtag.h licence type to
SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
and reorder line with license in description
Chip Bilbrey <chip@bilbrey.org>
- Remove Apeed reference from uapi jtag.h header
- Remove access mode from xfer and idle transactions
- Add new ioctl JTAG_SIOCMODE for set hw mode
- Add only one open per JTAG port blocking with mutex blocking
v10->v11
Notifications from kbuild test robot <lkp@intel.com>
- include types.h headeri to jtag.h
- fix incompatible type of xfer callback
- remove rdundant class defination
- Fix return order in case of xfer error
V9->v10
Comments pointed by Greg KH <gregkh@linuxfoundation.org>
- remove unnecessary alignment for pirv data
- move jtag_copy_to_user and jtag_copy_from_user code just to ioctl
- move int jtag_run_test_idle_op and jtag_xfer_op code
just to ioctl
- change return error codes to more applicable
- add missing error checks
- fix error check order in ioctl
- remove unnecessary blank lines
- add param validation to ioctl
- remove compat_ioctl
- remove only one open per JTAG port blocking.
User will care about this.
- Fix idr memory leak on jtag_exit
- change cdev device type to misc
V8->v9
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- use get_user() instead of __get_user().
- change jtag->open type from int to atomic_t
- remove spinlock on jtg_open
- remove mutex on jtag_register
- add unregister_chrdev_region on jtag_init err
- add unregister_chrdev_region on jtag_exit
- remove unnecessary pointer casts
- add *data parameter to xfer function prototype
v7->v8
Comments pointed by Moritz Fischer <moritz.fischer@ettus.com>
- Fix misspelling s/friver/driver
v6->v7
Notifications from kbuild test robot <lkp@intel.com>
- Remove include asm/types.h from jtag.h
- Add include <linux/types.h> to jtag.c
v5->v6
v4->v5
v3->v4
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- change transaction pointer tdio type to __u64
- change internal status type from enum to __u32
- reorder jtag_xfer members to avoid the implied padding
- add __packed attribute to jtag_xfer and jtag_run_test_idle
v2->v3
Notifications from kbuild test robot <lkp@intel.com>
- Change include path to <linux/types.h> in jtag.h
v1->v2
Comments pointed by Greg KH <gregkh@linuxfoundation.org>
- Change license type from GPLv2/BSD to GPLv2
- Change type of variables which crossed user/kernel to __type
- Remove "default n" from Kconfig
Comments pointed by Andrew Lunn <andrew@lunn.ch>
- Change list_add_tail in jtag_unregister to list_del
Comments pointed by Neil Armstrong <narmstrong@baylibre.com>
- Add SPDX-License-Identifier instead of license text
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- Change __copy_to_user to memdup_user
- Change __put_user to put_user
- Change type of variables to __type for compatible 32 and 64-bit systems
- Add check for maximum xfer data size
- Change lookup data mechanism to get jtag data from inode
- Add .compat_ioctl to file ops
- Add mem alignment for jtag priv data
Comments pointed by Tobias Klauser <tklauser@distanz.ch>
- Change function names to avoid match with variable types
- Fix description for jtag_ru_test_idle in uapi jtag.h
- Fix misprints IDEL/IDLE, trough/through
---
Documentation/ioctl/ioctl-number.txt | 2 +
MAINTAINERS | 10 ++
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/jtag/Kconfig | 16 ++
drivers/jtag/Makefile | 1 +
drivers/jtag/jtag.c | 284 ++++++++++++++++++++++++++++++++++
include/linux/jtag.h | 41 +++++
include/uapi/linux/jtag.h | 104 +++++++++++++
9 files changed, 461 insertions(+), 0 deletions(-)
create mode 100644 drivers/jtag/Kconfig
create mode 100644 drivers/jtag/Makefile
create mode 100644 drivers/jtag/jtag.c
create mode 100644 include/linux/jtag.h
create mode 100644 include/uapi/linux/jtag.h
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 3e3fdae..1af2508 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -321,6 +321,8 @@ Code Seq#(hex) Include File Comments
0xB0 all RATIO devices in development:
<mailto:vgo@ratio.de>
0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca>
+0xB2 00-0f linux/jtag.h JTAG driver
+ <mailto:oleksandrs@mellanox.com>
0xB3 00 linux/mmc/ioctl.h
0xB4 00-0F linux/gpio.h <mailto:linux-gpio@vger.kernel.org>
0xB5 00-0F uapi/linux/rpmsg.h <mailto:linux-remoteproc@vger.kernel.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index b46c9ce..42aac3a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7513,6 +7513,16 @@ L: linux-serial@vger.kernel.org
S: Maintained
F: drivers/tty/serial/jsm/
+JTAG SUBSYSTEM
+M: Oleksandr Shamray <oleksandrs@mellanox.com>
+M: Vadim Pasternak <vadimp@mellanox.com>
+S: Maintained
+F: include/linux/jtag.h
+F: include/uapi/linux/jtag.h
+F: drivers/jtag/
+F: Documentation/devicetree/bindings/jtag/
+F: Documentation/ABI/testing/jtag-cdev
+
K10TEMP HARDWARE MONITORING DRIVER
M: Clemens Ladisch <clemens@ladisch.de>
L: linux-hwmon@vger.kernel.org
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 152744c..414a34b 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -211,4 +211,6 @@ source "drivers/mux/Kconfig"
source "drivers/opp/Kconfig"
+source "drivers/jtag/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index e06f7f6..6d50f74 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -184,3 +184,4 @@ obj-$(CONFIG_FPGA) += fpga/
obj-$(CONFIG_FSI) += fsi/
obj-$(CONFIG_TEE) += tee/
obj-$(CONFIG_MULTIPLEXER) += mux/
+obj-$(CONFIG_JTAG) += jtag/
diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
new file mode 100644
index 0000000..0fad1a3
--- /dev/null
+++ b/drivers/jtag/Kconfig
@@ -0,0 +1,16 @@
+menuconfig JTAG
+ tristate "JTAG support"
+ ---help---
+ This provides basic core functionality support for jtag class devices
+ Hardware equipped with JTAG microcontroller which can be built
+ on top of this drivers. Driver exposes the set of IOCTL to the
+ user space for:
+ SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
+ SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
+ RUNTEST (Forces IEEE 1149.1 bus to a run state for specified
+ number of clocks).
+
+ If you want this support, you should say Y here.
+
+ To compile this driver as a module, choose M here: the module will
+ be called jtag.
diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile
new file mode 100644
index 0000000..af37493
--- /dev/null
+++ b/drivers/jtag/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_JTAG) += jtag.o
diff --git a/drivers/jtag/jtag.c b/drivers/jtag/jtag.c
new file mode 100644
index 0000000..4109b61
--- /dev/null
+++ b/drivers/jtag/jtag.c
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0
+// drivers/jtag/jtag.c
+//
+// Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+// Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/jtag.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/rtnetlink.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <uapi/linux/jtag.h>
+
+#define JTAG_NAME "jtag0"
+#define MAX_JTAG_NAME_LEN (sizeof("jtag") + 5)
+
+struct jtag {
+ struct miscdevice miscdev;
+ struct device *dev;
+ const struct jtag_ops *ops;
+ int id;
+ bool opened;
+ struct mutex open_lock;
+ unsigned long priv[0];
+};
+
+static DEFINE_IDA(jtag_ida);
+
+void *jtag_priv(struct jtag *jtag)
+{
+ return jtag->priv;
+}
+EXPORT_SYMBOL_GPL(jtag_priv);
+
+static long jtag_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct jtag *jtag = file->private_data;
+ struct jtag_run_test_idle idle;
+ struct jtag_xfer xfer;
+ u8 *xfer_data;
+ u32 data_size;
+ u32 value;
+ int err;
+
+ if (!arg)
+ return -EINVAL;
+
+ switch (cmd) {
+ case JTAG_GIOCFREQ:
+ if (!jtag->ops->freq_get)
+ err = -EOPNOTSUPP;
+
+ err = jtag->ops->freq_get(jtag, &value);
+ if (err)
+ break;
+
+ if (put_user(value, (__u32 *)arg))
+ err = -EFAULT;
+ break;
+
+ case JTAG_SIOCFREQ:
+ if (!jtag->ops->freq_set)
+ return -EOPNOTSUPP;
+
+ if (get_user(value, (__u32 *)arg))
+ return -EFAULT;
+ if (value == 0)
+ return -EINVAL;
+
+ err = jtag->ops->freq_set(jtag, value);
+ break;
+
+ case JTAG_IOCRUNTEST:
+ if (!jtag->ops->idle)
+ return -EOPNOTSUPP;
+
+ if (copy_from_user(&idle, (void *)arg,
+ sizeof(struct jtag_run_test_idle)))
+ return -EFAULT;
+
+ if (idle.endstate > JTAG_STATE_PAUSEDR)
+ return -EINVAL;
+
+ err = jtag->ops->idle(jtag, &idle);
+ break;
+
+ case JTAG_IOCXFER:
+ if (!jtag->ops->xfer)
+ return -EOPNOTSUPP;
+
+ if (copy_from_user(&xfer, (void *)arg,
+ sizeof(struct jtag_xfer)))
+ return -EFAULT;
+
+ if (xfer.length >= JTAG_MAX_XFER_DATA_LEN)
+ return -EINVAL;
+
+ if (xfer.type > JTAG_SDR_XFER)
+ return -EINVAL;
+
+ if (xfer.direction > JTAG_WRITE_XFER)
+ return -EINVAL;
+
+ if (xfer.endstate > JTAG_STATE_PAUSEDR)
+ return -EINVAL;
+
+ data_size = DIV_ROUND_UP(xfer.length, BITS_PER_BYTE);
+ xfer_data = memdup_user(u64_to_user_ptr(xfer.tdio), data_size);
+
+ if (!xfer_data)
+ return -EFAULT;
+
+ err = jtag->ops->xfer(jtag, &xfer, xfer_data);
+ if (err) {
+ kfree(xfer_data);
+ return -EFAULT;
+ }
+
+ err = copy_to_user(u64_to_user_ptr(xfer.tdio),
+ (void *)(xfer_data), data_size);
+
+ if (err) {
+ kfree(xfer_data);
+ return -EFAULT;
+ }
+
+ kfree(xfer_data);
+ if (copy_to_user((void *)arg, &xfer, sizeof(struct jtag_xfer)))
+ return -EFAULT;
+ break;
+
+ case JTAG_GIOCSTATUS:
+ if (!jtag->ops->status_get)
+ return -EOPNOTSUPP;
+
+ err = jtag->ops->status_get(jtag, &value);
+ if (err)
+ break;
+
+ err = put_user(value, (__u32 *)arg);
+ if (err)
+ err = -EFAULT;
+ break;
+ case JTAG_SIOCMODE:
+ if (!jtag->ops->mode_set)
+ return -EOPNOTSUPP;
+
+ if (get_user(value, (__u32 *)arg))
+ return -EFAULT;
+ if (value == 0)
+ return -EINVAL;
+
+ err = jtag->ops->mode_set(jtag, value);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return err;
+}
+
+static int jtag_open(struct inode *inode, struct file *file)
+{
+ struct jtag *jtag = container_of(file->private_data, struct jtag,
+ miscdev);
+
+ if (mutex_lock_interruptible(&jtag->open_lock))
+ return -ERESTARTSYS;
+
+ if (jtag->opened) {
+ mutex_unlock(&jtag->open_lock);
+ return -EBUSY;
+ }
+ jtag->opened = true;
+ mutex_unlock(&jtag->open_lock);
+
+ nonseekable_open(inode, file);
+ file->private_data = jtag;
+ return 0;
+}
+
+static int jtag_release(struct inode *inode, struct file *file)
+{
+ struct jtag *jtag = file->private_data;
+
+ mutex_lock(&jtag->open_lock);
+ jtag->opened = false;
+ mutex_unlock(&jtag->open_lock);
+ return 0;
+}
+
+static const struct file_operations jtag_fops = {
+ .owner = THIS_MODULE,
+ .open = jtag_open,
+ .release = jtag_release,
+ .llseek = noop_llseek,
+ .unlocked_ioctl = jtag_ioctl,
+};
+
+struct jtag *jtag_alloc(size_t priv_size, const struct jtag_ops *ops)
+{
+ struct jtag *jtag;
+
+ jtag = kzalloc(sizeof(*jtag) + priv_size, GFP_KERNEL);
+ if (!jtag)
+ return NULL;
+
+ jtag->ops = ops;
+ return jtag;
+}
+EXPORT_SYMBOL_GPL(jtag_alloc);
+
+void jtag_free(struct jtag *jtag)
+{
+ kfree(jtag);
+}
+EXPORT_SYMBOL_GPL(jtag_free);
+
+int jtag_register(struct jtag *jtag)
+{
+ char *name;
+ int err;
+ int id;
+
+ id = ida_simple_get(&jtag_ida, 0, 0, GFP_KERNEL);
+ if (id < 0)
+ return id;
+
+ jtag->id = id;
+ jtag->opened = false;
+
+ name = kzalloc(MAX_JTAG_NAME_LEN, GFP_KERNEL);
+ if (!name) {
+ err = -ENOMEM;
+ goto err_jtag_alloc;
+ }
+
+ err = snprintf(name, MAX_JTAG_NAME_LEN, "jtag%d", id);
+ if (err < 0)
+ goto err_jtag_name;
+
+ mutex_init(&jtag->open_lock);
+ jtag->miscdev.fops = &jtag_fops;
+ jtag->miscdev.minor = MISC_DYNAMIC_MINOR;
+ jtag->miscdev.name = name;
+
+ err = misc_register(&jtag->miscdev);
+ if (err) {
+ dev_err(jtag->dev, "Unable to register device\n");
+ goto err_jtag_name;
+ }
+ return 0;
+
+err_jtag_name:
+ kfree(name);
+err_jtag_alloc:
+ ida_simple_remove(&jtag_ida, id);
+ return err;
+}
+EXPORT_SYMBOL_GPL(jtag_register);
+
+void jtag_unregister(struct jtag *jtag)
+{
+ misc_deregister(&jtag->miscdev);
+ kfree(jtag->miscdev.name);
+ ida_simple_remove(&jtag_ida, jtag->id);
+}
+EXPORT_SYMBOL_GPL(jtag_unregister);
+
+static void __exit jtag_exit(void)
+{
+ ida_destroy(&jtag_ida);
+}
+
+module_exit(jtag_exit);
+
+MODULE_AUTHOR("Oleksandr Shamray <oleksandrs@mellanox.com>");
+MODULE_DESCRIPTION("Generic jtag support");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/jtag.h b/include/linux/jtag.h
new file mode 100644
index 0000000..918cfe0
--- /dev/null
+++ b/include/linux/jtag.h
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0
+// include/linux/jtag.h - JTAG class driver
+//
+// Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+// Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
+
+#ifndef __JTAG_H
+#define __JTAG_H
+
+#include <uapi/linux/jtag.h>
+
+#define jtag_u64_to_ptr(arg) ((void *)(uintptr_t)arg)
+
+#define JTAG_MAX_XFER_DATA_LEN 65535
+
+struct jtag;
+/**
+ * struct jtag_ops - callbacks for jtag control functions:
+ *
+ * @freq_get: get frequency function. Filled by device driver
+ * @freq_set: set frequency function. Filled by device driver
+ * @status_get: set status function. Filled by device driver
+ * @idle: set JTAG to idle state function. Filled by device driver
+ * @xfer: send JTAG xfer function. Filled by device driver
+ */
+struct jtag_ops {
+ int (*freq_get)(struct jtag *jtag, u32 *freq);
+ int (*freq_set)(struct jtag *jtag, u32 freq);
+ int (*status_get)(struct jtag *jtag, u32 *state);
+ int (*idle)(struct jtag *jtag, struct jtag_run_test_idle *idle);
+ int (*xfer)(struct jtag *jtag, struct jtag_xfer *xfer, u8 *xfer_data);
+ int (*mode_set)(struct jtag *jtag, u32 mode_mask);
+};
+
+void *jtag_priv(struct jtag *jtag);
+int jtag_register(struct jtag *jtag);
+void jtag_unregister(struct jtag *jtag);
+struct jtag *jtag_alloc(size_t priv_size, const struct jtag_ops *ops);
+void jtag_free(struct jtag *jtag);
+
+#endif /* __JTAG_H */
diff --git a/include/uapi/linux/jtag.h b/include/uapi/linux/jtag.h
new file mode 100644
index 0000000..cda2520
--- /dev/null
+++ b/include/uapi/linux/jtag.h
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+// include/uapi/linux/jtag.h - JTAG class driver uapi
+//
+// Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+// Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
+
+#ifndef __UAPI_LINUX_JTAG_H
+#define __UAPI_LINUX_JTAG_H
+
+#include <linux/types.h>
+/*
+ * JTAG_XFER_HW_MODE: JTAG hardware mode. Used to set HW drived or bitbang
+ * mode. This is bitmask param of ioctl JTAG_SIOCMODE command
+ */
+#define JTAG_XFER_HW_MODE 1
+
+/**
+ * enum jtag_endstate:
+ *
+ * @JTAG_STATE_IDLE: JTAG state machine IDLE state
+ * @JTAG_STATE_PAUSEIR: JTAG state machine PAUSE_IR state
+ * @JTAG_STATE_PAUSEDR: JTAG state machine PAUSE_DR state
+ */
+enum jtag_endstate {
+ JTAG_STATE_IDLE,
+ JTAG_STATE_PAUSEIR,
+ JTAG_STATE_PAUSEDR,
+};
+
+/**
+ * enum jtag_xfer_type:
+ *
+ * @JTAG_SIR_XFER: SIR transfer
+ * @JTAG_SDR_XFER: SDR transfer
+ */
+enum jtag_xfer_type {
+ JTAG_SIR_XFER,
+ JTAG_SDR_XFER,
+};
+
+/**
+ * enum jtag_xfer_direction:
+ *
+ * @JTAG_READ_XFER: read transfer
+ * @JTAG_WRITE_XFER: write transfer
+ */
+enum jtag_xfer_direction {
+ JTAG_READ_XFER,
+ JTAG_WRITE_XFER,
+};
+
+/**
+ * struct jtag_run_test_idle - forces JTAG state machine to
+ * RUN_TEST/IDLE state
+ *
+ * @reset: 0 - run IDLE/PAUSE from current state
+ * 1 - go through TEST_LOGIC/RESET state before IDLE/PAUSE
+ * @end: completion flag
+ * @tck: clock counter
+ *
+ * Structure represents interface to JTAG device for jtag idle
+ * execution.
+ */
+struct jtag_run_test_idle {
+ __u8 reset;
+ __u8 endstate;
+ __u8 tck;
+};
+
+/**
+ * struct jtag_xfer - jtag xfer:
+ *
+ * @type: transfer type
+ * @direction: xfer direction
+ * @length: xfer bits len
+ * @tdio : xfer data array
+ * @endir: xfer end state
+ *
+ * Structure represents interface to JTAG device for jtag sdr xfer
+ * execution.
+ */
+struct jtag_xfer {
+ __u8 type;
+ __u8 direction;
+ __u8 endstate;
+ __u32 length;
+ __u64 tdio;
+};
+
+/* ioctl interface */
+#define __JTAG_IOCTL_MAGIC 0xb2
+
+#define JTAG_IOCRUNTEST _IOW(__JTAG_IOCTL_MAGIC, 0,\
+ struct jtag_run_test_idle)
+#define JTAG_SIOCFREQ _IOW(__JTAG_IOCTL_MAGIC, 1, unsigned int)
+#define JTAG_GIOCFREQ _IOR(__JTAG_IOCTL_MAGIC, 2, unsigned int)
+#define JTAG_IOCXFER _IOWR(__JTAG_IOCTL_MAGIC, 3, struct jtag_xfer)
+#define JTAG_GIOCSTATUS _IOWR(__JTAG_IOCTL_MAGIC, 4, enum jtag_endstate)
+#define JTAG_SIOCMODE _IOW(__JTAG_IOCTL_MAGIC, 5, unsigned int)
+
+#define JTAG_FIRST_MINOR 0
+#define JTAG_MAX_DEVICES 32
+
+#endif /* __UAPI_LINUX_JTAG_H */
--
1.7.1
^ permalink raw reply related
* [patch v17 2/4] drivers: jtag: Add Aspeed SoC 24xx and 25xx families JTAG master driver
From: Oleksandr Shamray @ 2018-01-16 7:18 UTC (permalink / raw)
To: gregkh, arnd
Cc: linux-kernel, linux-arm-kernel, devicetree, openbmc, joel, jiri,
tklauser, linux-serial, vadimp, system-sw-low-level, robh+dt,
openocd-devel-owner, linux-api, davem, mchehab, Oleksandr Shamray,
Jiri Pirko
In-Reply-To: <1516087139-7510-1-git-send-email-oleksandrs@mellanox.com>
Driver adds support of Aspeed 2500/2400 series SOC JTAG master controller.
Driver implements the following jtag ops:
- freq_get;
- freq_set;
- status_get;
- idle;
- xfer;
It has been tested on Mellanox system with BMC equipped with
Aspeed 2520 SoC for programming CPLD devices.
Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Philippe Ombredanne <pombredanne@nexb.com>
Acked-by: Joel Stanley <joel@jms.id.au>
---
v16->v17
v15->v16
Comments pointed by Joel Stanley <joel.stan@gmail.com>
- Add reset_control on Jtag init/deinit
v14->v15
Comments pointed by Joel Stanley <joel.stan@gmail.com>
- Add ARCH_ASPEED || COMPILE_TEST to Kconfig
- remove unused offset variable
- remove "aspeed_jtag" from dev_err and dev_dbg messages
- change clk_prepare_enable initialisation order
v13->v14
Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
- Change style of head block comment from /**/ to //
v12->v13
Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
- Change jtag-aspeed.c licence type to
SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
and reorder line with license in description
Comments pointed by Kun Yi <kunyi@google.com>
- Changed capability check for aspeed,ast2400-jtag/ast200-jtag
v11->v12
Comments pointed by Chip Bilbrey <chip@bilbrey.org>
- Remove access mode from xfer and idle transactions
- Add new ioctl JTAG_SIOCMODE for set hw mode
v10->v11
v9->v10
V8->v9
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- add *data parameter to xfer function prototype
v7->v8
Comments pointed by Joel Stanley <joel.stan@gmail.com>
- aspeed_jtag_init replace goto to return;
- change input variables type from __u32 to u32
in functios freq_get, freq_set, status_get
- change sm_ variables type from char to u8
- in jatg_init add disable clocks on error case
- remove release_mem_region on error case
- remove devm_free_irq on jtag_deinit
- Fix misspelling Disabe/Disable
- Change compatible string to ast2400 and ast2000
v6->v7
Notifications from kbuild test robot <lkp@intel.com>
- Add include <linux/types.h> to jtag-asapeed.c
v5->v6
v4->v5
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- Added HAS_IOMEM dependence in Kconfig to avoid
"undefined reference to `devm_ioremap_resource'" error,
because in some arch this not supported
v3->v4
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- change transaction pointer tdio type to __u64
- change internal status type from enum to __u32
v2->v3
v1->v2
Comments pointed by Greg KH <gregkh@linuxfoundation.org>
- change license type from GPLv2/BSD to GPLv2
Comments pointed by Neil Armstrong <narmstrong@baylibre.com>
- Add clk_prepare_enable/clk_disable_unprepare in clock init/deinit
- Change .compatible to soc-specific compatible names
aspeed,aspeed4000-jtag/aspeed5000-jtag
- Added dt-bindings
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- Reorder functions and removed the forward declarations
- Add static const qualifier to state machine states transitions
- Change .compatible to soc-specific compatible names
aspeed,aspeed4000-jtag/aspeed5000-jtag
- Add dt-bindings
Comments pointed by Randy Dunlap <rdunlap@infradead.org>
- Change module name jtag-aspeed in description in Kconfig
Comments pointed by kbuild test robot <lkp@intel.com>
- Remove invalid include <asm/mach-types.h>
- add resource_size instead of calculation
---
drivers/jtag/Kconfig | 14 +
drivers/jtag/Makefile | 1 +
drivers/jtag/jtag-aspeed.c | 785 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 800 insertions(+), 0 deletions(-)
create mode 100644 drivers/jtag/jtag-aspeed.c
diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
index 0fad1a3..63ddf1f 100644
--- a/drivers/jtag/Kconfig
+++ b/drivers/jtag/Kconfig
@@ -14,3 +14,17 @@ menuconfig JTAG
To compile this driver as a module, choose M here: the module will
be called jtag.
+
+menuconfig JTAG_ASPEED
+ tristate "Aspeed SoC JTAG controller support"
+ depends on JTAG && HAS_IOMEM
+ depends on ARCH_ASPEED || COMPILE_TEST
+ ---help---
+ This provides a support for Aspeed JTAG device, equipped on
+ Aspeed SoC 24xx and 25xx families. Drivers allows programming
+ of hardware devices, connected to SoC through the JTAG interface.
+
+ If you want this support, you should say Y here.
+
+ To compile this driver as a module, choose M here: the module will
+ be called jtag-aspeed.
diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile
index af37493..04a855e 100644
--- a/drivers/jtag/Makefile
+++ b/drivers/jtag/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_JTAG) += jtag.o
+obj-$(CONFIG_JTAG_ASPEED) += jtag-aspeed.o
diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c
new file mode 100644
index 0000000..9cbd6da
--- /dev/null
+++ b/drivers/jtag/jtag-aspeed.c
@@ -0,0 +1,785 @@
+// SPDX-License-Identifier: GPL-2.0
+// drivers/jtag/aspeed-jtag.c
+//
+// Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+// Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/jtag.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <uapi/linux/jtag.h>
+
+#define ASPEED_JTAG_DATA 0x00
+#define ASPEED_JTAG_INST 0x04
+#define ASPEED_JTAG_CTRL 0x08
+#define ASPEED_JTAG_ISR 0x0C
+#define ASPEED_JTAG_SW 0x10
+#define ASPEED_JTAG_TCK 0x14
+#define ASPEED_JTAG_EC 0x18
+
+#define ASPEED_JTAG_DATA_MSB 0x01
+#define ASPEED_JTAG_DATA_CHUNK_SIZE 0x20
+
+/* ASPEED_JTAG_CTRL: Engine Control */
+#define ASPEED_JTAG_CTL_ENG_EN BIT(31)
+#define ASPEED_JTAG_CTL_ENG_OUT_EN BIT(30)
+#define ASPEED_JTAG_CTL_FORCE_TMS BIT(29)
+#define ASPEED_JTAG_CTL_INST_LEN(x) ((x) << 20)
+#define ASPEED_JTAG_CTL_LASPEED_INST BIT(17)
+#define ASPEED_JTAG_CTL_INST_EN BIT(16)
+#define ASPEED_JTAG_CTL_DR_UPDATE BIT(10)
+#define ASPEED_JTAG_CTL_DATA_LEN(x) ((x) << 4)
+#define ASPEED_JTAG_CTL_LASPEED_DATA BIT(1)
+#define ASPEED_JTAG_CTL_DATA_EN BIT(0)
+
+/* ASPEED_JTAG_ISR : Interrupt status and enable */
+#define ASPEED_JTAG_ISR_INST_PAUSE BIT(19)
+#define ASPEED_JTAG_ISR_INST_COMPLETE BIT(18)
+#define ASPEED_JTAG_ISR_DATA_PAUSE BIT(17)
+#define ASPEED_JTAG_ISR_DATA_COMPLETE BIT(16)
+#define ASPEED_JTAG_ISR_INST_PAUSE_EN BIT(3)
+#define ASPEED_JTAG_ISR_INST_COMPLETE_EN BIT(2)
+#define ASPEED_JTAG_ISR_DATA_PAUSE_EN BIT(1)
+#define ASPEED_JTAG_ISR_DATA_COMPLETE_EN BIT(0)
+#define ASPEED_JTAG_ISR_INT_EN_MASK GENMASK(3, 0)
+#define ASPEED_JTAG_ISR_INT_MASK GENMASK(19, 16)
+
+/* ASPEED_JTAG_SW : Software Mode and Status */
+#define ASPEED_JTAG_SW_MODE_EN BIT(19)
+#define ASPEED_JTAG_SW_MODE_TCK BIT(18)
+#define ASPEED_JTAG_SW_MODE_TMS BIT(17)
+#define ASPEED_JTAG_SW_MODE_TDIO BIT(16)
+
+/* ASPEED_JTAG_TCK : TCK Control */
+#define ASPEED_JTAG_TCK_DIVISOR_MASK GENMASK(10, 0)
+#define ASPEED_JTAG_TCK_GET_DIV(x) ((x) & ASPEED_JTAG_TCK_DIVISOR_MASK)
+
+/* ASPEED_JTAG_EC : Controller set for go to IDLE */
+#define ASPEED_JTAG_EC_GO_IDLE BIT(0)
+
+#define ASPEED_JTAG_IOUT_LEN(len) (ASPEED_JTAG_CTL_ENG_EN |\
+ ASPEED_JTAG_CTL_ENG_OUT_EN |\
+ ASPEED_JTAG_CTL_INST_LEN(len))
+
+#define ASPEED_JTAG_DOUT_LEN(len) (ASPEED_JTAG_CTL_ENG_EN |\
+ ASPEED_JTAG_CTL_ENG_OUT_EN |\
+ ASPEED_JTAG_CTL_DATA_LEN(len))
+
+#define ASPEED_JTAG_TCK_WAIT 10
+#define ASPEED_JTAG_RESET_CNTR 10
+
+#define ASPEED_JTAG_NAME "jtag-aspeed"
+
+struct aspeed_jtag {
+ void __iomem *reg_base;
+ struct device *dev;
+ struct clk *pclk;
+ enum jtag_endstate status;
+ int irq;
+ struct reset_control *rst;
+ u32 flag;
+ wait_queue_head_t jtag_wq;
+ u32 mode;
+};
+
+static char *end_status_str[] = {"idle", "ir pause", "drpause"};
+
+static u32 aspeed_jtag_read(struct aspeed_jtag *aspeed_jtag, u32 reg)
+{
+ return readl(aspeed_jtag->reg_base + reg);
+}
+
+static void
+aspeed_jtag_write(struct aspeed_jtag *aspeed_jtag, u32 val, u32 reg)
+{
+ writel(val, aspeed_jtag->reg_base + reg);
+}
+
+static int aspeed_jtag_freq_set(struct jtag *jtag, u32 freq)
+{
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+ unsigned long apb_frq;
+ u32 tck_val;
+ u16 div;
+
+ apb_frq = clk_get_rate(aspeed_jtag->pclk);
+ div = (apb_frq % freq == 0) ? (apb_frq / freq) - 1 : (apb_frq / freq);
+ tck_val = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_TCK);
+ aspeed_jtag_write(aspeed_jtag,
+ (tck_val & ASPEED_JTAG_TCK_DIVISOR_MASK) | div,
+ ASPEED_JTAG_TCK);
+ return 0;
+}
+
+static int aspeed_jtag_freq_get(struct jtag *jtag, u32 *frq)
+{
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+ u32 pclk;
+ u32 tck;
+
+ pclk = clk_get_rate(aspeed_jtag->pclk);
+ tck = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_TCK);
+ *frq = pclk / (ASPEED_JTAG_TCK_GET_DIV(tck) + 1);
+
+ return 0;
+}
+
+static int aspeed_jtag_mode_set(struct jtag *jtag, u32 mode)
+{
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+
+ aspeed_jtag->mode = mode;
+ return 0;
+}
+
+static void aspeed_jtag_sw_delay(struct aspeed_jtag *aspeed_jtag, int cnt)
+{
+ int i;
+
+ for (i = 0; i < cnt; i++)
+ aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_SW);
+}
+
+static char aspeed_jtag_tck_cycle(struct aspeed_jtag *aspeed_jtag,
+ u8 tms, u8 tdi)
+{
+ char tdo = 0;
+
+ /* TCK = 0 */
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ (tms * ASPEED_JTAG_SW_MODE_TMS) |
+ (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW);
+
+ aspeed_jtag_sw_delay(aspeed_jtag, ASPEED_JTAG_TCK_WAIT);
+
+ /* TCK = 1 */
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ ASPEED_JTAG_SW_MODE_TCK |
+ (tms * ASPEED_JTAG_SW_MODE_TMS) |
+ (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW);
+
+ if (aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_SW) &
+ ASPEED_JTAG_SW_MODE_TDIO)
+ tdo = 1;
+
+ aspeed_jtag_sw_delay(aspeed_jtag, ASPEED_JTAG_TCK_WAIT);
+
+ /* TCK = 0 */
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ (tms * ASPEED_JTAG_SW_MODE_TMS) |
+ (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW);
+ return tdo;
+}
+
+static void aspeed_jtag_wait_instruction_pause(struct aspeed_jtag *aspeed_jtag)
+{
+ wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
+ ASPEED_JTAG_ISR_INST_PAUSE);
+ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_PAUSE;
+}
+
+static void
+aspeed_jtag_wait_instruction_complete(struct aspeed_jtag *aspeed_jtag)
+{
+ wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
+ ASPEED_JTAG_ISR_INST_COMPLETE);
+ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_COMPLETE;
+}
+
+static void
+aspeed_jtag_wait_data_pause_complete(struct aspeed_jtag *aspeed_jtag)
+{
+ wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
+ ASPEED_JTAG_ISR_DATA_PAUSE);
+ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_DATA_PAUSE;
+}
+
+static void aspeed_jtag_wait_data_complete(struct aspeed_jtag *aspeed_jtag)
+{
+ wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
+ ASPEED_JTAG_ISR_DATA_COMPLETE);
+ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_DATA_COMPLETE;
+}
+
+static void aspeed_jtag_sm_cycle(struct aspeed_jtag *aspeed_jtag, const u8 *tms,
+ int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ aspeed_jtag_tck_cycle(aspeed_jtag, tms[i], 0);
+}
+
+static void aspeed_jtag_run_test_idle_sw(struct aspeed_jtag *aspeed_jtag,
+ struct jtag_run_test_idle *runtest)
+{
+ static const u8 sm_pause_irpause[] = {1, 1, 1, 1, 0, 1, 0};
+ static const u8 sm_pause_drpause[] = {1, 1, 1, 0, 1, 0};
+ static const u8 sm_idle_irpause[] = {1, 1, 0, 1, 0};
+ static const u8 sm_idle_drpause[] = {1, 0, 1, 0};
+ static const u8 sm_pause_idle[] = {1, 1, 0};
+ int i;
+
+ /* SW mode from idle/pause-> to pause/idle */
+ if (runtest->reset) {
+ for (i = 0; i < ASPEED_JTAG_RESET_CNTR; i++)
+ aspeed_jtag_tck_cycle(aspeed_jtag, 1, 0);
+ }
+
+ switch (aspeed_jtag->status) {
+ case JTAG_STATE_IDLE:
+ switch (runtest->endstate) {
+ case JTAG_STATE_PAUSEIR:
+ /* ->DRSCan->IRSCan->IRCap->IRExit1->PauseIR */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_idle_irpause,
+ sizeof(sm_idle_irpause));
+
+ aspeed_jtag->status = JTAG_STATE_PAUSEIR;
+ break;
+ case JTAG_STATE_PAUSEDR:
+ /* ->DRSCan->DRCap->DRExit1->PauseDR */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_idle_drpause,
+ sizeof(sm_idle_drpause));
+
+ aspeed_jtag->status = JTAG_STATE_PAUSEDR;
+ break;
+ case JTAG_STATE_IDLE:
+ /* IDLE */
+ aspeed_jtag_tck_cycle(aspeed_jtag, 0, 0);
+ aspeed_jtag->status = JTAG_STATE_IDLE;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case JTAG_STATE_PAUSEIR:
+ /* Fall-through */
+ case JTAG_STATE_PAUSEDR:
+ /* From IR/DR Pause */
+ switch (runtest->endstate) {
+ case JTAG_STATE_PAUSEIR:
+ /*
+ * to Exit2 IR/DR->Updt IR/DR->DRSCan->IRSCan->IRCap->
+ * IRExit1->PauseIR
+ */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_irpause,
+ sizeof(sm_pause_irpause));
+
+ aspeed_jtag->status = JTAG_STATE_PAUSEIR;
+ break;
+ case JTAG_STATE_PAUSEDR:
+ /*
+ * to Exit2 IR/DR->Updt IR/DR->DRSCan->DRCap->
+ * DRExit1->PauseDR
+ */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_drpause,
+ sizeof(sm_pause_drpause));
+ aspeed_jtag->status = JTAG_STATE_PAUSEDR;
+ break;
+ case JTAG_STATE_IDLE:
+ /* to Exit2 IR/DR->Updt IR/DR->IDLE */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_idle,
+ sizeof(sm_pause_idle));
+ aspeed_jtag->status = JTAG_STATE_IDLE;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ dev_err(aspeed_jtag->dev, "aspeed_jtag_run_test_idle error\n");
+ break;
+ }
+
+ /* Stay on IDLE for at least TCK cycle */
+ for (i = 0; i < runtest->tck; i++)
+ aspeed_jtag_tck_cycle(aspeed_jtag, 0, 0);
+}
+
+/**
+ * aspeed_jtag_run_test_idle:
+ * JTAG reset: generates at least 9 TMS high and 1 TMS low to force
+ * devices into Run-Test/Idle State.
+ */
+static int aspeed_jtag_idle(struct jtag *jtag,
+ struct jtag_run_test_idle *runtest)
+{
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+
+ dev_dbg(aspeed_jtag->dev, "runtest, status:%d, mode:%s, state:%s, reset:%d, tck:%d\n",
+ aspeed_jtag->status,
+ aspeed_jtag->mode & JTAG_XFER_HW_MODE ? "HW" : "SW",
+ end_status_str[runtest->endstate], runtest->reset,
+ runtest->tck);
+
+ if (!(aspeed_jtag->mode & JTAG_XFER_HW_MODE)) {
+ aspeed_jtag_run_test_idle_sw(aspeed_jtag, runtest);
+ return 0;
+ }
+
+ /* Disable sw mode */
+ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW);
+ /* x TMS high + 1 TMS low */
+ if (runtest->reset)
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_CTL_ENG_EN |
+ ASPEED_JTAG_CTL_ENG_OUT_EN |
+ ASPEED_JTAG_CTL_FORCE_TMS, ASPEED_JTAG_CTRL);
+ else
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_EC_GO_IDLE,
+ ASPEED_JTAG_EC);
+
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
+
+ aspeed_jtag->status = JTAG_STATE_IDLE;
+ return 0;
+}
+
+static void aspeed_jtag_xfer_sw(struct aspeed_jtag *aspeed_jtag,
+ struct jtag_xfer *xfer, unsigned long *data)
+{
+ unsigned long remain_xfer = xfer->length;
+ unsigned long shift_bits = 0;
+ unsigned long index = 0;
+ unsigned long tdi;
+ char tdo;
+
+ if (xfer->direction == JTAG_READ_XFER)
+ tdi = UINT_MAX;
+ else
+ tdi = data[index];
+
+ while (remain_xfer > 1) {
+ tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 0,
+ tdi & ASPEED_JTAG_DATA_MSB);
+ data[index] |= tdo << (shift_bits %
+ ASPEED_JTAG_DATA_CHUNK_SIZE);
+
+ tdi >>= 1;
+ shift_bits++;
+ remain_xfer--;
+
+ if (shift_bits % ASPEED_JTAG_DATA_CHUNK_SIZE == 0) {
+ dev_dbg(aspeed_jtag->dev, "R/W data[%lu]:%lx\n",
+ index, data[index]);
+
+ tdo = 0;
+ index++;
+
+ if (xfer->direction == JTAG_READ_XFER)
+ tdi = UINT_MAX;
+ else
+ tdi = data[index];
+ }
+ }
+
+ tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 1, tdi & ASPEED_JTAG_DATA_MSB);
+ data[index] |= tdo << (shift_bits % ASPEED_JTAG_DATA_CHUNK_SIZE);
+}
+
+static void aspeed_jtag_xfer_push_data(struct aspeed_jtag *aspeed_jtag,
+ enum jtag_xfer_type type, u32 bits_len)
+{
+ dev_dbg(aspeed_jtag->dev, "shift bits %d\n", bits_len);
+
+ if (type == JTAG_SIR_XFER) {
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_IOUT_LEN(bits_len),
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len) |
+ ASPEED_JTAG_CTL_INST_EN, ASPEED_JTAG_CTRL);
+ } else {
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len),
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len) |
+ ASPEED_JTAG_CTL_DATA_EN, ASPEED_JTAG_CTRL);
+ }
+}
+
+static void aspeed_jtag_xfer_push_data_last(struct aspeed_jtag *aspeed_jtag,
+ enum jtag_xfer_type type,
+ u32 shift_bits,
+ enum jtag_endstate endstate)
+{
+ if (endstate != JTAG_STATE_IDLE) {
+ if (type == JTAG_SIR_XFER) {
+ dev_dbg(aspeed_jtag->dev, "IR Keep Pause\n");
+
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_IOUT_LEN(shift_bits),
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_IOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_INST_EN,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_wait_instruction_pause(aspeed_jtag);
+ } else {
+ dev_dbg(aspeed_jtag->dev, "DR Keep Pause\n");
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_DOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_DR_UPDATE,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_DOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_DR_UPDATE |
+ ASPEED_JTAG_CTL_DATA_EN,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_wait_data_pause_complete(aspeed_jtag);
+ }
+ } else {
+ if (type == JTAG_SIR_XFER) {
+ dev_dbg(aspeed_jtag->dev, "IR go IDLE\n");
+
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_IOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_LASPEED_INST,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_IOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_LASPEED_INST |
+ ASPEED_JTAG_CTL_INST_EN,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_wait_instruction_complete(aspeed_jtag);
+ } else {
+ dev_dbg(aspeed_jtag->dev, "DR go IDLE\n");
+
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_DOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_LASPEED_DATA,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_DOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_LASPEED_DATA |
+ ASPEED_JTAG_CTL_DATA_EN,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_wait_data_complete(aspeed_jtag);
+ }
+ }
+}
+
+static void aspeed_jtag_xfer_hw(struct aspeed_jtag *aspeed_jtag,
+ struct jtag_xfer *xfer, unsigned long *data)
+{
+ unsigned long remain_xfer = xfer->length;
+ unsigned long index = 0;
+ char shift_bits;
+ u32 data_reg;
+
+ data_reg = xfer->type == JTAG_SIR_XFER ?
+ ASPEED_JTAG_INST : ASPEED_JTAG_DATA;
+ while (remain_xfer) {
+ if (xfer->direction == JTAG_WRITE_XFER) {
+ dev_dbg(aspeed_jtag->dev, "W dr->dr_data[%lu]:%lx\n",
+ index, data[index]);
+
+ aspeed_jtag_write(aspeed_jtag, data[index], data_reg);
+ } else {
+ aspeed_jtag_write(aspeed_jtag, 0, data_reg);
+ }
+
+ if (remain_xfer > ASPEED_JTAG_DATA_CHUNK_SIZE) {
+ shift_bits = ASPEED_JTAG_DATA_CHUNK_SIZE;
+
+ /*
+ * Read bytes were not equals to column length
+ * and go to Pause-DR
+ */
+ aspeed_jtag_xfer_push_data(aspeed_jtag, xfer->type,
+ shift_bits);
+ } else {
+ /*
+ * Read bytes equals to column length =>
+ * Update-DR
+ */
+ shift_bits = remain_xfer;
+ aspeed_jtag_xfer_push_data_last(aspeed_jtag, xfer->type,
+ shift_bits,
+ xfer->endstate);
+ }
+
+ if (xfer->direction == JTAG_READ_XFER) {
+ if (shift_bits < ASPEED_JTAG_DATA_CHUNK_SIZE) {
+ data[index] = aspeed_jtag_read(aspeed_jtag,
+ data_reg);
+
+ data[index] >>= ASPEED_JTAG_DATA_CHUNK_SIZE -
+ shift_bits;
+ } else {
+ data[index] = aspeed_jtag_read(aspeed_jtag,
+ data_reg);
+ }
+ dev_dbg(aspeed_jtag->dev, "R dr->dr_data[%lu]:%lx\n",
+ index, data[index]);
+ }
+
+ remain_xfer = remain_xfer - shift_bits;
+ index++;
+ dev_dbg(aspeed_jtag->dev, "remain_xfer %lu\n", remain_xfer);
+ }
+}
+
+static int aspeed_jtag_xfer(struct jtag *jtag, struct jtag_xfer *xfer,
+ u8 *xfer_data)
+{
+ static const u8 sm_update_shiftir[] = {1, 1, 0, 0};
+ static const u8 sm_update_shiftdr[] = {1, 0, 0};
+ static const u8 sm_pause_idle[] = {1, 1, 0};
+ static const u8 sm_pause_update[] = {1, 1};
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+ unsigned long *data = (unsigned long *)xfer_data;
+ unsigned long remain_xfer = xfer->length;
+ char dbg_str[256];
+ int pos = 0;
+ int i;
+
+ for (i = 0; i <= xfer->length / BITS_PER_BYTE; i++) {
+ pos += snprintf(&dbg_str[pos], sizeof(dbg_str) - pos,
+ "0x%02x ", xfer_data[i]);
+ }
+
+ dev_dbg(aspeed_jtag->dev, " %s %s xfer, mode:%s, END:%d, len:%lu, TDI[%s]\n",
+ xfer->type == JTAG_SIR_XFER ? "SIR" : "SDR",
+ xfer->direction == JTAG_READ_XFER ? "READ" : "WRITE",
+ aspeed_jtag->mode & JTAG_XFER_HW_MODE ? "HW" : "SW",
+ xfer->endstate, remain_xfer, dbg_str);
+
+ if (!(aspeed_jtag->mode & JTAG_XFER_HW_MODE)) {
+ /* SW mode */
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
+
+ if (aspeed_jtag->status != JTAG_STATE_IDLE) {
+ /*IR/DR Pause->Exit2 IR / DR->Update IR /DR */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_update,
+ sizeof(sm_pause_update));
+ }
+
+ if (xfer->type == JTAG_SIR_XFER)
+ /* ->IRSCan->CapIR->ShiftIR */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_update_shiftir,
+ sizeof(sm_update_shiftir));
+ else
+ /* ->DRScan->DRCap->DRShift */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_update_shiftdr,
+ sizeof(sm_update_shiftdr));
+
+ aspeed_jtag_xfer_sw(aspeed_jtag, xfer, data);
+
+ /* DIPause/DRPause */
+ aspeed_jtag_tck_cycle(aspeed_jtag, 0, 0);
+
+ if (xfer->endstate == JTAG_STATE_IDLE) {
+ /* ->DRExit2->DRUpdate->IDLE */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_idle,
+ sizeof(sm_pause_idle));
+ }
+ } else {
+ /* hw mode */
+ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW);
+ aspeed_jtag_xfer_hw(aspeed_jtag, xfer, data);
+ }
+
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
+ aspeed_jtag->status = xfer->endstate;
+ return 0;
+}
+
+static int aspeed_jtag_status_get(struct jtag *jtag, u32 *status)
+{
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+
+ *status = aspeed_jtag->status;
+ return 0;
+}
+
+static irqreturn_t aspeed_jtag_interrupt(s32 this_irq, void *dev_id)
+{
+ struct aspeed_jtag *aspeed_jtag = dev_id;
+ irqreturn_t ret;
+ u32 status;
+
+ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR);
+ dev_dbg(aspeed_jtag->dev, "status %x\n", status);
+
+ if (status & ASPEED_JTAG_ISR_INT_MASK) {
+ aspeed_jtag_write(aspeed_jtag,
+ (status & ASPEED_JTAG_ISR_INT_MASK)
+ | (status & ASPEED_JTAG_ISR_INT_EN_MASK),
+ ASPEED_JTAG_ISR);
+ aspeed_jtag->flag |= status & ASPEED_JTAG_ISR_INT_MASK;
+ }
+
+ if (aspeed_jtag->flag) {
+ wake_up_interruptible(&aspeed_jtag->jtag_wq);
+ ret = IRQ_HANDLED;
+ } else {
+ dev_err(aspeed_jtag->dev, "irq status:%x\n",
+ status);
+ ret = IRQ_NONE;
+ }
+ return ret;
+}
+
+int aspeed_jtag_init(struct platform_device *pdev,
+ struct aspeed_jtag *aspeed_jtag)
+{
+ struct resource *res;
+ int err;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ aspeed_jtag->reg_base = devm_ioremap_resource(aspeed_jtag->dev, res);
+ if (IS_ERR(aspeed_jtag->reg_base))
+ return -ENOMEM;
+
+ aspeed_jtag->pclk = devm_clk_get(aspeed_jtag->dev, NULL);
+ if (IS_ERR(aspeed_jtag->pclk)) {
+ dev_err(aspeed_jtag->dev, "devm_clk_get failed\n");
+ return PTR_ERR(aspeed_jtag->pclk);
+ }
+
+ aspeed_jtag->irq = platform_get_irq(pdev, 0);
+ if (aspeed_jtag->irq < 0) {
+ dev_err(aspeed_jtag->dev, "no irq specified\n");
+ return -ENOENT;
+ }
+
+ clk_prepare_enable(aspeed_jtag->pclk);
+
+ aspeed_jtag->rst = devm_reset_control_get_shared(aspeed_jtag->dev,
+ NULL);
+ if (IS_ERR(aspeed_jtag->rst)) {
+ dev_err(aspeed_jtag->dev,
+ "missing or invalid reset controller device tree entry");
+ return PTR_ERR(aspeed_jtag->rst);
+ }
+ reset_control_deassert(aspeed_jtag->rst);
+
+ /* Enable clock */
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_CTL_ENG_EN |
+ ASPEED_JTAG_CTL_ENG_OUT_EN, ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
+
+ err = devm_request_irq(aspeed_jtag->dev, aspeed_jtag->irq,
+ aspeed_jtag_interrupt, 0,
+ "aspeed-jtag", aspeed_jtag);
+ if (err) {
+ dev_err(aspeed_jtag->dev, "unable to get IRQ");
+ goto clk_unprep;
+ }
+ dev_dbg(&pdev->dev, "IRQ %d.\n", aspeed_jtag->irq);
+
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_ISR_INST_PAUSE |
+ ASPEED_JTAG_ISR_INST_COMPLETE |
+ ASPEED_JTAG_ISR_DATA_PAUSE |
+ ASPEED_JTAG_ISR_DATA_COMPLETE |
+ ASPEED_JTAG_ISR_INST_PAUSE_EN |
+ ASPEED_JTAG_ISR_INST_COMPLETE_EN |
+ ASPEED_JTAG_ISR_DATA_PAUSE_EN |
+ ASPEED_JTAG_ISR_DATA_COMPLETE_EN,
+ ASPEED_JTAG_ISR);
+
+ aspeed_jtag->flag = 0;
+ aspeed_jtag->mode = 0;
+ init_waitqueue_head(&aspeed_jtag->jtag_wq);
+ return 0;
+
+clk_unprep:
+ clk_disable_unprepare(aspeed_jtag->pclk);
+ return err;
+}
+
+int aspeed_jtag_deinit(struct platform_device *pdev,
+ struct aspeed_jtag *aspeed_jtag)
+{
+ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_ISR);
+ /* Disable clock */
+ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_CTRL);
+ reset_control_assert(aspeed_jtag->rst);
+ clk_disable_unprepare(aspeed_jtag->pclk);
+ return 0;
+}
+
+static const struct jtag_ops aspeed_jtag_ops = {
+ .freq_get = aspeed_jtag_freq_get,
+ .freq_set = aspeed_jtag_freq_set,
+ .status_get = aspeed_jtag_status_get,
+ .idle = aspeed_jtag_idle,
+ .xfer = aspeed_jtag_xfer,
+ .mode_set = aspeed_jtag_mode_set
+};
+
+static int aspeed_jtag_probe(struct platform_device *pdev)
+{
+ struct aspeed_jtag *aspeed_jtag;
+ struct device *dev;
+ struct jtag *jtag;
+ int err;
+
+ dev = &pdev->dev;
+ jtag = jtag_alloc(sizeof(*aspeed_jtag), &aspeed_jtag_ops);
+ if (!jtag)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, jtag);
+ aspeed_jtag = jtag_priv(jtag);
+ aspeed_jtag->dev = &pdev->dev;
+
+ /* Initialize device*/
+ err = aspeed_jtag_init(pdev, aspeed_jtag);
+ if (err)
+ goto err_jtag_init;
+
+ /* Initialize JTAG core structure*/
+ err = jtag_register(jtag);
+ if (err)
+ goto err_jtag_register;
+
+ return 0;
+
+err_jtag_register:
+ aspeed_jtag_deinit(pdev, aspeed_jtag);
+err_jtag_init:
+ jtag_free(jtag);
+ return err;
+}
+
+static int aspeed_jtag_remove(struct platform_device *pdev)
+{
+ struct jtag *jtag;
+
+ jtag = platform_get_drvdata(pdev);
+ aspeed_jtag_deinit(pdev, jtag_priv(jtag));
+ jtag_unregister(jtag);
+ jtag_free(jtag);
+ return 0;
+}
+
+static const struct of_device_id aspeed_jtag_of_match[] = {
+ { .compatible = "aspeed,ast2400-jtag", },
+ { .compatible = "aspeed,ast2500-jtag", },
+ {}
+};
+
+static struct platform_driver aspeed_jtag_driver = {
+ .probe = aspeed_jtag_probe,
+ .remove = aspeed_jtag_remove,
+ .driver = {
+ .name = ASPEED_JTAG_NAME,
+ .of_match_table = aspeed_jtag_of_match,
+ },
+};
+module_platform_driver(aspeed_jtag_driver);
+
+MODULE_AUTHOR("Oleksandr Shamray <oleksandrs@mellanox.com>");
+MODULE_DESCRIPTION("ASPEED JTAG driver");
+MODULE_LICENSE("GPL v2");
--
1.7.1
^ permalink raw reply related
* [patch v17 3/4] Documentation: jtag: Add bindings for Aspeed SoC 24xx and 25xx families JTAG master driver
From: Oleksandr Shamray @ 2018-01-16 7:18 UTC (permalink / raw)
To: gregkh, arnd
Cc: linux-kernel, linux-arm-kernel, devicetree, openbmc, joel, jiri,
tklauser, linux-serial, vadimp, system-sw-low-level, robh+dt,
openocd-devel-owner, linux-api, davem, mchehab, Oleksandr Shamray,
Jiri Pirko
In-Reply-To: <1516087139-7510-1-git-send-email-oleksandrs@mellanox.com>
It has been tested on Mellanox system with BMC equipped with
Aspeed 2520 SoC for programming CPLD devices.
Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Acked-by: Rob Herring <robh@kernel.org>
---
v16->v17
v15->v16
Comments pointed by Joel Stanley <joel.stan@gmail.com>
- change clocks = <&clk_apb> to proper clocks = <&syscon ASPEED_CLK_APB>
- add reset descriptions in bndings file
v14->v15
v13->v14
v12->v13
v11->v12
v10->v11
v9->v10
v8->v9
v7->v8
Comments pointed by pointed by Joel Stanley <joel.stan@gmail.com>
- Change compatible string to ast2400 and ast2000
V6->v7
Comments pointed by Tobias Klauser <tklauser@distanz.ch>
- Fix spell "Doccumentation" -> "Documentation"
v5->v6
Comments pointed by Tobias Klauser <tklauser@distanz.ch>
- Small nit: s/documentation/Documentation/
v4->v5
V3->v4
Comments pointed by Rob Herring <robh@kernel.org>
- delete unnecessary "status" and "reg-shift" descriptions in
bndings file
v2->v3
Comments pointed by Rob Herring <robh@kernel.org>
- split Aspeed jtag driver and binding to sepatrate patches
- delete unnecessary "status" and "reg-shift" descriptions in
bndings file
---
.../devicetree/bindings/jtag/aspeed-jtag.txt | 22 ++++++++++++++++++++
drivers/jtag/jtag-aspeed.c | 1 +
2 files changed, 23 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
diff --git a/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt b/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
new file mode 100644
index 0000000..7c36eb6
--- /dev/null
+++ b/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
@@ -0,0 +1,22 @@
+Aspeed JTAG driver for ast2400 and ast2500 SoC
+
+Required properties:
+- compatible: Should be one of
+ - "aspeed,ast2400-jtag"
+ - "aspeed,ast2500-jtag"
+- reg contains the offset and length of the JTAG memory
+ region
+- clocks root clock of bus, should reference the APB
+ clock in the second cell
+- resets phandle to reset controller with the reset number in
+ the second cell
+- interrupts should contain JTAG controller interrupt
+
+Example:
+jtag: jtag@1e6e4000 {
+ compatible = "aspeed,ast2500-jtag";
+ reg = <0x1e6e4000 0x1c>;
+ clocks = <&syscon ASPEED_CLK_APB>;
+ resets = <&syscon ASPEED_RESET_JTAG_MASTER>;
+ interrupts = <43>;
+};
diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c
index 9cbd6da..f679041 100644
--- a/drivers/jtag/jtag-aspeed.c
+++ b/drivers/jtag/jtag-aspeed.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
+#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <uapi/linux/jtag.h>
--
1.7.1
^ permalink raw reply related
* [patch v17 4/4] Documentation: jtag: Add ABI documentation
From: Oleksandr Shamray @ 2018-01-16 7:18 UTC (permalink / raw)
To: gregkh, arnd
Cc: linux-kernel, linux-arm-kernel, devicetree, openbmc, joel, jiri,
tklauser, linux-serial, vadimp, system-sw-low-level, robh+dt,
openocd-devel-owner, linux-api, davem, mchehab, Oleksandr Shamray
In-Reply-To: <1516087139-7510-1-git-send-email-oleksandrs@mellanox.com>
Added document that describe the ABI for JTAG class drivrer
Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
---
v16->v17
v15->v16
v14->v15
v13->v14
v12->v13
v11->v12
Tobias Klauser <tklauser@distanz.ch>
- rename /Documentation/ABI/testing/jatg-dev -> jtag-dev
- Typo: s/interfase/interface
v10->v11
v9->v10
Fixes added by Oleksandr:
- change jtag-cdev to jtag-dev in documentation
- update Kernel Version and Date in jtag-dev documentation;
v8->v9
v7->v8
v6->v7
Comments pointed by Pavel Machek <pavel@ucw.cz>
- Added jtag-cdev documentation to Documentation/ABI/testing folder
---
Documentation/ABI/testing/jtag-dev | 27 +++++++++++++++++++++++++++
1 files changed, 27 insertions(+), 0 deletions(-)
create mode 100644 Documentation/ABI/testing/jtag-dev
diff --git a/Documentation/ABI/testing/jtag-dev b/Documentation/ABI/testing/jtag-dev
new file mode 100644
index 0000000..cea9552
--- /dev/null
+++ b/Documentation/ABI/testing/jtag-dev
@@ -0,0 +1,27 @@
+What: /dev/jtag[0-9]+
+Date: October 2017
+KernelVersion: 4.17
+Contact: oleksandrs@mellanox.com
+Description:
+ The misc device files /dev/jtag* are the interface
+ between JTAG master interface and userspace.
+
+ The ioctl(2)-based ABI is defined and documented in
+ [include/uapi]<linux/jtag.h>.
+
+ The following file operations are supported:
+
+ open(2)
+ The argument flag currently support only one access
+ mode O_RDWR.
+
+ ioctl(2)
+ Initiate various actions.
+ See the inline documentation in [include/uapi]<linux/jtag.h>
+ for descriptions of all ioctls.
+
+ close(2)
+ Stops and free up the I/O contexts that was associated
+ with the file descriptor.
+
+Users: TBD
\ No newline at end of file
--
1.7.1
^ permalink raw reply related
* Re: [PATCH] serial: 8250_dw: Avoid overflow in dw8250_set_termios
From: Nuno Gonçalves @ 2018-01-16 8:48 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Ed Blake, Greg Kroah-Hartman, Linux Kernel Mailing List,
linux-serial
In-Reply-To: <CAHp75VfBCTFcO=bysUQBaN-6sYa_1LO8XSzc4WY+X0_M_SZwsQ@mail.gmail.com>
On Mon, Jan 15, 2018 at 9:30 PM, Andy Shevchenko
<andy.shevchenko@gmail.com> wrote:
> It seems the change broke Bluetooth on some Intel platforms. I'm not
> sure yet, but
> see here:
>
> https://github.com/Dunedan/mbp-2016-linux/issues/29#issuecomment-357583782
>
Hi Andy,
I guess you mean de9e33b [1] and not this patch (which is not in
mainline). Can you double check?
Thanks,
Nuno
[1] https://github.com/torvalds/linux/commit/de9e33bdfa22e607a88494ff21e9196d00bf4550
^ permalink raw reply
* Re: [PATCH v2 1/7] qcom-geni-se: Add QCOM GENI SE Driver summary
From: Bjorn Andersson @ 2018-01-16 16:55 UTC (permalink / raw)
To: Karthikeyan Ramasubramanian
Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
linux-serial, jslaby
In-Reply-To: <1515805547-22816-2-git-send-email-kramasub@codeaurora.org>
On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
> Generic Interface (GENI) firmware based Qualcomm Universal Peripheral (QUP)
> Wrapper is a programmable module that is composed of multiple Serial
> Engines (SE) and can support various Serial Interfaces like UART, SPI,
> I2C, I3C, etc. This document provides a high level overview of the GENI
> based QUP Wrapper.
>
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
Rather than adding a disconnected chunk of documentation move this into
the driver(s), using the format described in
https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html?highlight=kernel%20doc#overview-documentation-comments
Regards,
Bjorn
^ permalink raw reply
* Re: [PATCH v2 3/7] soc: qcom: Add GENI based QUP Wrapper driver
From: Bjorn Andersson @ 2018-01-17 6:20 UTC (permalink / raw)
To: Karthikeyan Ramasubramanian
Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
linux-serial, jslaby, Sagar Dharia, Girish Mahadevan
In-Reply-To: <1515805547-22816-4-git-send-email-kramasub@codeaurora.org>
On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
> This driver manages the Generic Interface (GENI) firmware based Qualcomm
> Universal Peripheral (QUP) Wrapper. GENI based QUP is the next generation
> programmable module composed of multiple Serial Engines (SE) and supports
> a wide range of serial interfaces like UART, SPI, I2C, I3C, etc. This
> driver also enables managing the serial interface independent aspects of
> Serial Engines.
>
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
> ---
> drivers/soc/qcom/Kconfig | 8 +
> drivers/soc/qcom/Makefile | 1 +
> drivers/soc/qcom/qcom-geni-se.c | 1016 +++++++++++++++++++++++++++++++++++++++
I'm not aware of any non-serial-engine "geni" at Qualcomm, so can we
drop the "se" throughout this driver?
> include/linux/qcom-geni-se.h | 807 +++++++++++++++++++++++++++++++
> 4 files changed, 1832 insertions(+)
> create mode 100644 drivers/soc/qcom/qcom-geni-se.c
> create mode 100644 include/linux/qcom-geni-se.h
>
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index b81374b..b306d51 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -3,6 +3,14 @@
> #
> menu "Qualcomm SoC drivers"
>
> +config QCOM_GENI_SE
> + tristate "QCOM GENI Serial Engine Driver"
Options in drivers/soc/qcom/Kconfig should have "depends on ARCH_QCOM",
as the makefile in this directory won't be processed when !ARCH_QCOM.
> + help
> + This module is used to manage Generic Interface (GENI) firmware based
> + Qualcomm Technologies, Inc. Universal Peripheral (QUP) Wrapper. This
> + module is also used to manage the common aspects of multiple Serial
> + Engines present in the QUP.
> +
> config QCOM_GLINK_SSR
> tristate "Qualcomm Glink SSR driver"
> depends on RPMSG
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
> index 40c56f6..74d5db8 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -1,4 +1,5 @@
> # SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o
> obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o
> obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
> obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o
> diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
> new file mode 100644
> index 0000000..3f43582
> --- /dev/null
> +++ b/drivers/soc/qcom/qcom-geni-se.c
> @@ -0,0 +1,1016 @@
> +/*
> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
Please use SPDX style license header, i.e. this file should start with:
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*/
> +
> +#include <linux/clk.h>
> +#include <linux/slab.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/io.h>
> +#include <linux/list.h>
This isn't used.
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/pm_runtime.h>
Is this used?
> +#include <linux/qcom-geni-se.h>
> +#include <linux/spinlock.h>
Is this used?
> +
> +#define MAX_CLK_PERF_LEVEL 32
> +
> +/**
> + * @struct geni_se_device - Data structure to represent the QUP Wrapper Core
Make this name indicate that this is the wrapper; e.g. struct geni_wrapper
> + * @dev: Device pointer of the QUP wrapper core.
> + * @base: Base address of this instance of QUP wrapper core.
> + * @m_ahb_clk: Handle to the primary AHB clock.
> + * @s_ahb_clk: Handle to the secondary AHB clock.
> + * @geni_dev_lock: Lock to protect the device elements.
> + * @num_clk_levels: Number of valid clock levels in clk_perf_tbl.
> + * @clk_perf_tbl: Table of clock frequency input to Serial Engine clock.
> + */
> +struct geni_se_device {
> + struct device *dev;
> + void __iomem *base;
> + struct clk *m_ahb_clk;
> + struct clk *s_ahb_clk;
As these two clocks always seems to be operated in tandem use
clk_bulk_data to keep track of them and use the clk_bulk*() operations
to simplify your prepare/unprepare sites.
> + struct mutex geni_dev_lock;
There's only one lock and it should be obvious from context that it's
the lock of the geni_se_device, so naming this "lock" should be
sufficient.
> + unsigned int num_clk_levels;
> + unsigned long *clk_perf_tbl;
> +};
> +
> +/* Offset of QUP Hardware Version Register */
> +#define QUP_HW_VER (0x4)
Drop the parenthesis. It would be nice if this define indicated that
this is a register and not a value. E.g. call it QUP_HW_VER_REG.
> +
> +#define HW_VER_MAJOR_MASK GENMASK(31, 28)
> +#define HW_VER_MAJOR_SHFT 28
> +#define HW_VER_MINOR_MASK GENMASK(27, 16)
> +#define HW_VER_MINOR_SHFT 16
> +#define HW_VER_STEP_MASK GENMASK(15, 0)
> +
> +/**
> + * geni_read_reg_nolog() - Helper function to read from a GENI register
> + * @base: Base address of the serial engine's register block.
> + * @offset: Offset within the serial engine's register block.
> + *
> + * Return: Return the contents of the register.
> + */
> +unsigned int geni_read_reg_nolog(void __iomem *base, int offset)
Remove this function.
> +{
> + return readl_relaxed(base + offset);
> +}
> +EXPORT_SYMBOL(geni_read_reg_nolog);
> +
> +/**
> + * geni_write_reg_nolog() - Helper function to write into a GENI register
> + * @value: Value to be written into the register.
> + * @base: Base address of the serial engine's register block.
> + * @offset: Offset within the serial engine's register block.
> + */
> +void geni_write_reg_nolog(unsigned int value, void __iomem *base, int offset)
Ditto
> +{
> + return writel_relaxed(value, (base + offset));
> +}
> +EXPORT_SYMBOL(geni_write_reg_nolog);
> +
> +/**
> + * geni_read_reg() - Helper function to read from a GENI register
> + * @base: Base address of the serial engine's register block.
> + * @offset: Offset within the serial engine's register block.
> + *
> + * Return: Return the contents of the register.
Drop the extra spaces after "Return:" in all your kerneldoc.
> + */
> +unsigned int geni_read_reg(void __iomem *base, int offset)
> +{
> + return readl_relaxed(base + offset);
> +}
> +EXPORT_SYMBOL(geni_read_reg);
> +
> +/**
> + * geni_write_reg() - Helper function to write into a GENI register
> + * @value: Value to be written into the register.
> + * @base: Base address of the serial engine's register block.
> + * @offset: Offset within the serial engine's register block.
> + */
> +void geni_write_reg(unsigned int value, void __iomem *base, int offset)
> +{
> + return writel_relaxed(value, (base + offset));
As written, this function adds no value and I find it quite confusing
that this is used both for read/writes on the wrapper as well as the
individual functions.
Compare:
geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
with just inlining:
writel(common_geni_m_irq_en, base + SE_GENI_M_IRQ_EN);
> +}
> +EXPORT_SYMBOL(geni_write_reg);
> +
> +/**
> + * geni_get_qup_hw_version() - Read the QUP wrapper Hardware version
> + * @wrapper_dev: Pointer to the corresponding QUP wrapper core.
> + * @major: Buffer for Major Version field.
> + * @minor: Buffer for Minor Version field.
> + * @step: Buffer for Step Version field.
> + *
> + * Return: 0 on success, standard Linux error codes on failure/error.
> + */
> +int geni_get_qup_hw_version(struct device *wrapper_dev, unsigned int *major,
> + unsigned int *minor, unsigned int *step)
> +{
> + unsigned int version;
> + struct geni_se_device *geni_se_dev;
> +
> + if (!wrapper_dev || !major || !minor || !step)
> + return -EINVAL;
This would only happen during development, as the engineer calls the
function incorrectly. Consider it a contract that these has to be valid
and skip the checks.
> +
> + geni_se_dev = dev_get_drvdata(wrapper_dev);
> + if (unlikely(!geni_se_dev))
> + return -ENODEV;
Make the children acquire the geni_se_dev handle (keep the type opaque)
and pass that instead of wrapper_dev. Then you can just drop this chunk.
> +
> + version = geni_read_reg(geni_se_dev->base, QUP_HW_VER);
> + *major = (version & HW_VER_MAJOR_MASK) >> HW_VER_MAJOR_SHFT;
> + *minor = (version & HW_VER_MINOR_MASK) >> HW_VER_MINOR_SHFT;
> + *step = version & HW_VER_STEP_MASK;
> + return 0;
If you implement these two changes there's no way this function can
fail, so you don't have to return a value here.
> +}
> +EXPORT_SYMBOL(geni_get_qup_hw_version);
> +
> +/**
> + * geni_se_get_proto() - Read the protocol configured for a serial engine
> + * @base: Base address of the serial engine's register block.
> + *
> + * Return: Protocol value as configured in the serial engine.
> + */
> +int geni_se_get_proto(void __iomem *base)
I keep reading this as "geni set get proto", you should be fine just
dropping _se_ from these function names. And if you want to denote that
it's the Qualcomm GENI then make it qcom_geni_*.
But more importantly, it is not obvious when reading this driver that
the typeless "base" being passed is that of the individual functional
block, and not the wrapper.
If you want to do this in an "object oriented" fashion create a struct
that's common for all geni instances, include it in the context of the
individual function devices and pass it into these functions.
> +{
> + int proto;
> +
> + proto = ((geni_read_reg(base, GENI_FW_REVISION_RO)
> + & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT);
Don't do everything in one go, as you can't fit it on one line anyways.
Writing it like this instead makes it easier to read:
u32 val;
val = readl(base + GENI_FW_S_REVISION_RO);
return (val & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT;
> + return proto;
> +}
> +EXPORT_SYMBOL(geni_se_get_proto);
> +
> +static int geni_se_irq_en(void __iomem *base)
> +{
> + unsigned int common_geni_m_irq_en;
> + unsigned int common_geni_s_irq_en;
The size of these values isn't unsigned int, it's u32. And if you give
them a shorter name the function becomes easier to read.
Further more, as you don't care about ordering you don't need two of
them either. So you should be able to just do:
u32 val;
val = readl(base + SE_GENI_M_IRQ_EN);
val |= M_COMMON_GENI_M_IRQ_EN;
writel(val, base + SE_GENI_M_IRQ_EN);
val = readl(base + SE_GENI_S_IRQ_EN);
val |= S_COMMON_GENI_S_IRQ_EN;
writel(val, base + SE_GENI_S_IRQ_EN);
> +
> + common_geni_m_irq_en = geni_read_reg(base, SE_GENI_M_IRQ_EN);
> + common_geni_s_irq_en = geni_read_reg(base, SE_GENI_S_IRQ_EN);
> + /* Common to all modes */
> + common_geni_m_irq_en |= M_COMMON_GENI_M_IRQ_EN;
> + common_geni_s_irq_en |= S_COMMON_GENI_S_IRQ_EN;
> +
> + geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
> + geni_write_reg(common_geni_s_irq_en, base, SE_GENI_S_IRQ_EN);
> + return 0;
And as this can't fail there's no reason to have a return value.
> +}
> +
> +
> +static void geni_se_set_rx_rfr_wm(void __iomem *base, unsigned int rx_wm,
> + unsigned int rx_rfr)
> +{
> + geni_write_reg(rx_wm, base, SE_GENI_RX_WATERMARK_REG);
> + geni_write_reg(rx_rfr, base, SE_GENI_RX_RFR_WATERMARK_REG);
> +}
> +
> +static int geni_se_io_set_mode(void __iomem *base)
> +{
> + unsigned int io_mode;
> + unsigned int geni_dma_mode;
> +
> + io_mode = geni_read_reg(base, SE_IRQ_EN);
> + geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
> +
> + io_mode |= (GENI_M_IRQ_EN | GENI_S_IRQ_EN);
> + io_mode |= (DMA_TX_IRQ_EN | DMA_RX_IRQ_EN);
> + geni_dma_mode &= ~GENI_DMA_MODE_EN;
> +
> + geni_write_reg(io_mode, base, SE_IRQ_EN);
> + geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
> + geni_write_reg(0, base, SE_GSI_EVENT_EN);
> + return 0;
As this function can't fail, don't return a value.
> +}
> +
> +static void geni_se_io_init(void __iomem *base)
> +{
> + unsigned int io_op_ctrl;
> + unsigned int geni_cgc_ctrl;
> + unsigned int dma_general_cfg;
These are all u32, be explicit.
> +
> + geni_cgc_ctrl = geni_read_reg(base, GENI_CGC_CTRL);
> + dma_general_cfg = geni_read_reg(base, SE_DMA_GENERAL_CFG);
> + geni_cgc_ctrl |= DEFAULT_CGC_EN;
> + dma_general_cfg |= (AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON |
> + DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON);
Drop the parenthesis and there's no harm in making multiple assignments
in favour of splitting the line.
> + io_op_ctrl = DEFAULT_IO_OUTPUT_CTRL_MSK;
> + geni_write_reg(geni_cgc_ctrl, base, GENI_CGC_CTRL);
> + geni_write_reg(dma_general_cfg, base, SE_DMA_GENERAL_CFG);
Is there a reason why this chunk of code is a mix of 3 independent
register updates?
> +
> + geni_write_reg(io_op_ctrl, base, GENI_OUTPUT_CTRL);
> + geni_write_reg(FORCE_DEFAULT, base, GENI_FORCE_DEFAULT_REG);
> +}
> +
> +/**
> + * geni_se_init() - Initialize the GENI Serial Engine
> + * @base: Base address of the serial engine's register block.
> + * @rx_wm: Receive watermark to be configured.
> + * @rx_rfr_wm: Ready-for-receive watermark to be configured.
What's the unit for these?
> + *
> + * This function is used to initialize the GENI serial engine, configure
> + * receive watermark and ready-for-receive watermarks.
> + *
> + * Return: 0 on success, standard Linux error codes on failure/error.
As neither geni_se_io_set_mode() nor geni_se_irq_en() can fail there's
no way geni_se_init() can fail and as such you don't need a return value
of this function.
> + */
> +int geni_se_init(void __iomem *base, unsigned int rx_wm, unsigned int rx_rfr)
> +{
> + int ret;
> +
> + geni_se_io_init(base);
> + ret = geni_se_io_set_mode(base);
> + if (ret)
> + return ret;
> +
> + geni_se_set_rx_rfr_wm(base, rx_wm, rx_rfr);
> + ret = geni_se_irq_en(base);
Just inline these two functions here.
> + return ret;
> +}
> +EXPORT_SYMBOL(geni_se_init);
This is an excellent candidate for initializing a common "geni
function"-struct shared among the children, i.e. make this:
void geni_init_port(struct geni_port *port, struct device *dev,
size_t rx_wm, rfr_wm);
And have this do the ioremap of the memory of dev and initialize some
common "geni_port" struct, then you can pass this to some set of
geni_port_*() helper functions.
> +
> +static int geni_se_select_fifo_mode(void __iomem *base)
> +{
> + int proto = geni_se_get_proto(base);
> + unsigned int common_geni_m_irq_en;
> + unsigned int common_geni_s_irq_en;
> + unsigned int geni_dma_mode;
These are of type u32.
If you use more succinct names the function will be easier to read; what
about master, slave and mode? (Or m_en, s_en and mode)
> +
> + geni_write_reg(0, base, SE_GSI_EVENT_EN);
> + geni_write_reg(0xFFFFFFFF, base, SE_GENI_M_IRQ_CLEAR);
> + geni_write_reg(0xFFFFFFFF, base, SE_GENI_S_IRQ_CLEAR);
> + geni_write_reg(0xFFFFFFFF, base, SE_DMA_TX_IRQ_CLR);
> + geni_write_reg(0xFFFFFFFF, base, SE_DMA_RX_IRQ_CLR);
> + geni_write_reg(0xFFFFFFFF, base, SE_IRQ_EN);
Use lowercase for the hex constants.
> +
> + common_geni_m_irq_en = geni_read_reg(base, SE_GENI_M_IRQ_EN);
> + common_geni_s_irq_en = geni_read_reg(base, SE_GENI_S_IRQ_EN);
> + geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
> + if (proto != UART) {
> + common_geni_m_irq_en |=
> + (M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN |
> + M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
Drop the parenthesis, split the assignment in multiple to make things
not span 3 lines.
> + common_geni_s_irq_en |= S_CMD_DONE_EN;
> + }
> + geni_dma_mode &= ~GENI_DMA_MODE_EN;
Please group things that are related.
The compiler doesn't care if you stretch the life time of your local
variables through your functions, but the brain does.
> +
> + geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
> + geni_write_reg(common_geni_s_irq_en, base, SE_GENI_S_IRQ_EN);
> + geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
> + return 0;
This can't fail, and you ignore the returned value in
geni_se_select_mode().
> +}
> +
> +static int geni_se_select_dma_mode(void __iomem *base)
> +{
> + unsigned int geni_dma_mode = 0;
This is u32, can be shorten to "mode" and it's first use is an
assignment - so you don't have to initialize it here.
> +
> + geni_write_reg(0, base, SE_GSI_EVENT_EN);
> + geni_write_reg(0xFFFFFFFF, base, SE_GENI_M_IRQ_CLEAR);
> + geni_write_reg(0xFFFFFFFF, base, SE_GENI_S_IRQ_CLEAR);
> + geni_write_reg(0xFFFFFFFF, base, SE_DMA_TX_IRQ_CLR);
> + geni_write_reg(0xFFFFFFFF, base, SE_DMA_RX_IRQ_CLR);
> + geni_write_reg(0xFFFFFFFF, base, SE_IRQ_EN);
Please lower case your hex constants.
> +
> + geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);
> + geni_dma_mode |= GENI_DMA_MODE_EN;
> + geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
> + return 0;
Can't fail.
> +}
> +
> +/**
> + * geni_se_select_mode() - Select the serial engine transfer mode
> + * @base: Base address of the serial engine's register block.
> + * @mode: Transfer mode to be selected.
> + *
> + * Return: 0 on success, standard Linux error codes on failure.
> + */
> +int geni_se_select_mode(void __iomem *base, int mode)
> +{
> + int ret = 0;
Drop the return value and "ret", if you want to help the developer of
child devices you can start off by a WARN_ON(mode != FIFO_MODE && mode
!= SE_DMA);
> +
> + switch (mode) {
> + case FIFO_MODE:
> + geni_se_select_fifo_mode(base);
> + break;
> + case SE_DMA:
> + geni_se_select_dma_mode(base);
> + break;
> + default:
> + ret = -EINVAL;
> + break;
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(geni_se_select_mode);
> +
> +/**
> + * geni_se_setup_m_cmd() - Setup the primary sequencer
> + * @base: Base address of the serial engine's register block.
> + * @cmd: Command/Operation to setup in the primary sequencer.
> + * @params: Parameter for the sequencer command.
> + *
> + * This function is used to configure the primary sequencer with the
> + * command and its assoicated parameters.
> + */
> +void geni_se_setup_m_cmd(void __iomem *base, u32 cmd, u32 params)
> +{
> + u32 m_cmd = (cmd << M_OPCODE_SHFT);
> +
> + m_cmd |= (params & M_PARAMS_MSK);
Rather than initializing the variable during declaration and then
amending the value it's cleaner if you do:
val = (cmd << M_OPCODE_SHFT) | (params & M_PARAMS_MSK);
> + geni_write_reg(m_cmd, base, SE_GENI_M_CMD0);
> +}
> +EXPORT_SYMBOL(geni_se_setup_m_cmd);
> +
[..]
> +/**
> + * geni_se_get_tx_fifo_depth() - Get the TX fifo depth of the serial engine
> + * @base: Base address of the serial engine's register block.
> + *
> + * This function is used to get the depth i.e. number of elements in the
> + * TX fifo of the serial engine.
> + *
> + * Return: TX fifo depth in units of FIFO words.
> + */
> +int geni_se_get_tx_fifo_depth(void __iomem *base)
> +{
> + int tx_fifo_depth;
> +
> + tx_fifo_depth = ((geni_read_reg(base, SE_HW_PARAM_0)
> + & TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT);
It easier to read this way:
u32 val;
val = readl(base, SE_HW_PARAM_0);
return (val & TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT;
> + return tx_fifo_depth;
> +}
> +EXPORT_SYMBOL(geni_se_get_tx_fifo_depth);
> +
> +/**
> + * geni_se_get_tx_fifo_width() - Get the TX fifo width of the serial engine
> + * @base: Base address of the serial engine's register block.
> + *
> + * This function is used to get the width i.e. word size per element in the
> + * TX fifo of the serial engine.
> + *
> + * Return: TX fifo width in bits
> + */
> +int geni_se_get_tx_fifo_width(void __iomem *base)
> +{
> + int tx_fifo_width;
> +
> + tx_fifo_width = ((geni_read_reg(base, SE_HW_PARAM_0)
> + & TX_FIFO_WIDTH_MSK) >> TX_FIFO_WIDTH_SHFT);
> + return tx_fifo_width;
Ditto.
> +}
> +EXPORT_SYMBOL(geni_se_get_tx_fifo_width);
> +
> +/**
> + * geni_se_get_rx_fifo_depth() - Get the RX fifo depth of the serial engine
> + * @base: Base address of the serial engine's register block.
> + *
> + * This function is used to get the depth i.e. number of elements in the
> + * RX fifo of the serial engine.
> + *
> + * Return: RX fifo depth in units of FIFO words
> + */
> +int geni_se_get_rx_fifo_depth(void __iomem *base)
> +{
> + int rx_fifo_depth;
> +
> + rx_fifo_depth = ((geni_read_reg(base, SE_HW_PARAM_1)
> + & RX_FIFO_DEPTH_MSK) >> RX_FIFO_DEPTH_SHFT);
> + return rx_fifo_depth;
Ditto.
> +}
> +EXPORT_SYMBOL(geni_se_get_rx_fifo_depth);
> +
> +/**
> + * geni_se_get_packing_config() - Get the packing configuration based on input
> + * @bpw: Bits of data per transfer word.
> + * @pack_words: Number of words per fifo element.
> + * @msb_to_lsb: Transfer from MSB to LSB or vice-versa.
> + * @cfg0: Output buffer to hold the first half of configuration.
> + * @cfg1: Output buffer to hold the second half of configuration.
> + *
> + * This function is used to calculate the packing configuration based on
> + * the input packing requirement and the configuration logic.
> + */
> +void geni_se_get_packing_config(int bpw, int pack_words, bool msb_to_lsb,
> + unsigned long *cfg0, unsigned long *cfg1)
This function is used only from geni_se_config_packing() which writes
the new config to the associated registers and from the serial driver
that does the same - but here pack_words differ for rx and tx.
If you improve geni_se_config_packing() to take rx and tx fifo sizes
independently you don't have to expose this function to the client
drivers and you don't need to return cfg0 and cfg1 like you do here.
> +{
> + u32 cfg[4] = {0};
> + int len;
> + int temp_bpw = bpw;
> + int idx_start = (msb_to_lsb ? (bpw - 1) : 0);
> + int idx = idx_start;
> + int idx_delta = (msb_to_lsb ? -BITS_PER_BYTE : BITS_PER_BYTE);
> + int ceil_bpw = ((bpw & (BITS_PER_BYTE - 1)) ?
> + ((bpw & ~(BITS_PER_BYTE - 1)) + BITS_PER_BYTE) : bpw);
> + int iter = (ceil_bpw * pack_words) >> 3;
> + int i;
> +
> + if (unlikely(iter <= 0 || iter > 4)) {
Don't use unlikely(), this function is not called one per port, there's
no need to clutter the code to "optimize" it.
> + *cfg0 = 0;
> + *cfg1 = 0;
> + return;
> + }
> +
> + for (i = 0; i < iter; i++) {
> + len = (temp_bpw < BITS_PER_BYTE) ?
> + (temp_bpw - 1) : BITS_PER_BYTE - 1;
> + cfg[i] = ((idx << 5) | (msb_to_lsb << 4) | (len << 1));
> + idx = ((temp_bpw - BITS_PER_BYTE) <= 0) ?
> + ((i + 1) * BITS_PER_BYTE) + idx_start :
> + idx + idx_delta;
> + temp_bpw = ((temp_bpw - BITS_PER_BYTE) <= 0) ?
> + bpw : (temp_bpw - BITS_PER_BYTE);
Each operation in this loop depend on temp_bpw being smaller or larger
than BITS_PER_BYTE, please rewrite it with a single if/else based on
this.
> + }
> + cfg[iter - 1] |= 1;
The 1 you're writing here looks like an "EOF". It would be nice with a
comment describing the format of these 4 registers and the meaning of
BIT(0).
> + *cfg0 = cfg[0] | (cfg[1] << 10);
> + *cfg1 = cfg[2] | (cfg[3] << 10);
> +}
> +EXPORT_SYMBOL(geni_se_get_packing_config);
> +
> +/**
> + * geni_se_config_packing() - Packing configuration of the serial engine
> + * @base: Base address of the serial engine's register block.
> + * @bpw: Bits of data per transfer word.
> + * @pack_words: Number of words per fifo element.
> + * @msb_to_lsb: Transfer from MSB to LSB or vice-versa.
> + *
> + * This function is used to configure the packing rules for the current
> + * transfer.
> + */
> +void geni_se_config_packing(void __iomem *base, int bpw,
> + int pack_words, bool msb_to_lsb)
> +{
> + unsigned long cfg0, cfg1;
These are u32.
> +
> + geni_se_get_packing_config(bpw, pack_words, msb_to_lsb, &cfg0, &cfg1);
> + geni_write_reg(cfg0, base, SE_GENI_TX_PACKING_CFG0);
> + geni_write_reg(cfg1, base, SE_GENI_TX_PACKING_CFG1);
> + geni_write_reg(cfg0, base, SE_GENI_RX_PACKING_CFG0);
> + geni_write_reg(cfg1, base, SE_GENI_RX_PACKING_CFG1);
> + if (pack_words || bpw == 32)
> + geni_write_reg((bpw >> 4), base, SE_GENI_BYTE_GRAN);
> +}
> +EXPORT_SYMBOL(geni_se_config_packing);
> +
> +static void geni_se_clks_off(struct geni_se_rsc *rsc)
> +{
> + struct geni_se_device *geni_se_dev;
> +
> + if (unlikely(!rsc || !rsc->wrapper_dev))
Drop the unlikely(). Why would you be passed a rsc with wrapper_dev
NULL?
> + return;
> +
> + geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
> + if (unlikely(!geni_se_dev))
> + return;
> +
> + clk_disable_unprepare(rsc->se_clk);
> + clk_disable_unprepare(geni_se_dev->s_ahb_clk);
> + clk_disable_unprepare(geni_se_dev->m_ahb_clk);
> +}
> +
> +/**
> + * geni_se_resources_off() - Turn off resources associated with the serial
> + * engine
> + * @rsc: Handle to resources associated with the serial engine.
> + *
> + * Return: 0 on success, standard Linux error codes on failure/error.
> + */
> +int geni_se_resources_off(struct geni_se_rsc *rsc)
> +{
> + int ret = 0;
No need to initialize ret.
> + struct geni_se_device *geni_se_dev;
> +
> + if (unlikely(!rsc || !rsc->wrapper_dev))
> + return -EINVAL;
> +
> + geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
> + if (unlikely(!geni_se_dev))
Only child devices would call this, so it's unlikely that this is a
probe defer.
Also the returned value is not used.
And drop the unlikely()
> + return -ENODEV;
> +
> + ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_sleep);
> + if (ret)
> + return ret;
> +
> + geni_se_clks_off(rsc);
> + return 0;
> +}
> +EXPORT_SYMBOL(geni_se_resources_off);
> +
> +static int geni_se_clks_on(struct geni_se_rsc *rsc)
> +{
> + int ret;
> + struct geni_se_device *geni_se_dev;
If you name this "geni" instead, or "wrapper" the rest will be cleaner.
> +
> + if (unlikely(!rsc || !rsc->wrapper_dev))
> + return -EINVAL;
> +
> + geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
> + if (unlikely(!geni_se_dev))
> + return -EPROBE_DEFER;
> +
> + ret = clk_prepare_enable(geni_se_dev->m_ahb_clk);
> + if (ret)
> + return ret;
> +
> + ret = clk_prepare_enable(geni_se_dev->s_ahb_clk);
> + if (ret) {
> + clk_disable_unprepare(geni_se_dev->m_ahb_clk);
> + return ret;
> + }
These two could be dealt with in a single clk_bulk_prepare_enable().
> +
> + ret = clk_prepare_enable(rsc->se_clk);
> + if (ret) {
> + clk_disable_unprepare(geni_se_dev->s_ahb_clk);
> + clk_disable_unprepare(geni_se_dev->m_ahb_clk);
> + }
> + return ret;
> +}
> +
> +/**
> + * geni_se_resources_on() - Turn on resources associated with the serial
> + * engine
> + * @rsc: Handle to resources associated with the serial engine.
> + *
> + * Return: 0 on success, standard Linux error codes on failure/error.
> + */
> +int geni_se_resources_on(struct geni_se_rsc *rsc)
> +{
> + int ret = 0;
> + struct geni_se_device *geni_se_dev;
> +
> + if (unlikely(!rsc || !rsc->wrapper_dev))
> + return -EINVAL;
> +
> + geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
> + if (unlikely(!geni_se_dev))
As with geni_se_resources_off()
> + return -EPROBE_DEFER;
> +
> + ret = geni_se_clks_on(rsc);
> + if (ret)
> + return ret;
> +
> + ret = pinctrl_select_state(rsc->geni_pinctrl, rsc->geni_gpio_active);
pinctrl_pm_select_default_state(rsc->dev);
So you need the dev associated with the rsc.
> + if (ret)
> + geni_se_clks_off(rsc);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(geni_se_resources_on);
> +
> +/**
> + * geni_se_clk_tbl_get() - Get the clock table to program DFS
> + * @rsc: Resource for which the clock table is requested.
> + * @tbl: Table in which the output is returned.
> + *
> + * This function is called by the protocol drivers to determine the different
> + * clock frequencies supported by Serail Engine Core Clock. The protocol
s/Serail/Serial/
> + * drivers use the output to determine the clock frequency index to be
> + * programmed into DFS.
> + *
> + * Return: number of valid performance levels in the table on success,
> + * standard Linux error codes on failure.
> + */
> +int geni_se_clk_tbl_get(struct geni_se_rsc *rsc, unsigned long **tbl)
> +{
> + struct geni_se_device *geni_se_dev;
> + int i;
> + unsigned long prev_freq = 0;
> + int ret = 0;
> +
> + if (unlikely(!rsc || !rsc->wrapper_dev || !rsc->se_clk || !tbl))
Drop the "unlikely", you're only calling this once.
> + return -EINVAL;
> +
> + *tbl = NULL;
Don't touch tbl on error.
> + geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
> + if (unlikely(!geni_se_dev))
> + return -EPROBE_DEFER;
How would this even be possible? This function should only be called by
child devices, which per definition means that this device been probed.
> +
> + mutex_lock(&geni_se_dev->geni_dev_lock);
> + if (geni_se_dev->clk_perf_tbl) {
> + *tbl = geni_se_dev->clk_perf_tbl;
> + ret = geni_se_dev->num_clk_levels;
> + goto exit_se_clk_tbl_get;
> + }
You're never freeing clk_perf_tbl, and the only reason you have
geni_dev_lock is to protect the "cached" array in geni_se_dev.
Move the allocation and generation of clk_perf_tbl to geni_se_probe()
and store the value, then make this function just return that array.
That way you can drop the lock and the code becomes cleaner.
> +
> + geni_se_dev->clk_perf_tbl = kzalloc(sizeof(*geni_se_dev->clk_perf_tbl) *
> + MAX_CLK_PERF_LEVEL, GFP_KERNEL);
Use kcalloc()
> + if (!geni_se_dev->clk_perf_tbl) {
> + ret = -ENOMEM;
> + goto exit_se_clk_tbl_get;
> + }
> +
> + for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
> + geni_se_dev->clk_perf_tbl[i] = clk_round_rate(rsc->se_clk,
> + prev_freq + 1);
> + if (geni_se_dev->clk_perf_tbl[i] == prev_freq) {
> + geni_se_dev->clk_perf_tbl[i] = 0;
Use a local variable to keep the return value of clk_round_rate(), that
way you don't have to revert the value here (not that it matters...) and
you don't need to line wrap the assignment above.
> + break;
> + }
> + prev_freq = geni_se_dev->clk_perf_tbl[i];
...and then you don't need a separate local variable to keep track of
the last value...
> + }
> + geni_se_dev->num_clk_levels = i;
> + *tbl = geni_se_dev->clk_perf_tbl;
> + ret = geni_se_dev->num_clk_levels;
> +exit_se_clk_tbl_get:
Name your labels based on what action they perform, e.g. "out_unlock"
(not err_unlock here because it's the successful path as well)
> + mutex_unlock(&geni_se_dev->geni_dev_lock);
> + return ret;
> +}
> +EXPORT_SYMBOL(geni_se_clk_tbl_get);
> +
> +/**
> + * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
> + * @rsc: Resource for which the clock frequency is requested.
> + * @req_freq: Requested clock frequency.
> + * @index: Index of the resultant frequency in the table.
> + * @res_freq: Resultant frequency which matches or is closer to the
> + * requested frequency.
> + * @exact: Flag to indicate exact multiple requirement of the requested
> + * frequency .
> + *
> + * This function is called by the protocol drivers to determine the matching
> + * or closest frequency of the Serial Engine clock to be selected in order
> + * to meet the performance requirements.
What's the benefit compared to calling clk_round_rate()?
Given that the function can return a rate that is a fraction of the
requested frequency even though "exact" is set, this description isn't
explaining the entire picture.
> + *
> + * Return: 0 on success, standard Linux error codes on failure.
Returning the new clockrate would make more sense.
> + */
> +int geni_se_clk_freq_match(struct geni_se_rsc *rsc, unsigned long req_freq,
> + unsigned int *index, unsigned long *res_freq,
> + bool exact)
> +{
> + unsigned long *tbl;
> + int num_clk_levels;
> + int i;
> +
> + num_clk_levels = geni_se_clk_tbl_get(rsc, &tbl);
> + if (num_clk_levels < 0)
> + return num_clk_levels;
> +
> + if (num_clk_levels == 0)
> + return -EFAULT;
> +
> + *res_freq = 0;
> + for (i = 0; i < num_clk_levels; i++) {
> + if (!(tbl[i] % req_freq)) {
> + *index = i;
> + *res_freq = tbl[i];
So this will return a frequency that divides the requested frequency
without a remainder, will index be used to calculate a multiplier from
this?
> + return 0;
> + }
> +
> + if (!(*res_freq) || ((tbl[i] > *res_freq) &&
> + (tbl[i] < req_freq))) {
> + *index = i;
> + *res_freq = tbl[i];
What if there's a previous value in tbl that given some multiplier has a
smaller difference to the requested frequency?
> + }
> + }
> +
> + if (exact || !(*res_freq))
*res_freq can't be 0, because in the case that num_clk_levels is 0 you
already returned -EFAULT above, in all other cases you will assign it at
least once.
> + return -ENOKEY;
> +
> + return 0;
Why not use the return value for the calculated frequency?
> +}
> +EXPORT_SYMBOL(geni_se_clk_freq_match);
> +
> +/**
> + * geni_se_tx_dma_prep() - Prepare the Serial Engine for TX DMA transfer
> + * @wrapper_dev: QUP Wrapper Device to which the TX buffer is mapped.
> + * @base: Base address of the SE register block.
> + * @tx_buf: Pointer to the TX buffer.
> + * @tx_len: Length of the TX buffer.
> + * @tx_dma: Pointer to store the mapped DMA address.
> + *
> + * This function is used to prepare the buffers for DMA TX.
> + *
> + * Return: 0 on success, standard Linux error codes on error/failure.
> + */
> +int geni_se_tx_dma_prep(struct device *wrapper_dev, void __iomem *base,
> + void *tx_buf, int tx_len, dma_addr_t *tx_dma)
All child devices will have a "base", so if you move that into what you
today call a geni_se_rsc and you pass that to all these functions
operating on behalf of the child device you have the first two
parameters passed in the same object.
A "tx_len" is typically of type size_t.
Also note that there are no other buf than tx_buf, no other len than
tx_len and no other dma address than tx_dma in this function, so you can
drop "tx_" without loosing any information - but improving code
cleanliness.
> +{
> + int ret;
> +
> + if (unlikely(!wrapper_dev || !base || !tx_buf || !tx_len || !tx_dma))
> + return -EINVAL;
Reduce the error checking here.
> +
> + ret = geni_se_map_buf(wrapper_dev, tx_dma, tx_buf, tx_len,
> + DMA_TO_DEVICE);
I think you should pass the wrapper itself to geni_se_tx_dma_prep() and
if you name this "wrapper" (instead of wrapper_dev) you're under 80
chars and don't need the line break.
> + if (ret)
> + return ret;
> +
> + geni_write_reg(7, base, SE_DMA_TX_IRQ_EN_SET);
So that's DMA_RX_IRQ_EN | DMA_TX_IRQ_EN | GENI_M_IRQ_EN?
> + geni_write_reg((u32)(*tx_dma), base, SE_DMA_TX_PTR_L);
> + geni_write_reg((u32)((*tx_dma) >> 32), base, SE_DMA_TX_PTR_H);
If you return the dma_addr_t from this function you will have the happy
side effect of being able to use tx_dma directly instead of *tx_dma and
you can remove some craziness from these calls.
> + geni_write_reg(1, base, SE_DMA_TX_ATTR);
This "1" would be nice to have some clarification on.
> + geni_write_reg(tx_len, base, SE_DMA_TX_LEN);
> + return 0;
> +}
> +EXPORT_SYMBOL(geni_se_tx_dma_prep);
> +
> +/**
> + * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer
> + * @wrapper_dev: QUP Wrapper Device to which the RX buffer is mapped.
> + * @base: Base address of the SE register block.
> + * @rx_buf: Pointer to the RX buffer.
> + * @rx_len: Length of the RX buffer.
> + * @rx_dma: Pointer to store the mapped DMA address.
> + *
> + * This function is used to prepare the buffers for DMA RX.
> + *
> + * Return: 0 on success, standard Linux error codes on error/failure.
The only real error that can come out of this is dma_mapping_error(), so
just return the dma_addr_t.
> + */
> +int geni_se_rx_dma_prep(struct device *wrapper_dev, void __iomem *base,
> + void *rx_buf, int rx_len, dma_addr_t *rx_dma)
As with tx, drop all the "rx_". And pass that geni_se_rsc object
instead.
> +{
> + int ret;
> +
> + if (unlikely(!wrapper_dev || !base || !rx_buf || !rx_len || !rx_dma))
> + return -EINVAL;
> +
> + ret = geni_se_map_buf(wrapper_dev, rx_dma, rx_buf, rx_len,
> + DMA_FROM_DEVICE);
> + if (ret)
> + return ret;
> +
> + geni_write_reg(7, base, SE_DMA_RX_IRQ_EN_SET);
We enable same interrupts for rx as tx? Why enable TX interrupt here?
> + geni_write_reg((u32)(*rx_dma), base, SE_DMA_RX_PTR_L);
> + geni_write_reg((u32)((*rx_dma) >> 32), base, SE_DMA_RX_PTR_H);
> + /* RX does not have EOT bit */
Okay? How does this relate to the 0 being written into RX_ATTR?
> + geni_write_reg(0, base, SE_DMA_RX_ATTR);
> + geni_write_reg(rx_len, base, SE_DMA_RX_LEN);
> + return 0;
> +}
> +EXPORT_SYMBOL(geni_se_rx_dma_prep);
> +
> +/**
> + * geni_se_tx_dma_unprep() - Unprepare the Serial Engine after TX DMA transfer
> + * @wrapper_dev: QUP Wrapper Device to which the RX buffer is mapped.
> + * @tx_dma: DMA address of the TX buffer.
> + * @tx_len: Length of the TX buffer.
> + *
> + * This function is used to unprepare the DMA buffers after DMA TX.
> + */
> +void geni_se_tx_dma_unprep(struct device *wrapper_dev,
> + dma_addr_t tx_dma, int tx_len)
> +{
> + if (tx_dma)
> + geni_se_unmap_buf(wrapper_dev, &tx_dma, tx_len,
> + DMA_TO_DEVICE);
> +}
> +EXPORT_SYMBOL(geni_se_tx_dma_unprep);
> +
> +/**
> + * geni_se_rx_dma_unprep() - Unprepare the Serial Engine after RX DMA transfer
> + * @wrapper_dev: QUP Wrapper Device to which the RX buffer is mapped.
> + * @rx_dma: DMA address of the RX buffer.
> + * @rx_len: Length of the RX buffer.
> + *
> + * This function is used to unprepare the DMA buffers after DMA RX.
> + */
> +void geni_se_rx_dma_unprep(struct device *wrapper_dev,
> + dma_addr_t rx_dma, int rx_len)
> +{
> + if (rx_dma)
> + geni_se_unmap_buf(wrapper_dev, &rx_dma, rx_len,
> + DMA_FROM_DEVICE);
> +}
> +EXPORT_SYMBOL(geni_se_rx_dma_unprep);
> +
> +/**
> + * geni_se_map_buf() - Map a single buffer into QUP wrapper device
I find the mixture of prepare and map in the API (where prepare also
maps) confusing, but no-one calls genbi_se_map_buf() can it be made
internal?
> + * @wrapper_dev: Pointer to the corresponding QUP wrapper core.
> + * @iova: Pointer in which the mapped virtual address is stored.
> + * @buf: Address of the buffer that needs to be mapped.
> + * @size: Size of the buffer.
> + * @dir: Direction of the DMA transfer.
> + *
> + * This function is used to map an already allocated buffer into the
> + * QUP device space.
> + *
> + * Return: 0 on success, standard Linux error codes on failure/error.
> + */
> +int geni_se_map_buf(struct device *wrapper_dev, dma_addr_t *iova,
> + void *buf, size_t size, enum dma_data_direction dir)
> +{
> + struct device *dev_p;
> + struct geni_se_device *geni_se_dev;
> +
> + if (!wrapper_dev || !iova || !buf || !size)
> + return -EINVAL;
No need to check that the programmer of the intermediate function
checked that the programmer of the client filled all these out
correctly.
> +
> + geni_se_dev = dev_get_drvdata(wrapper_dev);
> + if (!geni_se_dev || !geni_se_dev->dev)
> + return -ENODEV;
Pass the geni_se_device from the child driver, to remove the need for
this.
> +
> + dev_p = geni_se_dev->dev;
Name your variables more succinct and you don't need this local alias.
> +
> + *iova = dma_map_single(dev_p, buf, size, dir);
Just inline dma_map_single() in the functions calling this.
> + if (dma_mapping_error(dev_p, *iova))
> + return -EIO;
> + return 0;
> +}
> +EXPORT_SYMBOL(geni_se_map_buf);
> +
> +/**
> + * geni_se_unmap_buf() - Unmap a single buffer from QUP wrapper device
> + * @wrapper_dev: Pointer to the corresponding QUP wrapper core.
> + * @iova: Pointer in which the mapped virtual address is stored.
> + * @size: Size of the buffer.
> + * @dir: Direction of the DMA transfer.
> + *
> + * This function is used to unmap an already mapped buffer from the
> + * QUP device space.
> + *
> + * Return: 0 on success, standard Linux error codes on failure/error.
> + */
> +int geni_se_unmap_buf(struct device *wrapper_dev, dma_addr_t *iova,
> + size_t size, enum dma_data_direction dir)
There's no reason for iova to be a pointer. Just pass the dma_addr_t as
is.
Should this function really be exposed to the clients?
> +{
> + struct device *dev_p;
> + struct geni_se_device *geni_se_dev;
> +
> + if (!wrapper_dev || !iova || !size)
> + return -EINVAL;
Reduce the error checking.
> +
> + geni_se_dev = dev_get_drvdata(wrapper_dev);
> + if (!geni_se_dev || !geni_se_dev->dev)
> + return -ENODEV;
And pass the geni_se_rsc to this function for symmetry purposes, which
would give you the wrapper by just following the pointer and then the
device from there.
> +
> + dev_p = geni_se_dev->dev;
> + dma_unmap_single(dev_p, *iova, size, dir);
Just inline dma_unmap_single() in the calling functions.
> + return 0;
> +}
> +EXPORT_SYMBOL(geni_se_unmap_buf);
> +
> +static const struct of_device_id geni_se_dt_match[] = {
> + { .compatible = "qcom,geni-se-qup", },
> + {}
> +};
Move this next to the geni_se_driver below and don't forget the
MODULE_DEVICE_TABLE()
> +
> +static int geni_se_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct resource *res;
> + struct geni_se_device *geni_se_dev;
Just name this "geni".
> + int ret = 0;
No need to initialize ret, it's only ever used after assignment.
> +
> + geni_se_dev = devm_kzalloc(dev, sizeof(*geni_se_dev), GFP_KERNEL);
> + if (!geni_se_dev)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(dev, "%s: Mandatory resource info not found\n",
> + __func__);
> + devm_kfree(dev, geni_se_dev);
> + return -EINVAL;
> + }
It's idiomatic to not check for errors of platform_get_resource() as
devm_ioremap_resource() will fail gracefully if this happens.
> +
> + geni_se_dev->base = devm_ioremap_resource(dev, res);
> + if (IS_ERR_OR_NULL(geni_se_dev->base)) {
This should be IS_ERR() only.
> + dev_err(dev, "%s: Error mapping the resource\n", __func__);
> + devm_kfree(dev, geni_se_dev);
> + return -EFAULT;
> + }
> +
> + geni_se_dev->m_ahb_clk = devm_clk_get(dev, "m-ahb");
> + if (IS_ERR(geni_se_dev->m_ahb_clk)) {
> + ret = PTR_ERR(geni_se_dev->m_ahb_clk);
> + dev_err(dev, "Err getting M AHB clk %d\n", ret);
> + devm_iounmap(dev, geni_se_dev->base);
> + devm_kfree(dev, geni_se_dev);
The reason we use the devm_ versions of these is so that we don't have
to release our resources explicitly.
> + return ret;
> + }
> +
> + geni_se_dev->s_ahb_clk = devm_clk_get(dev, "s-ahb");
> + if (IS_ERR(geni_se_dev->s_ahb_clk)) {
> + ret = PTR_ERR(geni_se_dev->s_ahb_clk);
> + dev_err(dev, "Err getting S AHB clk %d\n", ret);
> + devm_clk_put(dev, geni_se_dev->m_ahb_clk);
> + devm_iounmap(dev, geni_se_dev->base);
> + devm_kfree(dev, geni_se_dev);
> + return ret;
> + }
Use devm_clk_bulk_get().
> +
> + geni_se_dev->dev = dev;
> + mutex_init(&geni_se_dev->geni_dev_lock);
> + dev_set_drvdata(dev, geni_se_dev);
> + dev_dbg(dev, "GENI SE Driver probed\n");
> + return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
You're not depopulating these devices when the wrapper goes away. Use
devm_of_platform_populate() here instead to make that happen.
> +}
> +
> +static int geni_se_remove(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct geni_se_device *geni_se_dev = dev_get_drvdata(dev);
> +
> + devm_clk_put(dev, geni_se_dev->s_ahb_clk);
> + devm_clk_put(dev, geni_se_dev->m_ahb_clk);
> + devm_iounmap(dev, geni_se_dev->base);
> + devm_kfree(dev, geni_se_dev);
Again, the reason to use devm_* is so that you don't have to free
things...so if this is what you have here you don't even need a remove
function.
> + return 0;
> +}
> +
> +static struct platform_driver geni_se_driver = {
> + .driver = {
> + .name = "geni_se_qup",
> + .of_match_table = geni_se_dt_match,
> + },
> + .probe = geni_se_probe,
> + .remove = geni_se_remove,
> +};
> +
> +static int __init geni_se_driver_init(void)
> +{
> + return platform_driver_register(&geni_se_driver);
> +}
> +arch_initcall(geni_se_driver_init);
> +
> +static void __exit geni_se_driver_exit(void)
> +{
> + platform_driver_unregister(&geni_se_driver);
> +}
> +module_exit(geni_se_driver_exit);
Should be fine to just use module_platform_driver(), you need separate
support for earlycon regardless.
> +
> +MODULE_DESCRIPTION("GENI Serial Engine Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h
> new file mode 100644
> index 0000000..5695da9
> --- /dev/null
> +++ b/include/linux/qcom-geni-se.h
> @@ -0,0 +1,807 @@
> +/*
> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
[..]
> + */
> +
Use SPDX header as above.
> +#ifndef _LINUX_QCOM_GENI_SE
> +#define _LINUX_QCOM_GENI_SE
> +#include <linux/clk.h>
> +#include <linux/dma-direction.h>
> +#include <linux/io.h>
> +#include <linux/list.h>
I don't think there's a need for io.h or list.h here.
> +
> +/* Transfer mode supported by GENI Serial Engines */
> +enum geni_se_xfer_mode {
> + INVALID,
> + FIFO_MODE,
> + SE_DMA,
These are quite bad names for things in a header file.
> +};
> +
> +/* Protocols supported by GENI Serial Engines */
> +enum geni_se_protocol_types {
> + NONE,
> + SPI,
> + UART,
> + I2C,
> + I3C
Ditto
> +};
> +
> +/**
> + * struct geni_se_rsc - GENI Serial Engine Resource
> + * @wrapper_dev: Pointer to the parent QUP Wrapper core.
> + * @se_clk: Handle to the core serial engine clock.
> + * @geni_pinctrl: Handle to the pinctrl configuration.
> + * @geni_gpio_active: Handle to the default/active pinctrl state.
> + * @geni_gpi_sleep: Handle to the sleep pinctrl state.
> + */
> +struct geni_se_rsc {
This looks like the common geni_port or geni_device I requested above.
> + struct device *wrapper_dev;
> + struct clk *se_clk;
The one and only clock can be named "clk".
> + struct pinctrl *geni_pinctrl;
> + struct pinctrl_state *geni_gpio_active;
> + struct pinctrl_state *geni_gpio_sleep;
The associated struct device already has init, default, idle and sleep
pinctrl states associated through the device->pins struct. Typically
this means that if you provide a "default" and "sleep" state, the
default will be selected when the device probes.
In order to select between the states call
pinctrl_pm_select_{default,sleep,idle}_state(dev);
> +};
> +
> +#define PINCTRL_DEFAULT "default"
> +#define PINCTRL_SLEEP "sleep"
> +
> +/* Common SE registers */
The purpose of the helper functions in the wrapper driver is to hide
the common details from the individual function drivers, so move these
defines into the c-file as they shouldn't be needed in the individual
drivers.
> +#define GENI_INIT_CFG_REVISION (0x0)
Drop the parenthesis.
[..]
> +#ifdef CONFIG_QCOM_GENI_SE
> +/**
> + * geni_read_reg_nolog() - Helper function to read from a GENI register
> + * @base: Base address of the serial engine's register block.
> + * @offset: Offset within the serial engine's register block.
> + *
> + * Return: Return the contents of the register.
> + */
The kerneldoc goes in the implementation, not the header file. And you
already have a copy there, so remove it from here.
> +unsigned int geni_read_reg_nolog(void __iomem *base, int offset);
[..]
> +#else
I see no point in providing dummy functions for these, just make the
individual drivers either depend or select the core helpers.
> +static inline unsigned int geni_read_reg_nolog(void __iomem *base, int offset)
> +{
> + return 0;
> +}
Regards,
Bjorn
^ permalink raw reply
* Re: [PATCH v2 2/7] dt-bindings: soc: qcom: Add device tree binding for GENI SE
From: Bjorn Andersson @ 2018-01-17 6:25 UTC (permalink / raw)
To: Karthikeyan Ramasubramanian
Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
linux-serial, jslaby
In-Reply-To: <1515805547-22816-3-git-send-email-kramasub@codeaurora.org>
On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
> Add device tree binding support for the QCOM GENI SE driver.
>
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
Better describe the entire GENI, with it's functions in the same
binding.
> ---
> .../devicetree/bindings/soc/qcom/qcom,geni-se.txt | 34 ++++++++++++++++++++++
> 1 file changed, 34 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
>
> diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
> new file mode 100644
> index 0000000..66f8a16
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt
> @@ -0,0 +1,34 @@
> +Qualcomm Technologies, Inc. GENI Serial Engine QUP Wrapper Controller
> +
> +Generic Interface (GENI) based Qualcomm Universal Peripheral (QUP) wrapper
> +is a programmable module for supporting a wide range of serial interfaces
> +like UART, SPI, I2C, I3C, etc. A single QUP module can provide upto 8 Serial
> +Interfaces, using its internal Serial Engines. The GENI Serial Engine QUP
> +Wrapper controller is modeled as a node with zero or more child nodes each
> +representing a serial engine.
> +
> +Required properties:
> +- compatible: Must be "qcom,geni-se-qup".
> +- reg: Must contain QUP register address and length.
> +- clock-names: Must contain "m-ahb" and "s-ahb".
> +- clocks: AHB clocks needed by the device.
> +
> +Required properties if child node exists:
> +- #address-cells: Must be 1
> +- #size-cells: Must be 1
But on a 64-bit system, shouldn't these be 2?
> +- ranges: Must be present
> +
> +Properties for children:
> +
> +A GENI based QUP wrapper controller node can contain 0 or more child nodes
> +representing serial devices. These serial devices can be a QCOM UART, I2C
> +controller, spi controller, or some combination of aforementioned devices.
> +
> +Example:
> + qup0: qcom,geniqup0@8c0000 {
I don't see a reason for this to be referenced, so skip the label. And
don't use qcom, in the node name; "geni" would be perfectly fine?
> + compatible = "qcom,geni-se-qup";
> + reg = <0x8c0000 0x6000>;
> + clock-names = "m-ahb", "s-ahb";
> + clocks = <&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
> + <&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
> + }
Regards,
Bjorn
^ permalink raw reply
* Re: [PATCH v2 4/7] dt-bindings: i2c: Add device tree bindings for GENI I2C Controller
From: Bjorn Andersson @ 2018-01-17 6:31 UTC (permalink / raw)
To: Karthikeyan Ramasubramanian
Cc: corbet-T1hC0tSOHrs, andy.gross-QSEj5FYQhm4dnm+yROfE0A,
david.brown-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
wsa-z923LK4zBo2bacvFa/9K2g,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
linux-serial-u79uwXL29TY76Z2rM5mHXA, jslaby-IBi9RG/b67k,
Sagar Dharia
In-Reply-To: <1515805547-22816-5-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
> Add device tree binding support for I2C Controller in GENI based
> QUP Wrapper.
>
> Signed-off-by: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> ---
> .../devicetree/bindings/i2c/i2c-qcom-geni.txt | 35 ++++++++++++++++++++++
> .../devicetree/bindings/soc/qcom/qcom,geni-se.txt | 19 ++++++++++++
> 2 files changed, 54 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
>
> diff --git a/Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt b/Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
> new file mode 100644
> index 0000000..ea84be7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/i2c/i2c-qcom-geni.txt
> @@ -0,0 +1,35 @@
> +Qualcomm Technologies Inc. GENI Serial Engine based I2C Controller
> +
> +Required properties:
> + - compatible: Should be:
> + * "qcom,i2c-geni.
As this is a subset of geni it would look better with qcom,geni-i2c
imho.
> + - reg: Should contain QUP register address and length.
> + - interrupts: Should contain I2C interrupt.
> + - clock-names: Should contain "se-clk".
Omit "clk" from the clock names.
> + - clocks: Serial engine core clock needed by the device.
> + - pinctrl-names/pinctrl-0/1: The GPIOs assigned to this core. The names
> + should be "active" and "sleep" for the pin confuguration when core is active
> + or when entering sleep state.
No need to describe pinctrl properties - and your description here
doesn't match the code.
> + - #address-cells: Should be <1> Address cells for i2c device address
> + - #size-cells: Should be <0> as i2c addresses have no size component
> +
> +Optional property:
> + - clock-frequency : Desired I2C bus clock frequency in Hz.
> + When missing default to 400000Hz.
> +
> +Child nodes should conform to i2c bus binding.
..."as described in i2c.txt"
Regards,
Bjorn
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 6/7] dt-bindings: serial: Add bindings for GENI based UART Controller
From: Bjorn Andersson @ 2018-01-17 6:35 UTC (permalink / raw)
To: Karthikeyan Ramasubramanian
Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, wsa,
gregkh, linux-doc, linux-arm-msm, devicetree, linux-i2c,
linux-serial, jslaby, Girish Mahadevan
In-Reply-To: <1515805547-22816-7-git-send-email-kramasub@codeaurora.org>
On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
> Add device tree binding support for GENI based UART Controller in the
> QUP Wrapper.
>
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
> ---
> .../devicetree/bindings/serial/qcom,geni-uart.txt | 29 ++++++++++++++++++++++
> .../devicetree/bindings/soc/qcom/qcom,geni-se.txt | 13 ++++++++++
> 2 files changed, 42 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
>
> diff --git a/Documentation/devicetree/bindings/serial/qcom,geni-uart.txt b/Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
> new file mode 100644
> index 0000000..e7b9e24
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/serial/qcom,geni-uart.txt
> @@ -0,0 +1,29 @@
> +Qualcomm Technologies Inc. GENI Serial Engine based UART Controller
> +
> +The Generic Interface (GENI) Serial Engine based UART controller supports
> +console use-cases and is supported only by GENI based Qualcomm Universal
> +Peripheral (QUP) cores.
> +
> +Required properties:
> +- compatible: should contain "qcom,geni-debug-uart".
Why is this uart a _debug_ uart? Is there a separate binding for the
geni-uart?
I like that your naming here matches my suggestion with qcom,geni-i2c.
> +- reg: Should contain UART register location and length.
> +- reg-names: Should contain "se-phys".
No need to specify reg-names for a single reg.
> +- interrupts: Should contain UART core interrupts.
> +- clock-names: Should contain "se-clk".
Omit the "clk"
> +- clocks: clocks needed for UART, includes the core clock.
Be more specific.
> +- pinctrl-names/pinctrl-0/1: The GPIOs assigned to this core. The names
> + Should be "active" and "sleep" for the pin confuguration when core is active
> + or when entering sleep state.
Omit pinctrl information.
> +
> +Example:
> +uart0: qcom,serial@a88000 {
Don't use qcom, in node name. This should be named "serial".
> + compatible = "qcom,geni-debug-uart";
> + reg = <0xa88000 0x7000>;
> + reg-names = "se-phys";
> + interrupts = <0 355 0>;
<GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>
> + clock-names = "se-clk";
> + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>;
> + pinctrl-names = "default", "sleep";
> + pinctrl-0 = <&qup_1_uart_3_active>;
> + pinctrl-1 = <&qup_1_uart_3_sleep>;
> +};
Regards,
Bjorn
^ permalink raw reply
* [PATCH] serdev: add method to set parity
From: Ulrich Hecht @ 2018-01-17 15:42 UTC (permalink / raw)
To: linux-serial-u79uwXL29TY76Z2rM5mHXA
Cc: linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA,
linux-bluetooth-u79uwXL29TY76Z2rM5mHXA,
robh-DgEjT+Ai2ygdnm+yROfE0A,
martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg,
johan-DgEjT+Ai2ygdnm+yROfE0A, marcel-kz+m5ild9QBg9hUCZPvPmw,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
magnus.damm-Re5JQEeQqe8AvxtiuMwx3w,
laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
wsa-z923LK4zBo2bacvFa/9K2g, geert-Td1EMuHUCqxL1ZNQvxDV9g,
Ulrich Hecht
Adds serdev_device_set_parity() and an implementation for ttyport.
Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
Broken out of the "[PATCH 0/6] serdev multiplexing support" series
because this kind of functionality is apparently also required
for "[RFC v2 0/9] Realtek Bluetooth serdev support (H5 protocol)",
which contains an earlier revision of this patch.
CU
Uli
drivers/tty/serdev/core.c | 12 ++++++++++++
drivers/tty/serdev/serdev-ttyport.c | 18 ++++++++++++++++++
include/linux/serdev.h | 10 ++++++++++
3 files changed, 40 insertions(+)
diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
index 5dc88f6..1f25896 100644
--- a/drivers/tty/serdev/core.c
+++ b/drivers/tty/serdev/core.c
@@ -290,6 +290,18 @@ int serdev_device_set_tiocm(struct serdev_device *serdev, int set, int clear)
}
EXPORT_SYMBOL_GPL(serdev_device_set_tiocm);
+int serdev_device_set_parity(struct serdev_device *serdev,
+ enum serdev_parity parity)
+{
+ struct serdev_controller *ctrl = serdev->ctrl;
+
+ if (!ctrl || !ctrl->ops->set_parity)
+ return -ENOTSUPP;
+
+ return ctrl->ops->set_parity(ctrl, parity);
+}
+EXPORT_SYMBOL_GPL(serdev_device_set_parity);
+
static int serdev_drv_probe(struct device *dev)
{
const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver);
diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c
index a5abb05..710e9a9 100644
--- a/drivers/tty/serdev/serdev-ttyport.c
+++ b/drivers/tty/serdev/serdev-ttyport.c
@@ -224,6 +224,23 @@ static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, u
return tty->driver->ops->tiocmset(tty, set, clear);
}
+static int ttyport_set_parity(struct serdev_controller *ctrl,
+ enum serdev_parity parity)
+{
+ struct serport *serport = serdev_controller_get_drvdata(ctrl);
+ struct tty_struct *tty = serport->tty;
+ struct ktermios ktermios = tty->termios;
+
+ ktermios.c_cflag &= ~(PARENB | PARODD);
+ if (parity != SERDEV_PARITY_NONE) {
+ ktermios.c_cflag |= PARENB;
+ if (parity == SERDEV_PARITY_ODD)
+ ktermios.c_cflag |= PARODD;
+ }
+
+ return tty_set_termios(tty, &ktermios);
+}
+
static const struct serdev_controller_ops ctrl_ops = {
.write_buf = ttyport_write_buf,
.write_flush = ttyport_write_flush,
@@ -235,6 +252,7 @@ static const struct serdev_controller_ops ctrl_ops = {
.wait_until_sent = ttyport_wait_until_sent,
.get_tiocm = ttyport_get_tiocm,
.set_tiocm = ttyport_set_tiocm,
+ .set_parity = ttyport_set_parity,
};
struct device *serdev_tty_port_register(struct tty_port *port,
diff --git a/include/linux/serdev.h b/include/linux/serdev.h
index 48d8ce2..4dd3cb7 100644
--- a/include/linux/serdev.h
+++ b/include/linux/serdev.h
@@ -78,6 +78,12 @@ static inline struct serdev_device_driver *to_serdev_device_driver(struct device
return container_of(d, struct serdev_device_driver, driver);
}
+enum serdev_parity {
+ SERDEV_PARITY_NONE,
+ SERDEV_PARITY_EVEN,
+ SERDEV_PARITY_ODD,
+};
+
/*
* serdev controller structures
*/
@@ -92,6 +98,7 @@ struct serdev_controller_ops {
void (*wait_until_sent)(struct serdev_controller *, long);
int (*get_tiocm)(struct serdev_controller *);
int (*set_tiocm)(struct serdev_controller *, unsigned int, unsigned int);
+ int (*set_parity)(struct serdev_controller *, enum serdev_parity);
};
/**
@@ -301,6 +308,9 @@ static inline int serdev_device_set_rts(struct serdev_device *serdev, bool enabl
return serdev_device_set_tiocm(serdev, 0, TIOCM_RTS);
}
+int serdev_device_set_parity(struct serdev_device *serdev,
+ enum serdev_parity parity);
+
/*
* serdev hooks into TTY core
*/
--
2.7.4
^ permalink raw reply related
* RCU stall in 8250 serial driver Linux 4.15-rc1
From: Shankara Pailoor @ 2018-01-17 16:53 UTC (permalink / raw)
To: gregkh, jslaby; +Cc: LKML, syzkaller, linux-serial
Hi,
Syzkaller found the following rcu stall report in Linux 4.15-rc1:
https://pastebin.com/NyZ9JdRv
The following C program reproduces it: https://pastebin.com/gqwDWWpA
Configs Here: https://pastebin.com/v6M3iKi1
Regards,
Shankara
^ permalink raw reply
* Re: RCU stall in 8250 serial driver Linux 4.15-rc1
From: Greg KH @ 2018-01-17 17:05 UTC (permalink / raw)
To: Shankara Pailoor; +Cc: jslaby, LKML, syzkaller, linux-serial
In-Reply-To: <CAASgV=tHtf88=h-wtUrnoC74M6BEnNXcCg8d3DyWJfx21dnLYg@mail.gmail.com>
On Wed, Jan 17, 2018 at 08:53:06AM -0800, Shankara Pailoor wrote:
> Hi,
>
> Syzkaller found the following rcu stall report in Linux 4.15-rc1:
> https://pastebin.com/NyZ9JdRv
>
> The following C program reproduces it: https://pastebin.com/gqwDWWpA
>
> Configs Here: https://pastebin.com/v6M3iKi1
I don't visit random web sites for random bugs. Please include all of
the information in the email itself.
thanks,
greg k-h
^ permalink raw reply
* Re: RCU stall in 8250 serial driver Linux 4.15-rc1
From: Shankara Pailoor @ 2018-01-17 17:24 UTC (permalink / raw)
To: Greg KH; +Cc: jslaby, LKML, syzkaller, linux-serial
In-Reply-To: <20180117170520.GA12606@kroah.com>
Hi Greg,
Sorry for that. Here is the stack trace. C Program below
TCP: request_sock_TCP: Possible SYN flooding on port 20003. Sending
cookies. Check SNMP counters.
TCP: request_sock_TCP: Possible SYN flooding on port 20003. Sending
cookies. Check SNMP counters.
TCP: request_sock_TCP: Possible SYN flooding on port 20003. Sending
cookies. Check SNMP counters.
INFO: rcu_sched detected stalls on CPUs/tasks:
1-....: (5686 ticks this GP) idle=59e/140000000000000/0
softirq=13217/13218 fqs=15550
(detected by 3, t=65002 jiffies, g=3309, c=3308, q=1338676)
Sending NMI from CPU 3 to CPUs 1:
NMI backtrace for cpu 1
CPU: 1 PID: 3206 Comm: syzkaller260107 Not tainted 4.15.0-rc1 #1
Hardware name: Google Google Compute Engine/Google Compute Engine,
BIOS Google 01/01/2011
task: ffff8800b5688000 task.stack: ffff8800b5ae0000
RIP: 0010:inb arch/x86/include/asm/io.h:348 [inline]
RIP: 0010:io_serial_in+0x60/0x80 drivers/tty/serial/8250/8250_port.c:434
RSP: 0018:ffff880105086320 EFLAGS: 00000002
RAX: dffffc0000000000 RBX: 00000000000003fd RCX: 0000000000000000
RDX: 00000000000003fd RSI: 0000000000000005 RDI: ffffffffb7699720
RBP: ffffffffb76996e0 R08: 0000000000000005 R09: 0000000000000000
R10: 0000000000000001 R11: 1ffff10016ad112c R12: 0000000000000020
R13: fffffbfff6ed3323 R14: fffffbfff6ed32e6 R15: ffffffffb769991a
FS: 00000000012d3880(0000) GS:ffff880105080000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00000000200007b6 CR3: 00000000ba7eb004 CR4: 00000000001606e0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
<IRQ>
serial_in drivers/tty/serial/8250/8250.h:111 [inline]
wait_for_xmitr+0x8a/0x1d0 drivers/tty/serial/8250/8250_port.c:2033
serial8250_console_putchar+0x19/0x50 drivers/tty/serial/8250/8250_port.c:3170
uart_console_write+0x98/0xc0 drivers/tty/serial/serial_core.c:1858
serial_port_out include/linux/serial_core.h:265 [inline]
serial8250_console_write+0x2ad/0x890 drivers/tty/serial/8250/8250_port.c:3243
trace_console_rcuidle include/trace/events/printk.h:10 [inline]
call_console_drivers kernel/printk/printk.c:1556 [inline]
console_unlock+0x635/0xb40 kernel/printk/printk.c:2233
log_output kernel/printk/printk.c:1675 [inline]
vprintk_emit+0x391/0x480 kernel/printk/printk.c:1745
vprintk_func+0x52/0xc0 kernel/printk/printk_safe.c:379
printk+0xaa/0xca kernel/printk/printk.c:1829
tcp_syn_flood_action net/ipv4/tcp_input.c:6171 [inline]
tcp_conn_request+0x2c91/0x2fc0 net/ipv4/tcp_input.c:6215
tcp_v4_conn_request+0x14f/0x200 net/ipv4/tcp_ipv4.c:1318
tcp_rcv_state_process+0x900/0x4770 net/ipv4/tcp_input.c:5819
tcp_v4_do_rcv+0x550/0x7c0 net/ipv4/tcp_ipv4.c:1490
tcp_v4_rcv+0x2c2d/0x2d30 net/ipv4/tcp_ipv4.c:1719
ip_local_deliver_finish+0x317/0xbf0 net/ipv4/ip_input.c:216
NF_HOOK include/linux/netfilter.h:250 [inline]
ip_local_deliver+0x1ba/0x640 net/ipv4/ip_input.c:257
dst_input include/net/dst.h:466 [inline]
ip_rcv_finish+0x867/0x1970 net/ipv4/ip_input.c:397
NF_HOOK include/linux/netfilter.h:250 [inline]
ip_rcv+0xb9d/0x1650 net/ipv4/ip_input.c:493
__netif_receive_skb_core+0x1e56/0x3330 net/core/dev.c:4461
__netif_receive_skb+0x27/0x1a0 net/core/dev.c:4526
rcu_read_unlock include/linux/rcupdate.h:683 [inline]
process_backlog+0x1d8/0x6b0 net/core/dev.c:5206
napi_poll net/core/dev.c:5604 [inline]
net_rx_action+0x66b/0x1330 net/core/dev.c:5669
trace_softirq_exit include/trace/events/irq.h:142 [inline]
__do_softirq+0x25e/0xabe kernel/softirq.c:286
do_softirq_own_stack+0x2a/0x40 arch/x86/entry/entry_64.S:984
</IRQ>
do_softirq.part.16+0x70/0x90 kernel/softirq.c:88
__local_bh_enable_ip+0x17c/0x1b0 kernel/softirq.c:161
local_bh_enable include/linux/bottom_half.h:32 [inline]
rcu_read_unlock_bh include/linux/rcupdate.h:727 [inline]
ip_finish_output2+0x8ad/0x1370 net/ipv4/ip_output.c:231
ip_finish_output+0x727/0xb20 net/ipv4/ip_output.c:317
NF_HOOK_COND include/linux/netfilter.h:239 [inline]
ip_output+0x1cf/0x720 net/ipv4/ip_output.c:405
dst_output include/net/dst.h:460 [inline]
ip_local_out+0x8e/0x160 net/ipv4/ip_output.c:124
ip_queue_xmit+0x934/0x1890 net/ipv4/ip_output.c:504
tcp_transmit_skb+0x19a9/0x35d0 net/ipv4/tcp_output.c:1176
tcp_connect+0x2824/0x3890 net/ipv4/tcp_output.c:3498
C program:
// autogenerated by syzkaller (http://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <sys/syscall.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <time.h>
#include <sys/prctl.h>
__attribute__((noreturn)) static void doexit(int status)
{
volatile unsigned i;
syscall(__NR_exit_group, status);
for (i = 0;; i++) {
}
}
#define NORETURN __attribute__((noreturn))
#include <stdint.h>
#include <string.h>
const int kFailStatus = 67;
const int kRetryStatus = 69;
NORETURN static void fail(const char* msg, ...)
{
int e = errno;
fflush(stdout);
va_list args;
va_start(args, msg);
vfprintf(stderr, msg, args);
va_end(args);
fprintf(stderr, " (errno %d)\n", e);
doexit((e == ENOMEM || e == EAGAIN) ? kRetryStatus : kFailStatus);
}
NORETURN static void exitf(const char* msg, ...)
{
int e = errno;
fflush(stdout);
va_list args;
va_start(args, msg);
vfprintf(stderr, msg, args);
va_end(args);
fprintf(stderr, " (errno %d)\n", e);
doexit(kRetryStatus);
}
static uint64_t current_time_ms()
{
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts))
fail("clock_gettime failed");
return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
}
static void test();
void loop()
{
int iter;
for (iter = 0;; iter++) {
int pid = fork();
if (pid < 0)
fail("clone failed");
if (pid == 0) {
prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
setpgrp();
test();
doexit(0);
}
int status = 0;
uint64_t start = current_time_ms();
for (;;) {
int res = waitpid(-1, &status, __WALL | WNOHANG);
if (res == pid)
break;
usleep(1000);
if (current_time_ms() - start > 5 * 1000) {
kill(-pid, SIGKILL);
kill(pid, SIGKILL);
while (waitpid(-1, &status, __WALL) != pid) {
}
break;
}
}
}
}
long r[28];
void test()
{
memset(r, -1, sizeof(r));
r[0] = syscall(__NR_mmap, 0x20000000ul, 0x3b8000ul, 0x3ul, 0x32ul,
0xfffffffffffffffful, 0x0ul);
r[1] = syscall(__NR_socket, 0x2ul, 0x1ul, 0x0ul);
*(uint16_t*)0x200007b6 = (uint16_t)0x2;
*(uint16_t*)0x200007b8 = (uint16_t)0x234e;
*(uint32_t*)0x200007ba = (uint32_t)0x0;
*(uint8_t*)0x200007be = (uint8_t)0x0;
*(uint8_t*)0x200007bf = (uint8_t)0x0;
*(uint8_t*)0x200007c0 = (uint8_t)0x0;
*(uint8_t*)0x200007c1 = (uint8_t)0x0;
*(uint8_t*)0x200007c2 = (uint8_t)0x0;
*(uint8_t*)0x200007c3 = (uint8_t)0x0;
*(uint8_t*)0x200007c4 = (uint8_t)0x0;
*(uint8_t*)0x200007c5 = (uint8_t)0x0;
r[13] = syscall(__NR_bind, r[1], 0x200007b6ul, 0x10ul);
r[14] = syscall(__NR_listen, r[1], 0x0ul);
r[15] = syscall(__NR_socket, 0x2ul, 0x1ul, 0x0ul);
*(uint16_t*)0x200008e6 = (uint16_t)0x2;
*(uint16_t*)0x200008e8 = (uint16_t)0x234e;
*(uint32_t*)0x200008ea = (uint32_t)0x100007f;
*(uint8_t*)0x200008ee = (uint8_t)0x0;
*(uint8_t*)0x200008ef = (uint8_t)0x0;
*(uint8_t*)0x200008f0 = (uint8_t)0x0;
*(uint8_t*)0x200008f1 = (uint8_t)0x0;
*(uint8_t*)0x200008f2 = (uint8_t)0x0;
*(uint8_t*)0x200008f3 = (uint8_t)0x0;
*(uint8_t*)0x200008f4 = (uint8_t)0x0;
*(uint8_t*)0x200008f5 = (uint8_t)0x0;
r[27] = syscall(__NR_connect, r[15], 0x200008e6ul, 0x10ul);
}
int main()
{
int i; for (i = 0; i < 8; i++) {
if (fork() == 0) {
loop();
return 0;
}
}
sleep(1000000);
return 0;
}
Regards,
Shankara
On Wed, Jan 17, 2018 at 9:05 AM, Greg KH <gregkh@linuxfoundation.org> wrote:
> On Wed, Jan 17, 2018 at 08:53:06AM -0800, Shankara Pailoor wrote:
>> Hi,
>>
>> Syzkaller found the following rcu stall report in Linux 4.15-rc1:
>> https://pastebin.com/NyZ9JdRv
>>
>> The following C program reproduces it: https://pastebin.com/gqwDWWpA
>>
>> Configs Here: https://pastebin.com/v6M3iKi1
>
> I don't visit random web sites for random bugs. Please include all of
> the information in the email itself.
>
> thanks,
>
> greg k-h
^ permalink raw reply
* Re: [patch v17 1/4] drivers: jtag: Add JTAG core driver
From: Julia Cartwright @ 2018-01-17 19:14 UTC (permalink / raw)
To: Oleksandr Shamray
Cc: gregkh, arnd, linux-kernel, linux-arm-kernel, devicetree, openbmc,
joel, jiri, tklauser, linux-serial, vadimp, system-sw-low-level,
robh+dt, openocd-devel-owner, linux-api, davem, mchehab,
Jiri Pirko
In-Reply-To: <1516087139-7510-2-git-send-email-oleksandrs@mellanox.com>
Hello Oleksandr-
On Tue, Jan 16, 2018 at 09:18:56AM +0200, Oleksandr Shamray wrote:
[..]
> v16->v17
> Comments pointed by Julia Cartwright <juliac@eso.teric.us>
More review feedback below:
[..]
> +++ b/drivers/jtag/jtag.c
[..]
> +static long jtag_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> +{
> + struct jtag *jtag = file->private_data;
> + struct jtag_run_test_idle idle;
> + struct jtag_xfer xfer;
> + u8 *xfer_data;
> + u32 data_size;
> + u32 value;
> + int err;
> +
> + if (!arg)
> + return -EINVAL;
> +
> + switch (cmd) {
> + case JTAG_GIOCFREQ:
> + if (!jtag->ops->freq_get)
> + err = -EOPNOTSUPP;
Did you mean:
return -EOPNOTSUPP;
?
> +
> + err = jtag->ops->freq_get(jtag, &value);
Otherwise you're check was worthless, you'll call NULL here.
Also, w.r.t. the set of ops which are required to be implemented: this
isn't the right place to do the check.
Instead, do it in jtag_alloc():
struct jtag *jtag_alloc(size_t priv_size, const struct jtag_ops *ops)
{
struct jtag *jtag;
if (!ops->freq_get || !ops->xfer || ...) /* fixup condition */
return NULL;
jtag = kzalloc(sizeof(*jtag) + priv_size, GFP_KERNEL);
if (!jtag)
return NULL;
jtag->ops = ops;
return jtag;
}
EXPORT_SYMBOL_GPL(jtag_alloc);
[..]
> + case JTAG_IOCXFER:
[..]
> + data_size = DIV_ROUND_UP(xfer.length, BITS_PER_BYTE);
> + xfer_data = memdup_user(u64_to_user_ptr(xfer.tdio), data_size);
> +
> + if (!xfer_data)
memdup_user() doesn't return NULL on error. You need to check for
IS_ERR(xfer_data).
> + return -EFAULT;
> +
> + err = jtag->ops->xfer(jtag, &xfer, xfer_data);
> + if (err) {
> + kfree(xfer_data);
> + return -EFAULT;
> + }
> +
> + err = copy_to_user(u64_to_user_ptr(xfer.tdio),
> + (void *)(xfer_data), data_size);
> +
> + if (err) {
> + kfree(xfer_data);
> + return -EFAULT;
> + }
> +
> + kfree(xfer_data);
Move the kfree() above the if (err).
> + if (copy_to_user((void *)arg, &xfer, sizeof(struct jtag_xfer)))
> + return -EFAULT;
> + break;
> +
> + case JTAG_GIOCSTATUS:
> + if (!jtag->ops->status_get)
> + return -EOPNOTSUPP;
> +
> + err = jtag->ops->status_get(jtag, &value);
> + if (err)
> + break;
> +
> + err = put_user(value, (__u32 *)arg);
> + if (err)
> + err = -EFAULT;
put_user() returns -EFAULT on failure, so this shouldn't be necessary.
[..]
> --- /dev/null
> +++ b/include/uapi/linux/jtag.h
[..]
> +/**
> + * struct jtag_xfer - jtag xfer:
> + *
> + * @type: transfer type
> + * @direction: xfer direction
> + * @length: xfer bits len
> + * @tdio : xfer data array
> + * @endir: xfer end state
> + *
> + * Structure represents interface to JTAG device for jtag sdr xfer
> + * execution.
> + */
> +struct jtag_xfer {
> + __u8 type;
> + __u8 direction;
> + __u8 endstate;
Just to be as unambiguous as possible, considering this is ABI, I would
suggest explicitly putting a padding byte here.
> + __u32 length;
> + __u64 tdio;
> +};
Thanks,
Julia
^ permalink raw reply
* Re: [PATCH] serdev: add method to set parity
From: Johan Hovold @ 2018-01-18 2:23 UTC (permalink / raw)
To: Ulrich Hecht
Cc: linux-serial-u79uwXL29TY76Z2rM5mHXA,
linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA,
linux-bluetooth-u79uwXL29TY76Z2rM5mHXA,
robh-DgEjT+Ai2ygdnm+yROfE0A,
martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg,
johan-DgEjT+Ai2ygdnm+yROfE0A, marcel-kz+m5ild9QBg9hUCZPvPmw,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
magnus.damm-Re5JQEeQqe8AvxtiuMwx3w,
laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
wsa-z923LK4zBo2bacvFa/9K2g, geert-Td1EMuHUCqxL1ZNQvxDV9g
In-Reply-To: <1516203739-4705-1-git-send-email-ulrich.hecht+renesas-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On Wed, Jan 17, 2018 at 04:42:19PM +0100, Ulrich Hecht wrote:
> Adds serdev_device_set_parity() and an implementation for ttyport.
Perhaps you can mention that the interface uses an enum and the three
settings that you add here.
> Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
> Broken out of the "[PATCH 0/6] serdev multiplexing support" series
> because this kind of functionality is apparently also required
> for "[RFC v2 0/9] Realtek Bluetooth serdev support (H5 protocol)",
> which contains an earlier revision of this patch.
Thanks for submitting this separately.
> drivers/tty/serdev/core.c | 12 ++++++++++++
> drivers/tty/serdev/serdev-ttyport.c | 18 ++++++++++++++++++
> include/linux/serdev.h | 10 ++++++++++
> 3 files changed, 40 insertions(+)
>
> diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
> index 5dc88f6..1f25896 100644
> --- a/drivers/tty/serdev/core.c
> +++ b/drivers/tty/serdev/core.c
> @@ -290,6 +290,18 @@ int serdev_device_set_tiocm(struct serdev_device *serdev, int set, int clear)
> }
> EXPORT_SYMBOL_GPL(serdev_device_set_tiocm);
>
> +int serdev_device_set_parity(struct serdev_device *serdev,
> + enum serdev_parity parity)
> +{
> + struct serdev_controller *ctrl = serdev->ctrl;
> +
> + if (!ctrl || !ctrl->ops->set_parity)
> + return -ENOTSUPP;
> +
> + return ctrl->ops->set_parity(ctrl, parity);
> +}
> +EXPORT_SYMBOL_GPL(serdev_device_set_parity);
Please place the parity functions (and fields) after the set_flow
equivalents so that we keep the line-setting functionality grouped
together.
> +static int ttyport_set_parity(struct serdev_controller *ctrl,
> + enum serdev_parity parity)
> +{
> + struct serport *serport = serdev_controller_get_drvdata(ctrl);
> + struct tty_struct *tty = serport->tty;
> + struct ktermios ktermios = tty->termios;
> +
> + ktermios.c_cflag &= ~(PARENB | PARODD);
You should also clear CMSPAR.
> + if (parity != SERDEV_PARITY_NONE) {
> + ktermios.c_cflag |= PARENB;
> + if (parity == SERDEV_PARITY_ODD)
> + ktermios.c_cflag |= PARODD;
> + }
> +
> + return tty_set_termios(tty, &ktermios);
Note that tty_set_termios() always return 0. You need to verify that you
got what you requested by looking at the termios after the function
returns (and possibly return -EINVAL). Not all drivers support (all)
parity modes.
Note that we currently do not implement proper error handling for flow
control, but that should be fixed.
Looks good otherwise.
Thanks,
Johan
^ permalink raw reply
* Re: [PATCH v2 5/7] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
From: Bjorn Andersson @ 2018-01-18 5:23 UTC (permalink / raw)
To: Karthikeyan Ramasubramanian
Cc: corbet-T1hC0tSOHrs, andy.gross-QSEj5FYQhm4dnm+yROfE0A,
david.brown-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
wsa-z923LK4zBo2bacvFa/9K2g,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
linux-serial-u79uwXL29TY76Z2rM5mHXA, jslaby-IBi9RG/b67k,
Sagar Dharia, Girish Mahadevan
In-Reply-To: <1515805547-22816-6-git-send-email-kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
On Fri 12 Jan 17:05 PST 2018, Karthikeyan Ramasubramanian wrote:
> This bus driver supports the GENI based i2c hardware controller in the
> Qualcomm SOCs. The Qualcomm Generic Interface (GENI) is a programmable
> module supporting a wide range of serial interfaces including I2C. The
> driver supports FIFO mode and DMA mode of transfer and switches modes
> dynamically depending on the size of the transfer.
>
> Signed-off-by: Karthikeyan Ramasubramanian <kramasub-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> Signed-off-by: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> Signed-off-by: Girish Mahadevan <girishm-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> ---
> drivers/i2c/busses/Kconfig | 10 +
> drivers/i2c/busses/Makefile | 1 +
> drivers/i2c/busses/i2c-qcom-geni.c | 656 +++++++++++++++++++++++++++++++++++++
> 3 files changed, 667 insertions(+)
> create mode 100644 drivers/i2c/busses/i2c-qcom-geni.c
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index 009345d..caef309 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -838,6 +838,16 @@ config I2C_PXA_SLAVE
> is necessary for systems where the PXA may be a target on the
> I2C bus.
>
> +config I2C_QCOM_GENI
> + tristate "Qualcomm Technologies Inc.'s GENI based I2C controller"
> + depends on ARCH_QCOM
Just depend on the GENI wrapper as well.
> + help
> + If you say yes to this option, support will be included for the
> + built-in I2C interface on the Qualcomm Technologies Inc.'s SoCs.
> +
> + This driver can also be built as a module. If so, the module
> + will be called i2c-qcom-geni.
> +
> config I2C_QUP
> tristate "Qualcomm QUP based I2C controller"
> depends on ARCH_QCOM
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index 2ce8576..201fce1 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -84,6 +84,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
> obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o
> obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
> obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o
> +obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom-geni.o
> obj-$(CONFIG_I2C_QUP) += i2c-qup.o
> obj-$(CONFIG_I2C_RIIC) += i2c-riic.o
> obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o
> diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
> new file mode 100644
> index 0000000..59ad4da
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-qcom-geni.c
> @@ -0,0 +1,656 @@
> +/*
> + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
Use SPDX license header.
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
Unused?
> +#include <linux/err.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/qcom-geni-se.h>
> +
> +#define SE_I2C_TX_TRANS_LEN (0x26C)
Drop the parenthesis, when not needed.
> +#define SE_I2C_RX_TRANS_LEN (0x270)
> +#define SE_I2C_SCL_COUNTERS (0x278)
> +#define SE_GENI_IOS (0x908)
> +
> +#define SE_I2C_ERR (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |\
> + M_GP_IRQ_1_EN | M_GP_IRQ_3_EN | M_GP_IRQ_4_EN)
> +#define SE_I2C_ABORT (1U << 1)
> +/* M_CMD OP codes for I2C */
> +#define I2C_WRITE (0x1)
> +#define I2C_READ (0x2)
> +#define I2C_WRITE_READ (0x3)
> +#define I2C_ADDR_ONLY (0x4)
> +#define I2C_BUS_CLEAR (0x6)
> +#define I2C_STOP_ON_BUS (0x7)
> +/* M_CMD params for I2C */
> +#define PRE_CMD_DELAY (BIT(0))
> +#define TIMESTAMP_BEFORE (BIT(1))
> +#define STOP_STRETCH (BIT(2))
> +#define TIMESTAMP_AFTER (BIT(3))
> +#define POST_COMMAND_DELAY (BIT(4))
> +#define IGNORE_ADD_NACK (BIT(6))
> +#define READ_FINISHED_WITH_ACK (BIT(7))
> +#define BYPASS_ADDR_PHASE (BIT(8))
> +#define SLV_ADDR_MSK (GENMASK(15, 9))
> +#define SLV_ADDR_SHFT (9)
> +
> +#define I2C_CORE2X_VOTE (10000)
> +#define GP_IRQ0 0
> +#define GP_IRQ1 1
> +#define GP_IRQ2 2
> +#define GP_IRQ3 3
> +#define GP_IRQ4 4
> +#define GP_IRQ5 5
> +#define GENI_OVERRUN 6
> +#define GENI_ILLEGAL_CMD 7
> +#define GENI_ABORT_DONE 8
> +#define GENI_TIMEOUT 9
> +
> +#define I2C_NACK GP_IRQ1
> +#define I2C_BUS_PROTO GP_IRQ3
> +#define I2C_ARB_LOST GP_IRQ4
> +#define DM_I2C_CB_ERR ((BIT(GP_IRQ1) | BIT(GP_IRQ3) | BIT(GP_IRQ4)) \
> + << 5)
> +
> +#define I2C_AUTO_SUSPEND_DELAY 250
> +#define KHz(freq) (1000 * freq)
> +
> +struct geni_i2c_dev {
> + struct device *dev;
> + void __iomem *base;
> + unsigned int tx_wm;
> + int irq;
> + int err;
> + struct i2c_adapter adap;
> + struct completion xfer;
How about naming this "done" or something like that, gi2c->xfer doesn't
really give the sense of being a "we're done with the operation"-event.
> + struct i2c_msg *cur;
> + struct geni_se_rsc i2c_rsc;
> + int cur_wr;
> + int cur_rd;
> + struct device *wrapper_dev;
This is already availabe in i2c_rsc, and in particular if you pass the
i2c_rsc down to the wrapper in the 2 cases you use the wrapper_dev you
don't need this duplication.
> + u32 clk_freq_out;
> + int clk_fld_idx;
Keep track of the const struct geni_i2c_clk_fld * here instead.
> +};
> +
> +struct geni_i2c_err_log {
> + int err;
> + const char *msg;
> +};
> +
> +static struct geni_i2c_err_log gi2c_log[] = {
> + [GP_IRQ0] = {-EINVAL, "Unknown I2C err GP_IRQ0"},
> + [I2C_NACK] = {-ENOTCONN,
> + "NACK: slv unresponsive, check its power/reset-ln"},
Break the 80-char rule, to improve readability.
> + [GP_IRQ2] = {-EINVAL, "Unknown I2C err GP IRQ2"},
> + [I2C_BUS_PROTO] = {-EPROTO,
> + "Bus proto err, noisy/unepxected start/stop"},
> + [I2C_ARB_LOST] = {-EBUSY,
> + "Bus arbitration lost, clock line undriveable"},
> + [GP_IRQ5] = {-EINVAL, "Unknown I2C err GP IRQ5"},
> + [GENI_OVERRUN] = {-EIO, "Cmd overrun, check GENI cmd-state machine"},
> + [GENI_ILLEGAL_CMD] = {-EILSEQ,
> + "Illegal cmd, check GENI cmd-state machine"},
> + [GENI_ABORT_DONE] = {-ETIMEDOUT, "Abort after timeout successful"},
> + [GENI_TIMEOUT] = {-ETIMEDOUT, "I2C TXN timed out"},
> +};
> +
> +struct geni_i2c_clk_fld {
> + u32 clk_freq_out;
> + u8 clk_div;
> + u8 t_high;
> + u8 t_low;
> + u8 t_cycle;
> +};
> +
> +static struct geni_i2c_clk_fld geni_i2c_clk_map[] = {
const
> + {KHz(100), 7, 10, 11, 26},
> + {KHz(400), 2, 5, 12, 24},
> + {KHz(1000), 1, 3, 9, 18},
> +};
> +
> +static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c)
> +{
> + int i;
> + int ret = 0;
> + bool clk_map_present = false;
> + struct geni_i2c_clk_fld *itr = geni_i2c_clk_map;
> +
> + for (i = 0; i < ARRAY_SIZE(geni_i2c_clk_map); i++, itr++) {
> + if (itr->clk_freq_out == gi2c->clk_freq_out) {
> + clk_map_present = true;
> + break;
Make this:
gi2c->clk_fld = geni_i2c_clk_map + i;
return 0;
> + }
> + }
> +
...then you can drop ret and clk_map_present and just return -EINVAL
here.
> + if (clk_map_present)
> + gi2c->clk_fld_idx = i;
> + else
> + ret = -EINVAL;
> +
> + return ret;
> +}
> +
> +static inline void qcom_geni_i2c_conf(struct geni_i2c_dev *gi2c, int dfs)
Drop the "inline", if it makes sense the compiler will inline it, if not
it knows better than we do.
dfs is always 0, so drop this parameter and hard code the value below.
> +{
> + struct geni_i2c_clk_fld *itr = geni_i2c_clk_map + gi2c->clk_fld_idx;
> +
> + geni_write_reg(dfs, gi2c->base, SE_GENI_CLK_SEL);
> +
> + geni_write_reg((itr->clk_div << 4) | 1, gi2c->base, GENI_SER_M_CLK_CFG);
> + geni_write_reg(((itr->t_high << 20) | (itr->t_low << 10) |
> + itr->t_cycle), gi2c->base, SE_I2C_SCL_COUNTERS);
> +
> + /* Ensure Clk config completes before return */
That's not what "mb" does, it ensures that later memory operations
aren't reordered beyond the barrier.
> + mb();
> +}
> +
> +static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
Looking at the code in this function it's just a bunch of logic to print
various debug messages...except for the last line that uses the gi2c_log
lookup table to convert the error to a errno. Sneaky...and not very
nice.
> +{
> + u32 m_cmd = readl_relaxed(gi2c->base + SE_GENI_M_CMD0);
Unless you have a really good reason, just use readl/writel in favour of
their _relaxed versions.
> + u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS);
> + u32 geni_s = readl_relaxed(gi2c->base + SE_GENI_STATUS);
> + u32 geni_ios = readl_relaxed(gi2c->base + SE_GENI_IOS);
> + u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN);
> + u32 rx_st, tx_st;
> +
> + if (gi2c->cur)
> + dev_dbg(gi2c->dev, "len:%d, slv-addr:0x%x, RD/WR:%d\n",
> + gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags);
> +
> + if (err == I2C_NACK || err == GENI_ABORT_DONE) {
> + dev_dbg(gi2c->dev, "%s\n", gi2c_log[err].msg);
> + goto err_ret;
> + } else {
> + dev_err(gi2c->dev, "%s\n", gi2c_log[err].msg);
> + }
> +
> + if (dma) {
> + rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT);
> + tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT);
> + } else {
> + rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS);
> + tx_st = readl_relaxed(gi2c->base + SE_GENI_TX_FIFO_STATUS);
> + }
> + dev_dbg(gi2c->dev, "DMA:%d tx_stat:0x%x, rx_stat:0x%x, irq-stat:0x%x\n",
> + dma, tx_st, rx_st, m_stat);
> + dev_dbg(gi2c->dev, "m_cmd:0x%x, geni_status:0x%x, geni_ios:0x%x\n",
> + m_cmd, geni_s, geni_ios);
> +err_ret:
> + gi2c->err = gi2c_log[err].err;
> +}
> +
> +static irqreturn_t geni_i2c_irq(int irq, void *dev)
> +{
> + struct geni_i2c_dev *gi2c = dev;
> + int i, j;
> + u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS);
> + u32 rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS);
> + u32 dm_tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT);
> + u32 dm_rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT);
> + u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN);
> + struct i2c_msg *cur = gi2c->cur;
> +
> + if (!cur || (m_stat & M_CMD_FAILURE_EN) ||
> + (dm_rx_st & (DM_I2C_CB_ERR)) ||
> + (m_stat & M_CMD_ABORT_EN)) {
> +
> + if (m_stat & M_GP_IRQ_1_EN)
> + geni_i2c_err(gi2c, I2C_NACK);
> + if (m_stat & M_GP_IRQ_3_EN)
> + geni_i2c_err(gi2c, I2C_BUS_PROTO);
> + if (m_stat & M_GP_IRQ_4_EN)
> + geni_i2c_err(gi2c, I2C_ARB_LOST);
> + if (m_stat & M_CMD_OVERRUN_EN)
> + geni_i2c_err(gi2c, GENI_OVERRUN);
> + if (m_stat & M_ILLEGAL_CMD_EN)
> + geni_i2c_err(gi2c, GENI_ILLEGAL_CMD);
> + if (m_stat & M_CMD_ABORT_EN)
> + geni_i2c_err(gi2c, GENI_ABORT_DONE);
> + if (m_stat & M_GP_IRQ_0_EN)
> + geni_i2c_err(gi2c, GP_IRQ0);
> +
> + if (!dma)
> + writel_relaxed(0, (gi2c->base +
> + SE_GENI_TX_WATERMARK_REG));
Drop the extra parenthesis. And using writel() instead keeps you under
80 chars.
> + goto irqret;
> + }
> +
> + if (dma) {
> + dev_dbg(gi2c->dev, "i2c dma tx:0x%x, dma rx:0x%x\n", dm_tx_st,
> + dm_rx_st);
> + goto irqret;
> + }
> +
> + if (((m_stat & M_RX_FIFO_WATERMARK_EN) ||
> + (m_stat & M_RX_FIFO_LAST_EN)) && (cur->flags & I2C_M_RD)) {
Some of these parenthesis are unnecessary, but more importantly the way
you wrap this line is misleading; the wrapping indicates that the two
latter conditionals are related, but the parenthesis says the first two
are.
The most significant part of this conditional is "is this a read
operation", so put this first.
> + u32 rxcnt = rx_st & RX_FIFO_WC_MSK;
> +
> + for (j = 0; j < rxcnt; j++) {
> + u32 temp;
> + int p;
> +
> + temp = readl_relaxed(gi2c->base + SE_GENI_RX_FIFOn);
> + for (i = gi2c->cur_rd, p = 0; (i < cur->len && p < 4);
> + i++, p++)
> + cur->buf[i] = (u8) ((temp >> (p * 8)) & 0xff);
> + gi2c->cur_rd = i;
The two-range for loop with a line wrap isn't very clean and the wrap
calls for a set of braces - which will look ugly.
How about something like this instead?
p = 0;
while (p < 4 && gi2c->cur_rd < cur->len) {
cur->buf[gi2c->cur_rd++] = temp & 0xff;
temp >>= 8;
p++;
}
> + if (gi2c->cur_rd == cur->len) {
> + dev_dbg(gi2c->dev, "FIFO i:%d,read 0x%x\n",
> + i, temp);
Who's the consumer of this debug line?
> + break;
> + }
> + }
> + } else if ((m_stat & M_TX_FIFO_WATERMARK_EN) &&
> + !(cur->flags & I2C_M_RD)) {
> + for (j = 0; j < gi2c->tx_wm; j++) {
> + u32 temp = 0;
> + int p;
> +
> + for (i = gi2c->cur_wr, p = 0; (i < cur->len && p < 4);
> + i++, p++)
> + temp |= (((u32)(cur->buf[i]) << (p * 8)));
> + writel_relaxed(temp, gi2c->base + SE_GENI_TX_FIFOn);
Same concern as above.
> + gi2c->cur_wr = i;
> + dev_dbg(gi2c->dev, "FIFO i:%d,wrote 0x%x\n", i, temp);
> + if (gi2c->cur_wr == cur->len) {
> + dev_dbg(gi2c->dev, "FIFO i2c bytes done writing\n");
> + writel_relaxed(0,
> + (gi2c->base + SE_GENI_TX_WATERMARK_REG));
The line break here is bad. checkpatch.pl --strict will help you find
these.
Also using writel() and dropping the parenthesis keeps you below 80
chars.
> + break;
> + }
> + }
> + }
> +irqret:
> + if (m_stat)
> + writel_relaxed(m_stat, gi2c->base + SE_GENI_M_IRQ_CLEAR);
Is it okay for this to be re-ordered wrt above writes?
> +
> + if (dma) {
> + if (dm_tx_st)
> + writel_relaxed(dm_tx_st, gi2c->base +
> + SE_DMA_TX_IRQ_CLR);
Use writel() and you don't have to wrap the line.
> + if (dm_rx_st)
> + writel_relaxed(dm_rx_st, gi2c->base +
> + SE_DMA_RX_IRQ_CLR);
> + /* Ensure all writes are done before returning from ISR. */
That's not what wmb does.
> + wmb();
> + }
> + /* if this is err with done-bit not set, handle that thr' timeout. */
Is "thr'" should for "through"?
> + if (m_stat & M_CMD_DONE_EN)
> + complete(&gi2c->xfer);
> + else if ((dm_tx_st & TX_DMA_DONE) || (dm_rx_st & RX_DMA_DONE))
> + complete(&gi2c->xfer);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int geni_i2c_xfer(struct i2c_adapter *adap,
> + struct i2c_msg msgs[],
> + int num)
> +{
> + struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
> + int i, ret = 0, timeout = 0;
No need to initialize these, first use is an assignment.
> +
> + gi2c->err = 0;
> + gi2c->cur = &msgs[0];
You're assigning cur in each iteration of the loop below, why do you
need to do it here as well?
> + reinit_completion(&gi2c->xfer);
> + ret = pm_runtime_get_sync(gi2c->dev);
> + if (ret < 0) {
> + dev_err(gi2c->dev, "error turning SE resources:%d\n", ret);
> + pm_runtime_put_noidle(gi2c->dev);
> + /* Set device in suspended since resume failed */
> + pm_runtime_set_suspended(gi2c->dev);
> + return ret;
With the questionable assignment above you're leaving the function with
gi2c->cur pointing to an object that will soon be invalid.
> + }
> +
> + qcom_geni_i2c_conf(gi2c, 0);
> + dev_dbg(gi2c->dev, "i2c xfer:num:%d, msgs:len:%d,flg:%d\n",
> + num, msgs[0].len, msgs[0].flags);
Use dynamic function tracing instead of debug prints for this.
> + for (i = 0; i < num; i++) {
I suggest that you split out two functions here, one for rx-one-message
and one for tx-one-message. There are some duplication between the two,
but not too bad.
> + int stretch = (i < (num - 1));
> + u32 m_param = 0;
> + u32 m_cmd = 0;
> + dma_addr_t tx_dma = 0;
> + dma_addr_t rx_dma = 0;
> + enum geni_se_xfer_mode mode = FIFO_MODE;
No need to initialize mode, as the first use is an assignment.
> +
> + m_param |= (stretch ? STOP_STRETCH : 0);
> + m_param |= ((msgs[i].addr & 0x7F) << SLV_ADDR_SHFT);
Drop some parenthesis.
> +
> + gi2c->cur = &msgs[i];
> + mode = msgs[i].len > 32 ? SE_DMA : FIFO_MODE;
> + ret = geni_se_select_mode(gi2c->base, mode);
> + if (ret) {
> + dev_err(gi2c->dev, "%s: Error mode init %d:%d:%d\n",
> + __func__, mode, i, msgs[i].len);
> + break;
> + }
> + if (msgs[i].flags & I2C_M_RD) {
> + dev_dbg(gi2c->dev,
> + "READ,n:%d,i:%d len:%d, stretch:%d\n",
> + num, i, msgs[i].len, stretch);
> + geni_write_reg(msgs[i].len,
> + gi2c->base, SE_I2C_RX_TRANS_LEN);
> + m_cmd = I2C_READ;
> + geni_se_setup_m_cmd(gi2c->base, m_cmd, m_param);
Drop m_cmd and just write I2C_READ in the function call.
> + if (mode == SE_DMA) {
> + ret = geni_se_rx_dma_prep(gi2c->wrapper_dev,
> + gi2c->base, msgs[i].buf,
> + msgs[i].len, &rx_dma);
> + if (ret) {
> + mode = FIFO_MODE;
> + ret = geni_se_select_mode(gi2c->base,
> + mode);
> + }
> + }
> + } else {
> + dev_dbg(gi2c->dev,
> + "WRITE:n:%d,i:%d len:%d, stretch:%d, m_param:0x%x\n",
> + num, i, msgs[i].len, stretch, m_param);
> + geni_write_reg(msgs[i].len, gi2c->base,
> + SE_I2C_TX_TRANS_LEN);
> + m_cmd = I2C_WRITE;
> + geni_se_setup_m_cmd(gi2c->base, m_cmd, m_param);
> + if (mode == SE_DMA) {
> + ret = geni_se_tx_dma_prep(gi2c->wrapper_dev,
> + gi2c->base, msgs[i].buf,
> + msgs[i].len, &tx_dma);
> + if (ret) {
> + mode = FIFO_MODE;
> + ret = geni_se_select_mode(gi2c->base,
> + mode);
> + }
> + }
> + if (mode == FIFO_MODE) /* Get FIFO IRQ */
> + geni_write_reg(1, gi2c->base,
> + SE_GENI_TX_WATERMARK_REG);
> + }
> + /* Ensure FIFO write go through before waiting for Done evet */
That's not what mb does, drop it.
> + mb();
> + timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
As written you should just use "ret", but the return value of
wait_for_completion_timeout() is unsigned long, so change the type of
timeout instead.
> + if (!timeout) {
> + geni_i2c_err(gi2c, GENI_TIMEOUT);
> + gi2c->cur = NULL;
> + geni_se_abort_m_cmd(gi2c->base);
> + timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
What if it takes a fraction above HZ time to complete the previous
operation, then you might end up here with the completion completed, but
from the wrong operation.
> + }
> + gi2c->cur_wr = 0;
> + gi2c->cur_rd = 0;
> + if (mode == SE_DMA) {
> + if (gi2c->err) {
> + if (msgs[i].flags != I2C_M_RD)
> + writel_relaxed(1, gi2c->base +
> + SE_DMA_TX_FSM_RST);
> + else
> + writel_relaxed(1, gi2c->base +
> + SE_DMA_RX_FSM_RST);
> + wait_for_completion_timeout(&gi2c->xfer, HZ);
> + }
> + geni_se_rx_dma_unprep(gi2c->wrapper_dev, rx_dma,
> + msgs[i].len);
Reduce the length of gi2c->wrapper_dev here by using a local variable,
so that you get below (or close to) 80 chars.
Also, in either rx or tx cases above I see only that you prep one of
thse, and then you're unprepping both.
> + geni_se_tx_dma_unprep(gi2c->wrapper_dev, tx_dma,
> + msgs[i].len);
> + }
> + ret = gi2c->err;
> + if (gi2c->err) {
> + dev_err(gi2c->dev, "i2c error :%d\n", gi2c->err);
> + break;
> + }
> + }
> + if (ret == 0)
> + ret = num;
> +
> + pm_runtime_mark_last_busy(gi2c->dev);
> + pm_runtime_put_autosuspend(gi2c->dev);
> + gi2c->cur = NULL;
> + gi2c->err = 0;
> + dev_dbg(gi2c->dev, "i2c txn ret:%d\n", ret);
> + return ret;
> +}
[..]
> +static int geni_i2c_probe(struct platform_device *pdev)
> +{
> + struct geni_i2c_dev *gi2c;
> + struct resource *res;
> + int ret;
> +
> + gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL);
> + if (!gi2c)
> + return -ENOMEM;
> +
> + gi2c->dev = &pdev->dev;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res)
> + return -EINVAL;
Put this right before devm_ioremap_resource() and drop the error check.
> +
> + gi2c->wrapper_dev = pdev->dev.parent;
> + gi2c->i2c_rsc.wrapper_dev = pdev->dev.parent;
As suggested in the other patch, if you have an helper function in the
wrapper driver that initializes the i2c_rsc then this could fill out the
actual wrapper, instead of just the device.
> + gi2c->i2c_rsc.se_clk = devm_clk_get(&pdev->dev, "se-clk");
> + if (IS_ERR(gi2c->i2c_rsc.se_clk)) {
> + ret = PTR_ERR(gi2c->i2c_rsc.se_clk);
> + dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
> + return ret;
> + }
> +
> + gi2c->base = devm_ioremap_resource(gi2c->dev, res);
> + if (IS_ERR(gi2c->base))
> + return PTR_ERR(gi2c->base);
> +
> + gi2c->i2c_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);
Drop all the pinctrl stuff. You might still want to call
pinctrl_pm_select_{idle,default,sleep}_state(), in the various stages of
PM state though.
> + if (IS_ERR_OR_NULL(gi2c->i2c_rsc.geni_pinctrl)) {
> + dev_err(&pdev->dev, "No pinctrl config specified\n");
> + ret = PTR_ERR(gi2c->i2c_rsc.geni_pinctrl);
> + return ret;
> + }
[..]
> +static int geni_i2c_runtime_resume(struct device *dev)
> +{
> + int ret;
> + struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
> +
> + ret = geni_se_resources_on(&gi2c->i2c_rsc);
> + if (ret)
> + return ret;
> +
> + if (!unlikely(gi2c->tx_wm)) {
Drop the unlikely()
> + int proto = geni_se_get_proto(gi2c->base);
> + int gi2c_tx_depth = geni_se_get_tx_fifo_depth(gi2c->base);
> +
> + if (unlikely(proto != I2C)) {
Has this changes since probe? Can't we verify that the proto is correct
during probe and then just trust that the hardware doesn't change
function?
> + dev_err(gi2c->dev, "Invalid proto %d\n", proto);
> + geni_se_resources_off(&gi2c->i2c_rsc);
> + return -ENXIO;
> + }
> +
> + gi2c->tx_wm = gi2c_tx_depth - 1;
> + geni_se_init(gi2c->base, gi2c->tx_wm, gi2c_tx_depth);
> + geni_se_config_packing(gi2c->base, 8, 4, true);
> + dev_dbg(gi2c->dev, "i2c fifo/se-dma mode. fifo depth:%d\n",
> + gi2c_tx_depth);
> + }
> + enable_irq(gi2c->irq);
> +
> + return 0;
> +}
[..]
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:i2c_geni");
What will reference the kernel module by this alias?
> --
> Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox