[PATCH 1/1] uio: uio_fsl_elbc_gpcm: new driver
Scott Wood
scottwood at freescale.com
Wed Dec 10 07:37:58 AEDT 2014
[Trimmed excessive CC list]
On Tue, 2014-12-09 at 17:43 +0100, John Ogness wrote:
> This driver provides UIO access to memory of a peripheral connected
> to the Freescale enhanced local bus controller (eLBC) interface
> using the general purpose chip-select mode (GPCM).
>
> Signed-off-by: John Ogness <john.ogness at linutronix.de>
> ---
> There are currently drivers that use FCM and UPM modes. But there
> are no drivers using the very simple GPCM mode.
Yes, there are -- the NOR flash driver, various board FPGAs, etc.
> If other drivers from other subsystems should start using this mode
> (for example, mtd, eeprom, char) then it may make sense to try to
> implement generic GPCM support in arch/powerpc/sysdev/fsl_lbc.c.
It's already there, though all that's needed is error handling. Setting
up the chipselects is handled in U-Boot (same as with FCM -- and
probably UPM, though I'm less familiar with that).
> diff --git a/Documentation/devicetree/bindings/powerpc/fsl/lbc.txt b/Documentation/devicetree/bindings/powerpc/fsl/lbc.txt
> index 3300fec..1c80fce 100644
> --- a/Documentation/devicetree/bindings/powerpc/fsl/lbc.txt
> +++ b/Documentation/devicetree/bindings/powerpc/fsl/lbc.txt
> @@ -16,20 +16,28 @@ Example:
> "fsl,pq2-localbus";
> #address-cells = <2>;
> #size-cells = <1>;
> - reg = <f0010100 40>;
> + reg = <0xf0010100 0x40>;
>
> - ranges = <0 0 fe000000 02000000
> - 1 0 f4500000 00008000>;
> + ranges = <0x0 0x0 0xfe000000 0x02000000
> + 0x1 0x0 0xf4500000 0x00008000
> + 0x2 0x0 0xfd810000 0x00010000>;
>
> flash at 0,0 {
> compatible = "jedec-flash";
> - reg = <0 0 2000000>;
> + reg = <0x0 0x0 0x2000000>;
> bank-width = <4>;
> device-width = <1>;
> };
>
> board-control at 1,0 {
> - reg = <1 0 20>;
> + reg = <0x1 0x0 0x20>;
> compatible = "fsl,mpc8272ads-bcsr";
> };
> +
Ideally this sort of cleanup change would be a separate patch.
> + simple-periph at 2,0 {
> + compatible = "fsl,elbc-gpcm-uio";
> + reg = <0x2 0x0 0x10000>;
> + elbc-gpcm-br = <0xfd810800>;
> + elbc-gpcm-or = <0xffff09f7>;
> + };
This is not an acceptable compatible string. Compatible should describe
what the hardware is, not how you plan to use it in Linux.
I also see no description of elbc-gpcm-br/or, and in general putting
register values in the device tree is not a good idea. As with
compatible, the device tree should describe the hardware, not how to
configure it.
> };
> diff --git a/arch/powerpc/include/asm/fsl_lbc.h b/arch/powerpc/include/asm/fsl_lbc.h
> index 067fb0d..c7240a0 100644
> --- a/arch/powerpc/include/asm/fsl_lbc.h
> +++ b/arch/powerpc/include/asm/fsl_lbc.h
> @@ -95,6 +95,9 @@ struct fsl_lbc_bank {
> #define OR_FCM_TRLX_SHIFT 2
> #define OR_FCM_EHTR 0x00000002
> #define OR_FCM_EHTR_SHIFT 1
> +
> +#define OR_GPCM_AM 0xFFFF8000
> +#define OR_GPCM_AM_SHIFT 15
> };
Please maintain a consistent whitespace style with what's already there.
> diff --git a/drivers/uio/uio_fsl_elbc_gpcm.c b/drivers/uio/uio_fsl_elbc_gpcm.c
> new file mode 100644
> index 0000000..b6cac91
> --- /dev/null
> +++ b/drivers/uio/uio_fsl_elbc_gpcm.c
> @@ -0,0 +1,499 @@
> +/* uio_fsl_elbc_gpcm: UIO driver for eLBC/GPCM peripherals
> +
> + Copyright (C) 2014 Linutronix GmbH
> + Author: John Ogness <john.ogness at linutronix.de>
> +
> + This driver provides UIO access to memory of a peripheral connected
> + to the Freescale enhanced local bus controller (eLBC) interface
> + using the general purpose chip-select mode (GPCM).
> +
> + Here is an example of the device tree entries:
> +
> + localbus at ffe05000 {
> + ranges = <0x2 0x0 0x0 0xff810000 0x10000>;
> +
> + dpm at 2,0 {
> + compatible = "fsl,elbc-gpcm-uio";
> + reg = <0x2 0x0 0x10000>;
> + elbc-gpcm-br = <0xff810800>;
> + elbc-gpcm-or = <0xffff09f7>;
> + interrupt-parent = <&mpic>;
> + interrupts = <4 1>;
> + device_type = "netx5152";
> + uio_name = "netx_custom";
> + netx5152,init-win0-offset = <0x0>;
> + };
> + };
What is "uio_name"? Again, not hardware description.
Where is the binding for this device? What is
netx5152,init-win0-offset? Documentations go in
Documentation/devicetree/bindings, not code comments.
Don't use device_type.
> +#ifdef CONFIG_UIO_FSL_ELBC_GPCM_NETX5152
> +#define DPM_HOST_WIN0_OFFSET 0xff00
> +#define DPM_HOST_INT_STAT0 0xe0
> +#define DPM_HOST_INT_EN0 0xf0
> +#define DPM_HOST_INT_MASK 0xe600ffff
> +#define DPM_HOST_INT_GLOBAL_EN 0x80000000
What's this doing in the "generic" file?
> +static void netx5152_shutdown(struct uio_info *info, bool init_err)
> +{
> + if (init_err)
> + return;
> +
> + /* disable interrupts */
> + iowrite32(0, info->mem[0].internal_addr + DPM_HOST_WIN0_OFFSET +
> + DPM_HOST_INT_EN0);
> +}
Why would you ever call this function with init_err = true? What is it
supposed to do? When you introduce a callback interface you should
document the semantics that are expected of an implementation,
especially if there are details that aren't clear from existing
examples.
> + /* get current BR/OR values */
> + reg_br_cur = in_be32(&priv->lbc->bank[priv->bank].br);
> + reg_or_cur = in_be32(&priv->lbc->bank[priv->bank].or);
> +
> + /* if bank already configured, make sure it matches */
> + if ((reg_br_cur & BR_V)) {
> + if ((reg_br_cur & BR_MSEL) != BR_MS_GPCM ||
> + (reg_br_cur & reg_or_cur & BR_BA)
> + != fsl_lbc_addr(res.start)) {
> + dev_err(priv->dev,
> + "bank in use by another peripheral\n");
> + ret = -ENODEV;
> + goto out_err1;
> + }
> +
> + /* warn if behavior settings changing */
> + if ((reg_br_cur & ~(BR_BA | BR_V)) !=
> + (reg_br_new & ~(BR_BA | BR_V))) {
> + dev_warn(priv->dev,
> + "modifying BR settings: 0x%08x -> 0x%08x",
> + reg_br_cur, reg_br_new);
> + }
> + if ((reg_or_cur & ~OR_GPCM_AM) != (reg_or_new & ~OR_GPCM_AM)) {
> + dev_warn(priv->dev,
> + "modifying OR settings: 0x%08x -> 0x%08x",
> + reg_or_cur, reg_or_new);
> + }
> + }
Why is a wrong value for MSEL in pre-existing BRn a fatal error but
wrong address/size isn't?
> + info->version = "0.0.1";
What does this version mean? When will it be updated?
> + if (irq != NO_IRQ) {
> + if (priv->irq_handler) {
> + info->irq = irq;
> + info->irq_flags = IRQF_SHARED;
> + info->handler = priv->irq_handler;
> + } else {
> + irq = NO_IRQ;
> + dev_warn(priv->dev, "ignoring irq, no handler\n");
> + }
> + }
Don't use NO_IRQ. A value of zero indicates the absence of an
interrupt.
> + if (priv->init)
> + priv->init(info);
> +
> + /* register UIO device */
> + if (uio_register_device(priv->dev, info) != 0) {
> + dev_err(priv->dev, "UIO registration failed\n");
> + ret = -ENODEV;
> + goto out_err2;
> + }
> +
> + /* store private data */
> + platform_set_drvdata(pdev, info);
Shouldn't you set drvdata before registering?
> +static struct platform_driver uio_fsl_elbc_gpcm_driver = {
> + .driver = {
> + .name = "fsl,elbc-gpcm-uio",
> + .owner = THIS_MODULE,
> + .of_match_table = uio_fsl_elbc_gpcm_match,
> + },
Setting .owner in platform_driver is no longer necessary, and there was
recently a big patchset to remove them.
> +static int __init uio_fsl_elbc_gpcm_init(void)
> +{
> + return platform_driver_register(&uio_fsl_elbc_gpcm_driver);
> +}
> +
> +static void __exit uio_fsl_elbc_gpcm_exit(void)
> +{
> + platform_driver_unregister(&uio_fsl_elbc_gpcm_driver);
> +}
> +
> +module_init(uio_fsl_elbc_gpcm_init);
> +module_exit(uio_fsl_elbc_gpcm_exit);
module_platform_driver()
-Scott
More information about the Linuxppc-dev
mailing list