* [PATCH v2 1/2] serial: 8250: Add flag so drivers can avoid THRE probe
From: Joel Stanley @ 2017-04-05 4:03 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Mark Rutland, Rob Herring
Cc: linux-serial-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Andy Shevchenko,
Benjamin Herrenschmidt, Jeremy Kerr,
openbmc-uLR06cmDAlY/bJ5BZ2RsiQ
In-Reply-To: <20170405040352.5661-1-joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>
The probing of THRE irq behaviour assumes the other end will be reading
bytes out of the buffer in order to probe the port at driver init. In
some cases the other end cannot be relied upon to read these bytes, so
provide a flag for them to skip this step.
Bit 16 was chosen as the flags are a int and the top bits are taken.
Acked-by: Benjamin Herrenschmidt <benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r@public.gmane.org>
Signed-off-by: Joel Stanley <joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>
---
drivers/tty/serial/8250/8250_port.c | 2 +-
include/linux/serial_core.h | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 6119516ef5fc..60a6c247340f 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -2229,7 +2229,7 @@ int serial8250_do_startup(struct uart_port *port)
}
}
- if (port->irq) {
+ if (port->irq && !(up->port.flags & UPF_NO_THRE_TEST)) {
unsigned char iir1;
/*
* Test for UARTs that do not reassert THRE when the
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 58484fb35cc8..260245deec94 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -195,6 +195,7 @@ struct uart_port {
#define UPF_NO_TXEN_TEST ((__force upf_t) (1 << 15))
#define UPF_MAGIC_MULTIPLIER ((__force upf_t) ASYNC_MAGIC_MULTIPLIER /* 16 */ )
+#define UPF_NO_THRE_TEST ((__force upf_t) (1 << 19))
/* Port has hardware-assisted h/w flow control */
#define UPF_AUTO_CTS ((__force upf_t) (1 << 20))
#define UPF_AUTO_RTS ((__force upf_t) (1 << 21))
--
2.11.0
--
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 related
* [PATCH v2 0/2] drivers: serial: Aspeed VUART driver
From: Joel Stanley @ 2017-04-05 4:03 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Mark Rutland, Rob Herring
Cc: linux-serial, linux-kernel, devicetree, openbmc, Andy Shevchenko,
Benjamin Herrenschmidt, Jeremy Kerr
This is v2 of a driver for the Aspeed VUART. This version addresses feedback
from Andy and Greg, and adds Rob's ack for the bindings change.
The VUART is a serial device on the BMC side of the LPC bus that connects a BMC
to it's host processor.
We add a flag to the serial core to allow the driver to skip probing of the
THRE irq behaviour, which could hang due to the host not reading bytes out of
the buffer.
We've been using this on systems for over a year, so it has seen a good amount
of testing.
Cheers,
Joel
Jeremy Kerr (1):
drivers/serial: Add driver for Aspeed virtual UART
Joel Stanley (1):
serial: 8250: Add flag so drivers can avoid THRE probe
Documentation/ABI/stable/sysfs-driver-aspeed-vuart | 15 +
Documentation/devicetree/bindings/serial/8250.txt | 2 +
drivers/tty/serial/8250/8250_port.c | 2 +-
drivers/tty/serial/8250/Kconfig | 10 +
drivers/tty/serial/8250/Makefile | 1 +
drivers/tty/serial/8250/aspeed-vuart.c | 341 +++++++++++++++++++++
include/linux/serial_core.h | 1 +
7 files changed, 371 insertions(+), 1 deletion(-)
create mode 100644 Documentation/ABI/stable/sysfs-driver-aspeed-vuart
create mode 100644 drivers/tty/serial/8250/aspeed-vuart.c
--
2.11.0
^ permalink raw reply
* Re: [linux-sunxi] [PATCH 03/11] dt-bindings: add device tree binding for X-Powers AXP803 PMIC
From: Chen-Yu Tsai @ 2017-04-05 3:52 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Lee Jones, Rob Herring, Chen-Yu Tsai, Maxime Ripard,
Liam Girdwood, devicetree, linux-sunxi, linux-kernel,
linux-arm-kernel
In-Reply-To: <20170404180145.12897-4-icenowy-h8G6r0blFSE@public.gmane.org>
On Wed, Apr 5, 2017 at 2:01 AM, Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org> wrote:
> AXP803 is a PMIC produced by Shenzhen X-Powers, with either I2C or RSB
> bus.
>
> Add a compatible for it.
>
> Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
> ---
> Documentation/devicetree/bindings/mfd/axp20x.txt | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt
> index b41d2601c6ba..31607631b0d8 100644
> --- a/Documentation/devicetree/bindings/mfd/axp20x.txt
> +++ b/Documentation/devicetree/bindings/mfd/axp20x.txt
> @@ -7,11 +7,12 @@ axp209 (X-Powers)
> axp221 (X-Powers)
> axp223 (X-Powers)
> axp809 (X-Powers)
> +axp803 (X-Powers)
>
> Required properties:
> - compatible: "x-powers,axp152", "x-powers,axp202", "x-powers,axp209",
> "x-powers,axp221", "x-powers,axp223", "x-powers,axp806",
> - "x-powers,axp809"
> + "x-powers,axp809", "x-powers,axp803"
Please sort them in ascending order. Otherwise,
Acked-by: Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org>
> - reg: The I2C slave address or RSB hardware address for the AXP chip
> - interrupt-parent: The parent interrupt controller
> - interrupts: SoC NMI / GPIO interrupt connected to the PMIC's IRQ pin
> --
> 2.12.2
>
> --
> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
> For more options, visit https://groups.google.com/d/optout.
--
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 02/11] arm64: allwinner: a64: add NMI controller on A64
From: Chen-Yu Tsai @ 2017-04-05 3:51 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Lee Jones, Rob Herring, Chen-Yu Tsai, Maxime Ripard,
Liam Girdwood, devicetree, linux-sunxi, linux-kernel,
linux-arm-kernel
In-Reply-To: <20170404180145.12897-3-icenowy-h8G6r0blFSE@public.gmane.org>
On Wed, Apr 5, 2017 at 2:01 AM, Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org> wrote:
> Allwinner A64 SoC features a NMI controller, which is usually connected
> to the AXP PMIC.
>
> Add support for it.
>
> Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
This might not be the best representation of the R_INTC block. Though
we'd need to change it for all SoCs if we want to be accurate. For now,
Acked-by: Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org>
^ permalink raw reply
* Re: [PATCH 01/11] arm64: allwinner: a64: enable RSB on A64
From: Chen-Yu Tsai @ 2017-04-05 3:48 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Lee Jones, Rob Herring, Chen-Yu Tsai, Maxime Ripard,
Liam Girdwood, devicetree, linux-sunxi, linux-kernel,
linux-arm-kernel
In-Reply-To: <20170404180145.12897-2-icenowy-h8G6r0blFSE@public.gmane.org>
On Wed, Apr 5, 2017 at 2:01 AM, Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org> wrote:
> Allwinner A64 have a RSB controller like the one on A23/A33 SoCs.
>
> Add it and its pinmux.
>
> Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
> ---
> arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 21 +++++++++++++++++++++
> 1 file changed, 21 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
> index 6bc606b4d74d..9a75b1c7c91a 100644
> --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
> +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
> @@ -420,6 +420,27 @@
> #gpio-cells = <3>;
> interrupt-controller;
> #interrupt-cells = <3>;
> +
> + r_rsb_pins: rsb@0 {
> + pins = "PL0", "PL1";
> + function = "s_rsb";
> + drive-strength = <20>;
We don't need this. The hardware default should be enough.
> + bias-pull-up;
Boards should have external pull-ups for these pins, as the PMICs
start up in I2C mode. If any board actually doesn't have them and
needs the internal pull-ups, they should be set at the board level.
Otherwise,
Acked-by: Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org>
> + };
> + };
> +
> + r_rsb: rsb@1f03400 {
> + compatible = "allwinner,sun8i-a23-rsb";
> + reg = <0x01f03400 0x400>;
> + interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&r_ccu 6>;
> + clock-frequency = <3000000>;
> + resets = <&r_ccu 2>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&r_rsb_pins>;
> + status = "disabled";
> + #address-cells = <1>;
> + #size-cells = <0>;
> };
> };
> };
> --
> 2.12.2
>
> --
> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
> For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* Re: [PATCH V1 1/1] mtd: mtk-nor: set controller to 4B mode with large capacity flash
From: Guochun Mao @ 2017-04-05 3:34 UTC (permalink / raw)
To: Cyrille Pitchen
Cc: Marek Vasut, Boris Brezillon, Mark Rutland,
devicetree-u79uwXL29TY76Z2rM5mHXA, Richard Weinberger,
Russell King, linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Matthias Brugger,
linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Cyrille Pitchen,
David Woodhouse,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <873a3dac-7d3a-0723-4c7d-c786d3398278-yU5RGvR974pGWvitb5QawA@public.gmane.org>
Hi Cyrille,
Thank you for so detailed explanation.
I'll check nor->addr_width in both mt8173_nor_read() and
mt8173_nor_write().
Best regards,
Guochun
On Fri, 2017-03-31 at 10:56 +0200, Cyrille Pitchen wrote:
> Le 31/03/2017 à 04:26, Guochun Mao a écrit :
> > Hi Cyrille, Marek,
> >
> > Thanks for your suggestions.
> >
> > On Thu, 2017-03-30 at 19:40 +0200, Cyrille Pitchen wrote:
> >> Hi Guochun,
> >>
> >> Le 30/03/2017 à 10:23, Guochun Mao a écrit :
> >>> when nor's size larger than 16MByte, nor and controller should
> >>> enter 4Byte mode simultaneously.
> >>>
> >>> Signed-off-by: Guochun Mao <guochun.mao-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> >>> ---
> >>> drivers/mtd/spi-nor/mtk-quadspi.c | 7 +++++++
> >>> 1 file changed, 7 insertions(+)
> >>>
> >>> diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c
> >>> index e661877..05cd8a8 100644
> >>> --- a/drivers/mtd/spi-nor/mtk-quadspi.c
> >>> +++ b/drivers/mtd/spi-nor/mtk-quadspi.c
> >>> @@ -369,6 +369,13 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
> >>> /* We only handle 1 byte */
> >>> ret = mt8173_nor_wr_sr(mt8173_nor, *buf);
> >>> break;
> >>> + case SPINOR_OP_EN4B:
> >>> + /* Set nor controller to 4-byte address mode,
> >>> + * and simultaneously set nor flash.
> >>> + * This case should cooperate with default operation.
> >>> + */
> >>> + writeb(readb(mt8173_nor->base + MTK_NOR_DUAL_REG) | 0x10,
> >>> + mt8173_nor->base + MTK_NOR_DUAL_REG);
> >>
> >> This is not good: you should check in both mt8173_nor_read() and
> >> mt8173_nor_write() whether nor->addr_width is either 3 or 4.
> >>
> >> from include/linux/mtd/spi-nor.h:
> >> * @addr_width: number of address bytes
> >>
> >> Besides SPI commands using an op code from 4-byte address instruction
> >> set always carry a 4-byte address. They can be used directly, without
> >> sending the SPINOR_OP_EN4B before. So you cannot assume that addresses
> >> will be 4-byte long only if your SPI controller driver has seen a
> >> SPINOR_OP_EN4B command before. This assumption is wrong.
> > Nor->addr_width is assigned in spi_nor_scan in spi-nor.c, and it's not
> > modified in later process.
> > Does it means that we will not switch nor between 3Byte address and
> > 4Byte?
> > So, is it better to check nor->addr_width when do nor initialization?
> >
>
> Currently yes, nor->addr_width, nor->read_opcode, nor->read_dummy, and
> nor->program_opcode are set once for all in spi_nor_scan().
>
> However, nor->read() is likely to be called soon from spi_nor_scan()
> with values of nor->addr_width, nor->read_opcode and nor->read_dummy
> different from those selected when exiting spi_nor_scan().
>
> So nor->read() / mt8173_nor_read should check nor->addr_width,
> nor->read_opcode and nor->read_dummy at each call.
>
> More precisely, I plan to use nor->read() from spi_nor_scan() to read
> SFDP (Serial Flash Discoverable Parameters) data.
>
> Whatever op code, numbers of address bytes and dummy cycles used for
> (Fast) Read commands, the Read SFDP command uses fixed settings
> standardized for all manufacturers and all memory parts:
> - op code: 5Ah
> - number of bytes for the address: 3 (even for memory > 128Mbits)
> - number of dummy clock cycles: 8 clocks
>
> https://patchwork.ozlabs.org/patch/742380/
>
> Please have a look at the spi_nor_read_sfdp().
>
> spi_nor_read_sfdp() is likely to be called from and only from
> spi_nor_scan().
>
> Best regards,
>
> Cyrille
>
> >>
> >> SPI controller driver should never check SPINOR_OP_* op codes like this.
> > I agree that SPI controller driver should not check SPINOR_OP_* op codes
> > like what I do.
> > I will correct it next version.
> >
> > Best Regards,
> > Guochun
> >>
> >> Then, testing SPINOR_OP_RDSR from mt8173_nor_read_reg() or
> >> SPINOR_OP_WRSR from mt8173_nor_write_reg() is not a good practice too:
> >> op codes may change depending on the memory manufacturer. So testing op
> >> code values like you do can work with some memories but maybe not all.
> >>
> >> Finally, don't use 0x10, please define a macro instead.
> >>
> >> Best regards,
> >>
> >> Cyrille
> >>
> >>> default:
> >>> ret = mt8173_nor_do_tx_rx(mt8173_nor, opcode, buf, len, NULL, 0);
> >>> if (ret)
> >>>
> >>
> >
> >
> >
>
--
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 2/2] ARM: dts: imx7: add USDHC NAND clock to SDHC instances
From: Stefan Agner @ 2017-04-05 2:36 UTC (permalink / raw)
To: Fabio Estevam
Cc: Dong Aisheng, Shawn Guo, Sascha Hauer, Stephen Boyd, Dong Aisheng,
Fabio Estevam, robh+dt-DgEjT+Ai2ygdnm+yROfE0A, Mark Rutland,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-clk-u79uwXL29TY76Z2rM5mHXA, linux-kernel
In-Reply-To: <CAOMZO5A7QQEe5rgBbJBzo8GY41aYV9WA8e9teCUROyihg0kq_g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 2017-04-04 19:15, Fabio Estevam wrote:
> On Sun, Apr 2, 2017 at 2:02 PM, Fabio Estevam <festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>> On Sat, Apr 1, 2017 at 1:15 AM, Stefan Agner <stefan-XLVq0VzYD2Y@public.gmane.org> wrote:
>>
>>> IMX7D_IPG_ROOT_CLK is currently not a valid clock in upstream... So we
>>> would have to add it to the clock driver first.
>>>
>>> I guess we could/should add it anyway at one point? But probably also as
>>> init on, just to make sure Linux does not disable it since it is
>>> currently used by several IPs implicitly.
>>
>> Yes, I made a previous attempt do add IMX7D_IPG_ROOT_CLK and it did
>> not work as I did not put it in the init_on clock list.
>>
>> Will submit a new patch adding it to init_on, thanks.
>
> I thought that adding IMX7D_IPG_ROOT_CLK would do the trick, but the
> patch below also causes the kernel to not boot:
>
> --- a/drivers/clk/imx/clk-imx7d.c
> +++ b/drivers/clk/imx/clk-imx7d.c
> @@ -386,7 +386,7 @@ static int const clks_init_on[] __initconst = {
> IMX7D_PLL_SYS_MAIN_480M_CLK, IMX7D_NAND_USDHC_BUS_ROOT_CLK,
> IMX7D_DRAM_PHYM_ROOT_CLK, IMX7D_DRAM_ROOT_CLK,
> IMX7D_DRAM_PHYM_ALT_ROOT_CLK, IMX7D_DRAM_ALT_ROOT_CLK,
> - IMX7D_AHB_CHANNEL_ROOT_CLK,
> + IMX7D_AHB_CHANNEL_ROOT_CLK, IMX7D_IPG_ROOT_CLK,
> };
>
> static struct clk_onecell_data clk_data;
> @@ -788,7 +788,7 @@ static void __init imx7d_clocks_init(struct
> device_node *ccm_node)
> clks[IMX7D_WRCLK_ROOT_DIV] =
> imx_clk_divider2("wrclk_post_div", "wrclk_pre_div", base + 0xbd00, 0,
> 6);
> clks[IMX7D_CLKO1_ROOT_DIV] =
> imx_clk_divider2("clko1_post_div", "clko1_pre_div", base + 0xbd80, 0,
> 6);
> clks[IMX7D_CLKO2_ROOT_DIV] =
> imx_clk_divider2("clko2_post_div", "clko2_pre_div", base + 0xbe00, 0,
> 6);
> -
> + clks[IMX7D_IPG_ROOT_CLK] = imx_clk_divider2("ipg_root_clk",
> "ahb_root_clk", base + 0x9080, 0, 2);
> clks[IMX7D_ARM_A7_ROOT_CLK] = imx_clk_gate4("arm_a7_root_clk",
> "arm_a7_div", base + 0x4000, 0);
> clks[IMX7D_ARM_M4_ROOT_CLK] = imx_clk_gate4("arm_m4_root_clk",
> "arm_m4_div", base + 0x4010, 0);
> clks[IMX7D_ARM_M0_ROOT_CLK] = imx_clk_gate4("arm_m0_root_clk",
> "arm_m0_div", base + 0x4020, 0);
Hm, imx_clk_divider2 sets CLK_SET_RATE_PARENT, maybe that influences the
parent?
I guess we actually don't want the clock framework to change that clock
rate, not sure whether we can freeze it or similar.
--
Stefan
--
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 v5] of/pci: Fix memory leak in of_pci_get_host_bridge_resources
From: Jeffy Chen @ 2017-04-05 2:28 UTC (permalink / raw)
To: linux-pci
Cc: robh, toshi.kani, shawn.lin, briannorris, linux-kernel, dianders,
bhelgaas, dtor, Jeffy Chen, devicetree, Rob Herring, Frank Rowand
In-Reply-To: <1491359303-1385-1-git-send-email-jeffy.chen@rock-chips.com>
Currently we only free the allocated resource struct when error.
This would cause memory leak after pci_free_resource_list.
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
Changes in v5:
Fix some careless compile errors.
Changes in v4:
Use IORESOURCE_AUTO to mark struct resources which could be copied.
Changes in v3:
Add some comments.
Changes in v2:
Don't change the resource_list_create_entry's behavior.
drivers/of/of_pci.c | 51 ++++++++++++++++++---------------------------------
drivers/pci/bus.c | 7 ++++++-
2 files changed, 24 insertions(+), 34 deletions(-)
diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c
index 0ee42c3..49233bf 100644
--- a/drivers/of/of_pci.c
+++ b/drivers/of/of_pci.c
@@ -189,9 +189,7 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
unsigned char busno, unsigned char bus_max,
struct list_head *resources, resource_size_t *io_base)
{
- struct resource_entry *window;
- struct resource *res;
- struct resource *bus_range;
+ struct resource res;
struct of_pci_range range;
struct of_pci_range_parser parser;
char range_type[4];
@@ -200,24 +198,21 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
if (io_base)
*io_base = (resource_size_t)OF_BAD_ADDR;
- bus_range = kzalloc(sizeof(*bus_range), GFP_KERNEL);
- if (!bus_range)
- return -ENOMEM;
-
pr_info("host bridge %s ranges:\n", dev->full_name);
- err = of_pci_parse_bus_range(dev, bus_range);
+ err = of_pci_parse_bus_range(dev, &res);
if (err) {
- bus_range->start = busno;
- bus_range->end = bus_max;
- bus_range->flags = IORESOURCE_BUS;
- pr_info(" No bus range found for %s, using %pR\n",
- dev->full_name, bus_range);
+ res.start = busno;
+ res.end = bus_max;
+ res.flags = IORESOURCE_BUS;
+ pr_info(" No bus range found for %s\n", dev->full_name);
} else {
- if (bus_range->end > bus_range->start + bus_max)
- bus_range->end = bus_range->start + bus_max;
+ if (res.end > res.start + bus_max)
+ res.end = res.start + bus_max;
}
- pci_add_resource(resources, bus_range);
+
+ res.flags |= IORESOURCE_AUTO;
+ pci_add_resource(resources, &res);
/* Check for ranges property */
err = of_pci_range_parser_init(&parser, dev);
@@ -244,24 +239,16 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
if (range.cpu_addr == OF_BAD_ADDR || range.size == 0)
continue;
- res = kzalloc(sizeof(struct resource), GFP_KERNEL);
- if (!res) {
- err = -ENOMEM;
- goto parse_failed;
- }
-
- err = of_pci_range_to_resource(&range, dev, res);
- if (err) {
- kfree(res);
+ err = of_pci_range_to_resource(&range, dev, &res);
+ if (err)
continue;
- }
- if (resource_type(res) == IORESOURCE_IO) {
+ if (resource_type(&res) == IORESOURCE_IO) {
if (!io_base) {
pr_err("I/O range found for %s. Please provide an io_base pointer to save CPU base address\n",
dev->full_name);
err = -EINVAL;
- goto conversion_failed;
+ goto parse_failed;
}
if (*io_base != (resource_size_t)OF_BAD_ADDR)
pr_warn("More than one I/O resource converted for %s. CPU base address for old range lost!\n",
@@ -269,16 +256,14 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
*io_base = range.cpu_addr;
}
- pci_add_resource_offset(resources, res, res->start - range.pci_addr);
+ res.flags |= IORESOURCE_AUTO;
+ pci_add_resource_offset(resources, &res,
+ res.start - range.pci_addr);
}
return 0;
-conversion_failed:
- kfree(res);
parse_failed:
- resource_list_for_each_entry(window, resources)
- kfree(window->res);
pci_free_resource_list(resources);
return err;
}
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index bc56cf1..3dbcb95 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -22,12 +22,17 @@ void pci_add_resource_offset(struct list_head *resources, struct resource *res,
{
struct resource_entry *entry;
- entry = resource_list_create_entry(res, 0);
+ entry = resource_list_create_entry(NULL, 0);
if (!entry) {
printk(KERN_ERR "PCI: can't add host bridge window %pR\n", res);
return;
}
+ if (res->flags & IORESOURCE_AUTO)
+ *entry->res = *res;
+ else
+ entry->res = res;
+
entry->offset = offset;
resource_list_add_tail(entry, resources);
}
--
2.1.4
^ permalink raw reply related
* [PATCH v5 0/1] Fix memory leak in of_pci_get_host_bridge_resources
From: Jeffy Chen @ 2017-04-05 2:28 UTC (permalink / raw)
To: linux-pci
Cc: robh, toshi.kani, shawn.lin, briannorris, linux-kernel, dianders,
bhelgaas, dtor, Jeffy Chen, devicetree, Rob Herring, Frank Rowand
In of_pci_get_host_bridge_resources, we alloced some struct resource
variables, and they would cause memory leak since no where to free them.
Changes in v5:
Fix some careless compile errors.
Changes in v4:
Use IORESOURCE_AUTO to mark struct resources which could be copied.
Changes in v3:
Add some comments.
Changes in v2:
Don't change the resource_list_create_entry's behavior.
Jeffy Chen (1):
of/pci: Fix memory leak in of_pci_get_host_bridge_resources
drivers/of/of_pci.c | 51 ++++++++++++++++++---------------------------------
drivers/pci/bus.c | 7 ++++++-
2 files changed, 24 insertions(+), 34 deletions(-)
--
2.1.4
^ permalink raw reply
* Re: Re: [PATCH v3 04/11] drm/sun4i: abstract the layer type
From: Chen-Yu Tsai @ 2017-04-05 2:27 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Sean Paul, Rob Herring, Maxime Ripard, Chen-Yu Tsai,
Jernej Skrabec, devicetree, linux-kernel, dri-devel, linux-sunxi,
linux-clk, linux-arm-kernel
In-Reply-To: <dbf0f478-ff53-0ced-24a3-e9de7077efd9-h8G6r0blFSE@public.gmane.org>
On Wed, Apr 5, 2017 at 3:53 AM, Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org> wrote:
>
>
> 在 2017年04月05日 03:28, Sean Paul 写道:
>>
>> On Thu, Mar 30, 2017 at 03:46:06AM +0800, Icenowy Zheng wrote:
>>>
>>> As we are going to add support for the Allwinner DE2 Mixer in sun4i-drm
>>> driver, we will finally have two types of layer.
>>>
>>> Abstract the layer type to void * and a ops struct, which contains the
>>> only function used by crtc -- get the drm_plane struct of the layer.
>>>
>>> Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
>>> ---
>>> Refactored patch in v3.
>>>
>>> drivers/gpu/drm/sun4i/sun4i_crtc.c | 19 +++++++++++--------
>>> drivers/gpu/drm/sun4i/sun4i_crtc.h | 3 ++-
>>> drivers/gpu/drm/sun4i/sun4i_layer.c | 19 ++++++++++++++++++-
>>> drivers/gpu/drm/sun4i/sun4i_layer.h | 2 +-
>>> drivers/gpu/drm/sun4i/sunxi_layer.h | 17 +++++++++++++++++
>>> 5 files changed, 49 insertions(+), 11 deletions(-)
>>> create mode 100644 drivers/gpu/drm/sun4i/sunxi_layer.h
>>>
>>> diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c
>>> b/drivers/gpu/drm/sun4i/sun4i_crtc.c
>>> index 3c876c3a356a..33854ee7f636 100644
>>> --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
>>> +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
>>> @@ -29,6 +29,7 @@
>>> #include "sun4i_crtc.h"
>>> #include "sun4i_drv.h"
>>> #include "sun4i_layer.h"
>>> +#include "sunxi_layer.h"
>>> #include "sun4i_tcon.h"
>>>
>>> static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
>>> @@ -149,7 +150,7 @@ struct sun4i_crtc *sun4i_crtc_init(struct drm_device
>>> *drm,
>>> scrtc->tcon = tcon;
>>>
>>> /* Create our layers */
>>> - scrtc->layers = sun4i_layers_init(drm, scrtc->backend);
>>> + scrtc->layers = (void **)sun4i_layers_init(drm, scrtc);
>>> if (IS_ERR(scrtc->layers)) {
>>> dev_err(drm->dev, "Couldn't create the planes\n");
>>> return NULL;
>>> @@ -157,14 +158,15 @@ struct sun4i_crtc *sun4i_crtc_init(struct
>>> drm_device *drm,
>>>
>>> /* find primary and cursor planes for drm_crtc_init_with_planes
>>> */
>>> for (i = 0; scrtc->layers[i]; i++) {
>>> - struct sun4i_layer *layer = scrtc->layers[i];
>>> + void *layer = scrtc->layers[i];
>>> + struct drm_plane *plane =
>>> scrtc->layer_ops->get_plane(layer);
>>>
>>> - switch (layer->plane.type) {
>>> + switch (plane->type) {
>>> case DRM_PLANE_TYPE_PRIMARY:
>>> - primary = &layer->plane;
>>> + primary = plane;
>>> break;
>>> case DRM_PLANE_TYPE_CURSOR:
>>> - cursor = &layer->plane;
>>> + cursor = plane;
>>> break;
>>> default:
>>> break;
>>> @@ -190,10 +192,11 @@ struct sun4i_crtc *sun4i_crtc_init(struct
>>> drm_device *drm,
>>> /* Set possible_crtcs to this crtc for overlay planes */
>>> for (i = 0; scrtc->layers[i]; i++) {
>>> uint32_t possible_crtcs =
>>> BIT(drm_crtc_index(&scrtc->crtc));
>>> - struct sun4i_layer *layer = scrtc->layers[i];
>>> + void *layer = scrtc->layers[i];
>>> + struct drm_plane *plane =
>>> scrtc->layer_ops->get_plane(layer);
>>>
>>> - if (layer->plane.type == DRM_PLANE_TYPE_OVERLAY)
>>> - layer->plane.possible_crtcs = possible_crtcs;
>>> + if (plane->type == DRM_PLANE_TYPE_OVERLAY)
>>> + plane->possible_crtcs = possible_crtcs;
>>> }
>>>
>>> return scrtc;
>>> diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.h
>>> b/drivers/gpu/drm/sun4i/sun4i_crtc.h
>>> index 230cb8f0d601..a4036ee44cf8 100644
>>> --- a/drivers/gpu/drm/sun4i/sun4i_crtc.h
>>> +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.h
>>> @@ -19,7 +19,8 @@ struct sun4i_crtc {
>>>
>>> struct sun4i_backend *backend;
>>> struct sun4i_tcon *tcon;
>>> - struct sun4i_layer **layers;
>>> + void **layers;
>>> + const struct sunxi_layer_ops *layer_ops;
>>
>>
>> I think you should probably take a different approach to abstract the
>> layer
>> type. How about creating
>>
>> struct sunxi_layer {
>> struct drm_plane plane;
>> }
>>
>> base and then subclassing that for sun4i and sun8i? By doing this you can
>> avoid
>> the nasty casting and you can also get rid of the get_plane() hook and
>> layer_ops.
>
>
> For the situation that using ** things are easily to get weird.
That code could be reworked, by initializing the layers directly within
the crtc init code. If you look at rockchip's drm driver, you'll see
they do this. There is a good reason to do it this way, as you need
to first create the primary and cursor layers, pass them in when you
create the crtc, then initialize any additional layers with the
possible_crtcs bitmap.
In our driver we are currently initializing all layers, then going
back and filling in possible_crtcs for the extra layers.
And as Maxime and I mentioned in the other thread, we don't really
need to keep a reference to **layers.
Regards
ChenYu
>
>>
>> Sean
>>
>>
>>
>>> };
>>>
>>> static inline struct sun4i_crtc *drm_crtc_to_sun4i_crtc(struct drm_crtc
>>> *crtc)
>>> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c
>>> b/drivers/gpu/drm/sun4i/sun4i_layer.c
>>> index f26bde5b9117..bc4a70d6968b 100644
>>> --- a/drivers/gpu/drm/sun4i/sun4i_layer.c
>>> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
>>> @@ -16,7 +16,9 @@
>>> #include <drm/drmP.h>
>>>
>>> #include "sun4i_backend.h"
>>> +#include "sun4i_crtc.h"
>>> #include "sun4i_layer.h"
>>> +#include "sunxi_layer.h"
>>>
>>> struct sun4i_plane_desc {
>>> enum drm_plane_type type;
>>> @@ -100,6 +102,17 @@ static const struct sun4i_plane_desc
>>> sun4i_backend_planes[] = {
>>> },
>>> };
>>>
>>> +static struct drm_plane *sun4i_layer_get_plane(void *layer)
>>> +{
>>> + struct sun4i_layer *sun4i_layer = layer;
>>> +
>>> + return &sun4i_layer->plane;
>>> +}
>>> +
>>> +static const struct sunxi_layer_ops layer_ops = {
>>> + .get_plane = sun4i_layer_get_plane,
>>> +};
>>> +
>>> static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>>> struct sun4i_backend
>>> *backend,
>>> const struct
>>> sun4i_plane_desc *plane)
>>> @@ -129,9 +142,10 @@ static struct sun4i_layer
>>> *sun4i_layer_init_one(struct drm_device *drm,
>>> }
>>>
>>> struct sun4i_layer **sun4i_layers_init(struct drm_device *drm,
>>> - struct sun4i_backend *backend)
>>> + struct sun4i_crtc *crtc)
>>> {
>>> struct sun4i_layer **layers;
>>> + struct sun4i_backend *backend = crtc->backend;
>>> int i;
>>>
>>> layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun4i_backend_planes)
>>> + 1,
>>> @@ -181,5 +195,8 @@ struct sun4i_layer **sun4i_layers_init(struct
>>> drm_device *drm,
>>> layers[i] = layer;
>>> };
>>>
>>> + /* Assign layer ops to the CRTC */
>>> + crtc->layer_ops = &layer_ops;
>>> +
>>> return layers;
>>> }
>>> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h
>>> b/drivers/gpu/drm/sun4i/sun4i_layer.h
>>> index 4be1f0919df2..425eea7b9e3b 100644
>>> --- a/drivers/gpu/drm/sun4i/sun4i_layer.h
>>> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
>>> @@ -27,6 +27,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
>>> }
>>>
>>> struct sun4i_layer **sun4i_layers_init(struct drm_device *drm,
>>> - struct sun4i_backend *backend);
>>> + struct sun4i_crtc *crtc);
>>>
>>> #endif /* _SUN4I_LAYER_H_ */
>>> diff --git a/drivers/gpu/drm/sun4i/sunxi_layer.h
>>> b/drivers/gpu/drm/sun4i/sunxi_layer.h
>>> new file mode 100644
>>> index 000000000000..d8838ec39299
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/sun4i/sunxi_layer.h
>>> @@ -0,0 +1,17 @@
>>> +/*
>>> + * Copyright (C) 2017 Icenowy Zheng <icenowy-ymACFijhrKM@public.gmane.org>
>>> + *
>>> + * This program is free software; you can redistribute it and/or
>>> + * modify it under the terms of the GNU General Public License as
>>> + * published by the Free Software Foundation; either version 2 of
>>> + * the License, or (at your option) any later version.
>>> + */
>>> +
>>> +#ifndef _SUNXI_LAYER_H_
>>> +#define _SUNXI_LAYER_H_
>>> +
>>> +struct sunxi_layer_ops {
>>> + struct drm_plane *(*get_plane)(void *layer);
>>> +};
>>> +
>>> +#endif /* _SUNXI_LAYER_H_ */
>>> --
>>> 2.12.0
>>>
>>>
>>> _______________________________________________
>>> linux-arm-kernel mailing list
>>> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>
>>
>
> --
> You received this message because you are subscribed to the Google Groups
> "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
> For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* Re: [PATCH v2 2/2] of/pci: Fix memory leak in of_pci_get_host_bridge_resources
From: jeffy @ 2017-04-05 2:22 UTC (permalink / raw)
To: Bjorn Helgaas, Dmitry Torokhov
Cc: Rob Herring, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
toshi.kani-ZPxbGqLxI0U, Shawn Lin, Brian Norris, Doug Anderson,
Frank Rowand, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CAErSpo6CZ1hoHyzEnWBi7VDr51bLurF4-4t=-v4jH27kOpzYrg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Hi Bjorn,
On 04/05/2017 03:18 AM, Bjorn Helgaas wrote:
> On Thu, Mar 23, 2017 at 5:58 PM, Dmitry Torokhov <dtor-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> wrote:
>> On Thu, Mar 23, 2017 at 3:07 PM, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
>>> On Thu, Mar 23, 2017 at 3:12 AM, Jeffy Chen <jeffy.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org> wrote:
>>>> Currently we only free the allocated resource struct when error.
>>>> This would cause memory leak after pci_free_resource_list.
>>>>
>>>> Signed-off-by: Jeffy Chen <jeffy.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>>>> ---
>>>>
>>>> Changes in v2:
>>>> Don't change the resource_list_create_entry's behavior.
>>>>
>>>> drivers/of/of_pci.c | 57 +++++++++++++++++++++++------------------------------
>>>> 1 file changed, 25 insertions(+), 32 deletions(-)
>>>>
>>>> diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c
>>>> index 0ee42c3..a0ec246 100644
>>>> --- a/drivers/of/of_pci.c
>>>> +++ b/drivers/of/of_pci.c
>>>> @@ -190,8 +190,7 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
>>>> struct list_head *resources, resource_size_t *io_base)
>>>> {
>>>> struct resource_entry *window;
>>>> - struct resource *res;
>>>> - struct resource *bus_range;
>>>> + struct resource res;
>>>> struct of_pci_range range;
>>>> struct of_pci_range_parser parser;
>>>> char range_type[4];
>>>> @@ -200,24 +199,24 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
>>>> if (io_base)
>>>> *io_base = (resource_size_t)OF_BAD_ADDR;
>>>>
>>>> - bus_range = kzalloc(sizeof(*bus_range), GFP_KERNEL);
>>>> - if (!bus_range)
>>>> - return -ENOMEM;
>>>> -
>>>> pr_info("host bridge %s ranges:\n", dev->full_name);
>>>>
>>>> - err = of_pci_parse_bus_range(dev, bus_range);
>>>> + err = of_pci_parse_bus_range(dev, &res);
>>>> if (err) {
>>>> - bus_range->start = busno;
>>>> - bus_range->end = bus_max;
>>>> - bus_range->flags = IORESOURCE_BUS;
>>>> - pr_info(" No bus range found for %s, using %pR\n",
>>>> - dev->full_name, bus_range);
>>>> + res.start = busno;
>>>> + res.end = bus_max;
>>>> + res.flags = IORESOURCE_BUS;
>>>> + pr_info(" No bus range found for %s\n", dev->full_name);
>>>> } else {
>>>> - if (bus_range->end > bus_range->start + bus_max)
>>>> - bus_range->end = bus_range->start + bus_max;
>>>> + if (res.end > res.start + bus_max)
>>>> + res.end = res.start + bus_max;
>>>> + }
>>>> + window = pci_add_resource(resources, NULL);
>>>> + if (!window) {
>>>> + err = -ENOMEM;
>>>> + goto parse_failed;
>>>> }
>>>> - pci_add_resource(resources, bus_range);
>>>> + *window->res = res;
>>>
>>> Well, now this seems racy. You add a blank resource to the list first
>>> and then fill it in.
>>>
>>
>> Huh? There is absolutely no guarantees for concurrent access here.
>> pcI_add_resource_offset() first adds a resource and then modifies
>> offset. Here we add an empty resource and then fill it in.
>
> I don't really like this pattern either. Even if there's no actual
> racy behavior, it takes more analysis than necessary to figure that
> out.
>
> pci_add_resource_offset() allocates a resource list entry, sets the
> offset, then adds it to the list. It doesn't update a resource entry
> that might be visible to anybody else. Here we do update a resource
> that is already visible to others because it's already on the list.
i was following ./drivers/pnp/resource.c, but i'm agree this is not a
good way.
i'll upload a new version to fix this in another way. more ideas:
1/ pass a struct device to of_pci_get_host_bridge_resources and use
devm_kzalloc
2/ add a new type of flags(or reuse IORESOURCE_AUTO) to tell
pci_free_resource_list to kfree them)
3/ add new helpers of of_pci_add_resource[_offset] to alloc empty res,
fill it, add to list.
>
> Bjorn
>
> BTW, please CC linux-pci on the entire series so it's easier to
> review. I don't know where you envision having this applied, but I
> only apply things to the PCI tree after they appear on linux-pci.
>
oh, sorry, didn't notice that, will do in next version.
>
>
--
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 2/2] ARM: dts: imx7: add USDHC NAND clock to SDHC instances
From: Fabio Estevam @ 2017-04-05 2:15 UTC (permalink / raw)
To: Stefan Agner
Cc: Dong Aisheng, Shawn Guo, Sascha Hauer, Stephen Boyd, Dong Aisheng,
Fabio Estevam, robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
Mark Rutland,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-clk-u79uwXL29TY76Z2rM5mHXA, linux-kernel
In-Reply-To: <CAOMZO5CprxqnqRXMFJLBAgYAZGGnXNs3ZciyVz_mSLRnnSQQRQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Sun, Apr 2, 2017 at 2:02 PM, Fabio Estevam <festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Sat, Apr 1, 2017 at 1:15 AM, Stefan Agner <stefan-XLVq0VzYD2Y@public.gmane.org> wrote:
>
>> IMX7D_IPG_ROOT_CLK is currently not a valid clock in upstream... So we
>> would have to add it to the clock driver first.
>>
>> I guess we could/should add it anyway at one point? But probably also as
>> init on, just to make sure Linux does not disable it since it is
>> currently used by several IPs implicitly.
>
> Yes, I made a previous attempt do add IMX7D_IPG_ROOT_CLK and it did
> not work as I did not put it in the init_on clock list.
>
> Will submit a new patch adding it to init_on, thanks.
I thought that adding IMX7D_IPG_ROOT_CLK would do the trick, but the
patch below also causes the kernel to not boot:
--- a/drivers/clk/imx/clk-imx7d.c
+++ b/drivers/clk/imx/clk-imx7d.c
@@ -386,7 +386,7 @@ static int const clks_init_on[] __initconst = {
IMX7D_PLL_SYS_MAIN_480M_CLK, IMX7D_NAND_USDHC_BUS_ROOT_CLK,
IMX7D_DRAM_PHYM_ROOT_CLK, IMX7D_DRAM_ROOT_CLK,
IMX7D_DRAM_PHYM_ALT_ROOT_CLK, IMX7D_DRAM_ALT_ROOT_CLK,
- IMX7D_AHB_CHANNEL_ROOT_CLK,
+ IMX7D_AHB_CHANNEL_ROOT_CLK, IMX7D_IPG_ROOT_CLK,
};
static struct clk_onecell_data clk_data;
@@ -788,7 +788,7 @@ static void __init imx7d_clocks_init(struct
device_node *ccm_node)
clks[IMX7D_WRCLK_ROOT_DIV] =
imx_clk_divider2("wrclk_post_div", "wrclk_pre_div", base + 0xbd00, 0,
6);
clks[IMX7D_CLKO1_ROOT_DIV] =
imx_clk_divider2("clko1_post_div", "clko1_pre_div", base + 0xbd80, 0,
6);
clks[IMX7D_CLKO2_ROOT_DIV] =
imx_clk_divider2("clko2_post_div", "clko2_pre_div", base + 0xbe00, 0,
6);
-
+ clks[IMX7D_IPG_ROOT_CLK] = imx_clk_divider2("ipg_root_clk",
"ahb_root_clk", base + 0x9080, 0, 2);
clks[IMX7D_ARM_A7_ROOT_CLK] = imx_clk_gate4("arm_a7_root_clk",
"arm_a7_div", base + 0x4000, 0);
clks[IMX7D_ARM_M4_ROOT_CLK] = imx_clk_gate4("arm_m4_root_clk",
"arm_m4_div", base + 0x4010, 0);
clks[IMX7D_ARM_M0_ROOT_CLK] = imx_clk_gate4("arm_m0_root_clk",
"arm_m0_div", base + 0x4020, 0);
--
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 v4] of/pci: Fix memory leak in of_pci_get_host_bridge_resources
From: Jeffy Chen @ 2017-04-05 2:15 UTC (permalink / raw)
To: linux-pci-u79uwXL29TY76Z2rM5mHXA
Cc: robh-DgEjT+Ai2ygdnm+yROfE0A, toshi.kani-ZPxbGqLxI0U,
shawn.lin-TNX95d0MmH7DzftRWevZcw,
briannorris-F7+t8E8rja9g9hUCZPvPmw,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dianders-F7+t8E8rja9g9hUCZPvPmw, bhelgaas-hpIqsD4AKlfQT0dZR+AlfA,
dtor-F7+t8E8rja9g9hUCZPvPmw, Jeffy Chen,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Frank Rowand
In-Reply-To: <1491358521-17834-1-git-send-email-jeffy.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Currently we only free the allocated resource struct when error.
This would cause memory leak after pci_free_resource_list.
Signed-off-by: Jeffy Chen <jeffy.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
Changes in v4:
Use IORESOURCE_AUTO to mark struct resources which could be copied.
Changes in v3:
Add some comments.
Changes in v2:
Don't change the resource_list_create_entry's behavior.
drivers/of/of_pci.c | 51 ++++++++++++++++++---------------------------------
drivers/pci/bus.c | 7 ++++++-
2 files changed, 24 insertions(+), 34 deletions(-)
diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c
index 0ee42c3..1ecbd79 100644
--- a/drivers/of/of_pci.c
+++ b/drivers/of/of_pci.c
@@ -189,9 +189,7 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
unsigned char busno, unsigned char bus_max,
struct list_head *resources, resource_size_t *io_base)
{
- struct resource_entry *window;
- struct resource *res;
- struct resource *bus_range;
+ struct resource res;
struct of_pci_range range;
struct of_pci_range_parser parser;
char range_type[4];
@@ -200,24 +198,21 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
if (io_base)
*io_base = (resource_size_t)OF_BAD_ADDR;
- bus_range = kzalloc(sizeof(*bus_range), GFP_KERNEL);
- if (!bus_range)
- return -ENOMEM;
-
pr_info("host bridge %s ranges:\n", dev->full_name);
- err = of_pci_parse_bus_range(dev, bus_range);
+ err = of_pci_parse_bus_range(dev, &res);
if (err) {
- bus_range->start = busno;
- bus_range->end = bus_max;
- bus_range->flags = IORESOURCE_BUS;
- pr_info(" No bus range found for %s, using %pR\n",
- dev->full_name, bus_range);
+ res.start = busno;
+ res.end = bus_max;
+ res.flags = IORESOURCE_BUS;
+ pr_info(" No bus range found for %s\n", dev->full_name);
} else {
- if (bus_range->end > bus_range->start + bus_max)
- bus_range->end = bus_range->start + bus_max;
+ if (res.end > res.start + bus_max)
+ res.end = res.start + bus_max;
}
- pci_add_resource(resources, bus_range);
+
+ res.flags |= IORESOURCE_AUTO;
+ pci_add_resource(resources, res);
/* Check for ranges property */
err = of_pci_range_parser_init(&parser, dev);
@@ -244,24 +239,16 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
if (range.cpu_addr == OF_BAD_ADDR || range.size == 0)
continue;
- res = kzalloc(sizeof(struct resource), GFP_KERNEL);
- if (!res) {
- err = -ENOMEM;
- goto parse_failed;
- }
-
- err = of_pci_range_to_resource(&range, dev, res);
- if (err) {
- kfree(res);
+ err = of_pci_range_to_resource(&range, dev, &res);
+ if (err)
continue;
- }
- if (resource_type(res) == IORESOURCE_IO) {
+ if (resource_type(&res) == IORESOURCE_IO) {
if (!io_base) {
pr_err("I/O range found for %s. Please provide an io_base pointer to save CPU base address\n",
dev->full_name);
err = -EINVAL;
- goto conversion_failed;
+ goto parse_failed;
}
if (*io_base != (resource_size_t)OF_BAD_ADDR)
pr_warn("More than one I/O resource converted for %s. CPU base address for old range lost!\n",
@@ -269,16 +256,14 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
*io_base = range.cpu_addr;
}
- pci_add_resource_offset(resources, res, res->start - range.pci_addr);
+ res.flags |= IORESOURCE_AUTO;
+ pci_add_resource_offset(resources, res,
+ res->start - range.pci_addr);
}
return 0;
-conversion_failed:
- kfree(res);
parse_failed:
- resource_list_for_each_entry(window, resources)
- kfree(window->res);
pci_free_resource_list(resources);
return err;
}
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index bc56cf1..5f32bfd 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -22,12 +22,17 @@ void pci_add_resource_offset(struct list_head *resources, struct resource *res,
{
struct resource_entry *entry;
- entry = resource_list_create_entry(res, 0);
+ entry = resource_list_create_entry(NULL, 0);
if (!entry) {
printk(KERN_ERR "PCI: can't add host bridge window %pR\n", res);
return;
}
+ if (res->flags & IORESOURCE_AUTO)
+ *entry->res = *res;
+ else
+ entry->res = res
+
entry->offset = offset;
resource_list_add_tail(entry, resources);
}
--
2.1.4
--
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 related
* [PATCH v4 0/1] Fix memory leak in of_pci_get_host_bridge_resources
From: Jeffy Chen @ 2017-04-05 2:15 UTC (permalink / raw)
To: linux-pci
Cc: robh, toshi.kani, shawn.lin, briannorris, linux-kernel, dianders,
bhelgaas, dtor, Jeffy Chen, devicetree, Rob Herring, Frank Rowand
In of_pci_get_host_bridge_resources, we alloced some struct resource
variables, and they would cause memory leak since no where to free them.
Changes in v4:
Use IORESOURCE_AUTO to mark struct resources which could be copied.
Changes in v3:
Add some comments.
Changes in v2:
Don't change the resource_list_create_entry's behavior.
Jeffy Chen (1):
of/pci: Fix memory leak in of_pci_get_host_bridge_resources
drivers/of/of_pci.c | 51 ++++++++++++++++++---------------------------------
drivers/pci/bus.c | 7 ++++++-
2 files changed, 24 insertions(+), 34 deletions(-)
--
2.1.4
^ permalink raw reply
* Re: [PATCH v4 4/6] arm64: dts: rockchip: add core dtsi file for RK3328 SoCs
From: Elaine Zhang @ 2017-04-05 2:07 UTC (permalink / raw)
To: Heiko Stuebner, cl
Cc: robh+dt, mark.rutland, zhengxing, andy.yan, jay.xu, matthias.bgg,
paweljarosz3691, devicetree, linux-arm-kernel, linux-rockchip,
linux-kernel, wsa, linux-i2c, jic23, knaack.h, lars, pmeerw, wxt,
david.wu, linux-iio, shawn.lin, akpm, dianders, yamada.masahiro,
catalin.marinas, will.deacon, afaerber, shawnguo, khilman, arnd,
fabio.estevam, kever.yang, tony.xie
In-Reply-To: <6636047.jL6XHNkknt@phil>
On 04/05/2017 12:04 AM, Heiko Stuebner wrote:
> Am Montag, 27. März 2017, 17:40:48 CEST schrieb cl@rock-chips.com:
>> From: Liang Chen <cl@rock-chips.com>
>>
>> This patch adds core dtsi file for Rockchip RK3328 SoCs.
>>
>> Signed-off-by: Liang Chen <cl@rock-chips.com>
>
> applied for 4.12, with the following list of changes:
>
> - reorder some properties to bring them in alphabetical order
> - dropped the status-disabled from the power-controller
> power-domain control is a quite essential part of the system, so if
> boards really want to disable them, they should do it in their board file
> Having power-domains on all the time, is also our default in all other
> devicetrees.
> - removed #dma-cells from spi0 -> this is not a dma controller
> - reword the cru assigned-clocks comment a bit
> - fixed sdmmc1_bus4 pins, as indicated by Shawn and after looking up the
> correct pins in the manual
>
>
> And a final question, are you sure about SCLK_PDM becoming a child of the
> APLL in your cru assigned-clocks, as the APLL will vary later on with cpufreq
> active?
>
the NPLL will vary later on with cpufreq active.
The NPLL is better than APLL, so NPLL is for clk_core,and apll is for pdm.
please see the TRM in CRU:
1.4 Function Description
/........./
To maximize the flexibility, some of clocks can select divider source
from 5 PLLs. (Note: It’s
recommended to use NEW PLL instead of ARM PLL as arm clock source,
because NEW PLL is
near to ARM. And it’s jitter is better than ARM PLL).
>
> Heiko
>
>
>
^ permalink raw reply
* [PATCH v5 23/23] drivers/fsi: Use asynchronous slave mode
From: Christopher Bostic @ 2017-04-05 2:06 UTC (permalink / raw)
To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, rostedt-nx8X9YLhiw1AfugRpC6u6w,
mingo-H+wXaHxf7aLQT0dZR+AlfA,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Jeremy Kerr, joel-U3u1mxZcP9KHXe+LvDLADg,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, andrew-zrmu5oMJ5Fs,
alistair-Y4h6yKqj69EXC2x5gXVKYQ,
benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r, Chris Bostic
In-Reply-To: <20170405020607.79939-1-cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
From: Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>
For slaves that are behind a software-clocked master, we want FSI CFAMs
to run asynchronously to the FSI clock, so set up our slaves to be in
async mode.
Signed-off-by: Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>
Signed-off-by: Chris Bostic <cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
---
drivers/fsi/fsi-core.c | 22 +++++++++++++++++++++-
drivers/fsi/fsi-master-gpio.c | 1 +
drivers/fsi/fsi-master.h | 2 ++
3 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 747d0e3..a6ed34f 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -47,6 +47,7 @@
#define FSI_SMODE 0x0 /* R/W: Mode register */
#define FSI_SISC 0x8 /* R/W: Interrupt condition */
#define FSI_SSTAT 0x14 /* R : Slave status */
+#define FSI_LLMODE 0x100 /* R/W: Link layer mode register */
/*
* SMODE fields
@@ -62,6 +63,11 @@
#define FSI_SMODE_LBCRR_SHIFT 8 /* Clk ratio shift */
#define FSI_SMODE_LBCRR_MASK 0xf /* Clk ratio mask */
+/*
+ * LLMODE fields
+ */
+#define FSI_LLMODE_ASYNC 0x1
+
#define FSI_SLAVE_SIZE_23b 0x800000
static DEFINE_IDA(master_ida);
@@ -560,8 +566,8 @@ static void fsi_slave_release(struct device *dev)
static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
{
+ uint32_t chip_id, llmode;
struct fsi_slave *slave;
- uint32_t chip_id;
uint8_t crc;
int rc;
@@ -597,6 +603,20 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
return -ENODEV;
}
+ /* If we're behind a master that doesn't provide a self-running bus
+ * clock, put the slave into async mode
+ */
+ if (master->flags & FSI_MASTER_FLAG_SWCLOCK) {
+ llmode = cpu_to_be32(FSI_LLMODE_ASYNC);
+ rc = fsi_master_write(master, link, id,
+ FSI_SLAVE_BASE + FSI_LLMODE,
+ &llmode, sizeof(llmode));
+ if (rc)
+ dev_warn(&master->dev,
+ "can't set llmode on slave:%02x:%02x %d\n",
+ link, id, rc);
+ }
+
/* We can communicate with a slave; create the slave device and
* register.
*/
diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
index 5d9e0b0..d5cce88 100644
--- a/drivers/fsi/fsi-master-gpio.c
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -569,6 +569,7 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
master->gpio_mux = gpio;
master->master.n_links = 1;
+ master->master.flags = FSI_MASTER_FLAG_SWCLOCK;
master->master.read = fsi_master_gpio_read;
master->master.write = fsi_master_gpio_write;
master->master.term = fsi_master_gpio_term;
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index d6a4885..fd39924 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -19,6 +19,8 @@
#include <linux/device.h>
+#define FSI_MASTER_FLAG_SWCLOCK 0x1
+
struct fsi_master {
struct device dev;
int idx;
--
1.8.2.2
--
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 related
* [PATCH v5 22/23] drivers/fsi: Add hub master support
From: Christopher Bostic @ 2017-04-05 2:06 UTC (permalink / raw)
To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, rostedt-nx8X9YLhiw1AfugRpC6u6w,
mingo-H+wXaHxf7aLQT0dZR+AlfA,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Chris Bostic, joel-U3u1mxZcP9KHXe+LvDLADg,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, andrew-zrmu5oMJ5Fs,
alistair-Y4h6yKqj69EXC2x5gXVKYQ,
benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r, Jeremy Kerr
In-Reply-To: <20170405020607.79939-1-cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
From: Chris Bostic <cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
Add an engine driver to expose a "hub" FSI master - which has a set of
control registers in the engine address space, and uses a chunk of the
slave address space for actual FSI communication.
Additional changes from Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>.
Signed-off-by: Chris Bostic <cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
Signed-off-by: Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>
Signed-off-by: Joel Stanley <joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>
---
drivers/fsi/Kconfig | 9 ++
drivers/fsi/Makefile | 1 +
drivers/fsi/fsi-master-hub.c | 327 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 337 insertions(+)
create mode 100644 drivers/fsi/fsi-master-hub.c
diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index 0fa265c..e1156b4 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -18,6 +18,15 @@ config FSI_MASTER_GPIO
---help---
This option enables a FSI master driver using GPIO lines.
+config FSI_MASTER_HUB
+ tristate "FSI hub master"
+ depends on FSI
+ ---help---
+ This option enables a FSI hub master driver. Hub is a type of FSI
+ master that is connected to the upstream master via a slave. Hubs
+ allow chaining of FSI links to an arbitrary depth. This allows for
+ a high target device fanout.
+
config FSI_SCOM
tristate "SCOM FSI client device driver"
depends on FSI
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index 3466f08..65eb99d 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_FSI) += fsi-core.o
+obj-$(CONFIG_FSI_MASTER_HUB) += fsi-master-hub.o
obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c
new file mode 100644
index 0000000..133b9bf
--- /dev/null
+++ b/drivers/fsi/fsi-master-hub.c
@@ -0,0 +1,327 @@
+/*
+ * FSI hub master driver
+ *
+ * Copyright (C) IBM Corporation 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/fsi.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "fsi-master.h"
+
+/* Control Registers */
+#define FSI_MMODE 0x0 /* R/W: mode */
+#define FSI_MDLYR 0x4 /* R/W: delay */
+#define FSI_MCRSP 0x8 /* R/W: clock rate */
+#define FSI_MENP0 0x10 /* R/W: enable */
+#define FSI_MLEVP0 0x18 /* R: plug detect */
+#define FSI_MSENP0 0x18 /* S: Set enable */
+#define FSI_MCENP0 0x20 /* C: Clear enable */
+#define FSI_MAEB 0x70 /* R: Error address */
+#define FSI_MVER 0x74 /* R: master version/type */
+#define FSI_MRESP0 0xd0 /* W: Port reset */
+#define FSI_MESRB0 0x1d0 /* R: Master error status */
+#define FSI_MRESB0 0x1d0 /* W: Reset bridge */
+#define FSI_MECTRL 0x2e0 /* W: Error control */
+
+/* MMODE: Mode control */
+#define FSI_MMODE_EIP 0x80000000 /* Enable interrupt polling */
+#define FSI_MMODE_ECRC 0x40000000 /* Enable error recovery */
+#define FSI_MMODE_EPC 0x10000000 /* Enable parity checking */
+#define FSI_MMODE_P8_TO_LSB 0x00000010 /* Timeout value LSB */
+ /* MSB=1, LSB=0 is 0.8 ms */
+ /* MSB=0, LSB=1 is 0.9 ms */
+#define FSI_MMODE_CRS0SHFT 18 /* Clk rate selection 0 shift */
+#define FSI_MMODE_CRS0MASK 0x3ff /* Clk rate selection 0 mask */
+#define FSI_MMODE_CRS1SHFT 8 /* Clk rate selection 1 shift */
+#define FSI_MMODE_CRS1MASK 0x3ff /* Clk rate selection 1 mask */
+
+/* MRESB: Reset brindge */
+#define FSI_MRESB_RST_GEN 0x80000000 /* General reset */
+#define FSI_MRESB_RST_ERR 0x40000000 /* Error Reset */
+
+/* MRESB: Reset port */
+#define FSI_MRESP_RST_ALL_MASTER 0x20000000 /* Reset all FSI masters */
+#define FSI_MRESP_RST_ALL_LINK 0x10000000 /* Reset all FSI port contr. */
+#define FSI_MRESP_RST_MCR 0x08000000 /* Reset FSI master reg. */
+#define FSI_MRESP_RST_PYE 0x04000000 /* Reset FSI parity error */
+#define FSI_MRESP_RST_ALL 0xfc000000 /* Reset any error */
+
+/* MECTRL: Error control */
+#define FSI_MECTRL_EOAE 0x8000 /* Enable machine check when */
+ /* master 0 in error */
+#define FSI_MECTRL_P8_AUTO_TERM 0x4000 /* Auto terminate */
+
+#define FSI_ENGID_HUB_MASTER 0x1c
+#define FSI_HUB_LINK_OFFSET 0x80000
+#define FSI_HUB_LINK_SIZE 0x80000
+#define FSI_HUB_MASTER_MAX_LINKS 8
+
+#define FSI_LINK_ENABLE_SETUP_TIME 10 /* in mS */
+
+/*
+ * FSI hub master support
+ *
+ * A hub master increases the number of potential target devices that the
+ * primary FSI master can access. For each link a primary master supports,
+ * each of those links can in turn be chained to a hub master with multiple
+ * links of its own.
+ *
+ * The hub is controlled by a set of control registers exposed as a regular fsi
+ * device (the hub->upstream device), and provides access to the downstream FSI
+ * bus as through an address range on the slave itself (->addr and ->size).
+ *
+ * [This differs from "cascaded" masters, which expose the entire downstream
+ * bus entirely through the fsi device address range, and so have a smaller
+ * accessible address space.]
+ */
+struct fsi_master_hub {
+ struct fsi_master master;
+ struct fsi_device *upstream;
+ uint32_t addr, size; /* slave-relative addr of */
+ /* master address space */
+};
+
+#define to_fsi_master_hub(m) container_of(m, struct fsi_master_hub, master)
+
+static int hub_master_read(struct fsi_master *master, int link,
+ uint8_t id, uint32_t addr, void *val, size_t size)
+{
+ struct fsi_master_hub *hub = to_fsi_master_hub(master);
+
+ if (id != 0)
+ return -EINVAL;
+
+ addr += hub->addr + (link * FSI_HUB_LINK_SIZE);
+ return fsi_slave_read(hub->upstream->slave, addr, val, size);
+}
+
+static int hub_master_write(struct fsi_master *master, int link,
+ uint8_t id, uint32_t addr, const void *val, size_t size)
+{
+ struct fsi_master_hub *hub = to_fsi_master_hub(master);
+
+ if (id != 0)
+ return -EINVAL;
+
+ addr += hub->addr + (link * FSI_HUB_LINK_SIZE);
+ return fsi_slave_write(hub->upstream->slave, addr, val, size);
+}
+
+static int hub_master_break(struct fsi_master *master, int link)
+{
+ uint32_t addr, cmd;
+
+ addr = 0x4;
+ cmd = cpu_to_be32(0xc0de0000);
+
+ return hub_master_write(master, link, 0, addr, &cmd, sizeof(cmd));
+}
+
+static int hub_master_link_enable(struct fsi_master *master, int link)
+{
+ struct fsi_master_hub *hub = to_fsi_master_hub(master);
+ int idx, bit;
+ __be32 reg;
+ int rc;
+
+ idx = link / 32;
+ bit = link % 32;
+
+ reg = cpu_to_be32(0x80000000 >> bit);
+
+ rc = fsi_device_write(hub->upstream, FSI_MSENP0 + (4 * idx), ®, 4);
+
+ mdelay(FSI_LINK_ENABLE_SETUP_TIME);
+
+ fsi_device_read(hub->upstream, FSI_MENP0 + (4 * idx), ®, 4);
+
+ return rc;
+}
+
+static void hub_master_release(struct device *dev)
+{
+ struct fsi_master_hub *hub = to_fsi_master_hub(dev_to_fsi_master(dev));
+
+ kfree(hub);
+}
+
+/* mmode encoders */
+static inline u32 fsi_mmode_crs0(u32 x)
+{
+ return (x & FSI_MMODE_CRS0MASK) << FSI_MMODE_CRS0SHFT;
+}
+
+static inline u32 fsi_mmode_crs1(u32 x)
+{
+ return (x & FSI_MMODE_CRS1MASK) << FSI_MMODE_CRS1SHFT;
+}
+
+static int hub_master_init(struct fsi_master_hub *hub)
+{
+ struct fsi_device *dev = hub->upstream;
+ __be32 reg;
+ int rc;
+
+ reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
+ | FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
+ rc = fsi_device_write(dev, FSI_MRESP0, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ /* Initialize the MFSI (hub master) engine */
+ reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
+ | FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
+ rc = fsi_device_write(dev, FSI_MRESP0, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ reg = cpu_to_be32(FSI_MECTRL_EOAE | FSI_MECTRL_P8_AUTO_TERM);
+ rc = fsi_device_write(dev, FSI_MECTRL, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ reg = cpu_to_be32(FSI_MMODE_EIP | FSI_MMODE_ECRC | FSI_MMODE_EPC
+ | fsi_mmode_crs0(1) | fsi_mmode_crs1(1)
+ | FSI_MMODE_P8_TO_LSB);
+ rc = fsi_device_write(dev, FSI_MMODE, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ reg = cpu_to_be32(0xffff0000);
+ rc = fsi_device_write(dev, FSI_MDLYR, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ reg = ~0;
+ rc = fsi_device_write(dev, FSI_MSENP0, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ /* Leave enabled long enough for master logic to set up */
+ mdelay(FSI_LINK_ENABLE_SETUP_TIME);
+
+ rc = fsi_device_write(dev, FSI_MCENP0, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ rc = fsi_device_read(dev, FSI_MAEB, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK);
+ rc = fsi_device_write(dev, FSI_MRESP0, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ rc = fsi_device_read(dev, FSI_MLEVP0, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ /* Reset the master bridge */
+ reg = cpu_to_be32(FSI_MRESB_RST_GEN);
+ rc = fsi_device_write(dev, FSI_MRESB0, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ reg = cpu_to_be32(FSI_MRESB_RST_ERR);
+ return fsi_device_write(dev, FSI_MRESB0, ®, sizeof(reg));
+}
+
+static int hub_master_probe(struct device *dev)
+{
+ struct fsi_device *fsi_dev = to_fsi_dev(dev);
+ struct fsi_master_hub *hub;
+ uint32_t reg, links;
+ __be32 __reg;
+ int rc;
+
+ rc = fsi_device_read(fsi_dev, FSI_MVER, &__reg, sizeof(__reg));
+ if (rc)
+ return rc;
+
+ reg = be32_to_cpu(__reg);
+ links = (reg >> 8) & 0xff;
+ dev_info(dev, "hub version %08x (%d links)\n", reg, links);
+
+ rc = fsi_slave_claim_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
+ FSI_HUB_LINK_SIZE * links);
+ if (rc) {
+ dev_err(dev, "can't claim slave address range for links");
+ return rc;
+ }
+
+ hub = kzalloc(sizeof(*hub), GFP_KERNEL);
+ if (!hub) {
+ rc = -ENOMEM;
+ goto err_release;
+ }
+
+ hub->addr = FSI_HUB_LINK_OFFSET;
+ hub->size = FSI_HUB_LINK_SIZE * links;
+ hub->upstream = fsi_dev;
+
+ hub->master.dev.parent = dev;
+ hub->master.dev.release = hub_master_release;
+
+ hub->master.n_links = links;
+ hub->master.read = hub_master_read;
+ hub->master.write = hub_master_write;
+ hub->master.send_break = hub_master_break;
+ hub->master.link_enable = hub_master_link_enable;
+
+ dev_set_drvdata(dev, hub);
+
+ hub_master_init(hub);
+
+ rc = fsi_master_register(&hub->master);
+ if (!rc)
+ return 0;
+
+ kfree(hub);
+err_release:
+ fsi_slave_release_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
+ FSI_HUB_LINK_SIZE * links);
+ return rc;
+}
+
+static int hub_master_remove(struct device *dev)
+{
+ struct fsi_master_hub *hub = dev_get_drvdata(dev);
+
+ fsi_master_unregister(&hub->master);
+ fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size);
+ return 0;
+}
+
+static struct fsi_device_id hub_master_ids[] = {
+ {
+ .engine_type = FSI_ENGID_HUB_MASTER,
+ .version = FSI_VERSION_ANY,
+ },
+ { 0 }
+};
+
+static struct fsi_driver hub_master_driver = {
+ .id_table = hub_master_ids,
+ .drv = {
+ .name = "fsi-master-hub",
+ .bus = &fsi_bus_type,
+ .probe = hub_master_probe,
+ .remove = hub_master_remove,
+ }
+};
+
+module_fsi_driver(hub_master_driver);
+MODULE_LICENSE("GPL");
--
1.8.2.2
--
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 related
* [PATCH v5 21/23] drivers/fsi: Add SCOM FSI client device driver
From: Christopher Bostic @ 2017-04-05 2:06 UTC (permalink / raw)
To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, rostedt-nx8X9YLhiw1AfugRpC6u6w,
mingo-H+wXaHxf7aLQT0dZR+AlfA,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Chris Bostic, joel-U3u1mxZcP9KHXe+LvDLADg,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, andrew-zrmu5oMJ5Fs,
alistair-Y4h6yKqj69EXC2x5gXVKYQ,
benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r, Edward A . James,
Jeremy Kerr
In-Reply-To: <20170405020607.79939-1-cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
From: Chris Bostic <cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
Create a simple SCOM engine device driver that reads and writes
its control registers via an FSI bus.
Includes changes from Edward A. James <eajames-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>.
Signed-off-by: Chris Bostic <cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
Signed-off-by: Joel Stanley <joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>
Signed-off-by: Edward A. James <eajames-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>
---
drivers/fsi/Kconfig | 6 ++
drivers/fsi/Makefile | 1 +
drivers/fsi/fsi-scom.c | 263 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 270 insertions(+)
create mode 100644 drivers/fsi/fsi-scom.c
diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index 9cf8345..0fa265c 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -18,6 +18,12 @@ config FSI_MASTER_GPIO
---help---
This option enables a FSI master driver using GPIO lines.
+config FSI_SCOM
+ tristate "SCOM FSI client device driver"
+ depends on FSI
+ ---help---
+ This option enables an FSI based SCOM device driver.
+
endif
endmenu
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index ed28ac0..3466f08 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_FSI) += fsi-core.o
obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
+obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
new file mode 100644
index 0000000..98d062f
--- /dev/null
+++ b/drivers/fsi/fsi-scom.c
@@ -0,0 +1,263 @@
+/*
+ * SCOM FSI Client device driver
+ *
+ * Copyright (C) IBM Corporation 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERGCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/fsi.h>
+#include <linux/module.h>
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/miscdevice.h>
+#include <linux/list.h>
+#include <linux/idr.h>
+
+#define FSI_ENGID_SCOM 0x5
+
+#define SCOM_FSI2PIB_DELAY 50
+
+/* SCOM engine register set */
+#define SCOM_DATA0_REG 0x00
+#define SCOM_DATA1_REG 0x04
+#define SCOM_CMD_REG 0x08
+#define SCOM_RESET_REG 0x1C
+
+#define SCOM_RESET_CMD 0x80000000
+#define SCOM_WRITE_CMD 0x80000000
+
+struct scom_device {
+ struct list_head link;
+ struct fsi_device *fsi_dev;
+ struct miscdevice mdev;
+ char name[32];
+ int idx;
+};
+
+#define to_scom_dev(x) container_of((x), struct scom_device, mdev)
+
+static struct list_head scom_devices;
+
+static DEFINE_IDA(scom_ida);
+
+static int put_scom(struct scom_device *scom_dev, uint64_t value,
+ uint32_t addr)
+{
+ int rc;
+ uint32_t data;
+
+ data = cpu_to_be32(SCOM_RESET_CMD);
+ rc = fsi_device_write(scom_dev->fsi_dev, SCOM_RESET_REG, &data,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ data = cpu_to_be32((value >> 32) & 0xffffffff);
+ rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ data = cpu_to_be32(value & 0xffffffff);
+ rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ data = cpu_to_be32(SCOM_WRITE_CMD | addr);
+ return fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
+ sizeof(uint32_t));
+}
+
+static int get_scom(struct scom_device *scom_dev, uint64_t *value,
+ uint32_t addr)
+{
+ uint32_t result, data;
+ int rc;
+
+ *value = 0ULL;
+ data = cpu_to_be32(addr);
+ rc = fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA0_REG, &result,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ *value |= (uint64_t)cpu_to_be32(result) << 32;
+ rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA1_REG, &result,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ *value |= cpu_to_be32(result);
+
+ return 0;
+}
+
+static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
+ loff_t *offset)
+{
+ int rc;
+ struct miscdevice *mdev =
+ (struct miscdevice *)filep->private_data;
+ struct scom_device *scom = to_scom_dev(mdev);
+ struct device *dev = &scom->fsi_dev->dev;
+ uint64_t val;
+
+ if (len != sizeof(uint64_t))
+ return -EINVAL;
+
+ rc = get_scom(scom, &val, *offset);
+ if (rc) {
+ dev_dbg(dev, "get_scom fail:%d\n", rc);
+ return rc;
+ }
+
+ rc = copy_to_user(buf, &val, len);
+ if (rc)
+ dev_dbg(dev, "copy to user failed:%d\n", rc);
+
+ return rc ? rc : len;
+}
+
+static ssize_t scom_write(struct file *filep, const char __user *buf,
+ size_t len, loff_t *offset)
+{
+ int rc;
+ struct miscdevice *mdev = filep->private_data;
+ struct scom_device *scom = to_scom_dev(mdev);
+ struct device *dev = &scom->fsi_dev->dev;
+ uint64_t val;
+
+ if (len != sizeof(uint64_t))
+ return -EINVAL;
+
+ rc = copy_from_user(&val, buf, len);
+ if (rc) {
+ dev_dbg(dev, "copy from user failed:%d\n", rc);
+ return -EINVAL;
+ }
+
+ rc = put_scom(scom, val, *offset);
+ if (rc) {
+ dev_dbg(dev, "put_scom failed with:%d\n", rc);
+ return rc;
+ }
+
+ return len;
+}
+
+static loff_t scom_llseek(struct file *file, loff_t offset, int whence)
+{
+ switch (whence) {
+ case SEEK_CUR:
+ break;
+ case SEEK_SET:
+ file->f_pos = offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return offset;
+}
+
+static const struct file_operations scom_fops = {
+ .owner = THIS_MODULE,
+ .llseek = scom_llseek,
+ .read = scom_read,
+ .write = scom_write,
+};
+
+static int scom_probe(struct device *dev)
+{
+ struct fsi_device *fsi_dev = to_fsi_dev(dev);
+ struct scom_device *scom;
+
+ scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
+ if (!scom)
+ return -ENOMEM;
+
+ scom->idx = ida_simple_get(&scom_ida, 1, INT_MAX, GFP_KERNEL);
+ snprintf(scom->name, sizeof(scom->name), "scom%d", scom->idx);
+ scom->fsi_dev = fsi_dev;
+ scom->mdev.minor = MISC_DYNAMIC_MINOR;
+ scom->mdev.fops = &scom_fops;
+ scom->mdev.name = scom->name;
+ scom->mdev.parent = dev;
+ list_add(&scom->link, &scom_devices);
+
+ return misc_register(&scom->mdev);
+}
+
+static int scom_remove(struct device *dev)
+{
+ struct scom_device *scom, *scom_tmp;
+ struct fsi_device *fsi_dev = to_fsi_dev(dev);
+
+ list_for_each_entry_safe(scom, scom_tmp, &scom_devices, link) {
+ if (scom->fsi_dev == fsi_dev) {
+ list_del(&scom->link);
+ ida_simple_remove(&scom_ida, scom->idx);
+ misc_deregister(&scom->mdev);
+ }
+ }
+
+ return 0;
+}
+
+static struct fsi_device_id scom_ids[] = {
+ {
+ .engine_type = FSI_ENGID_SCOM,
+ .version = FSI_VERSION_ANY,
+ },
+ { 0 }
+};
+
+static struct fsi_driver scom_drv = {
+ .id_table = scom_ids,
+ .drv = {
+ .name = "scom",
+ .bus = &fsi_bus_type,
+ .probe = scom_probe,
+ .remove = scom_remove,
+ }
+};
+
+static int scom_init(void)
+{
+ INIT_LIST_HEAD(&scom_devices);
+ return fsi_driver_register(&scom_drv);
+}
+
+static void scom_exit(void)
+{
+ struct list_head *pos;
+ struct scom_device *scom;
+
+ list_for_each(pos, &scom_devices) {
+ scom = list_entry(pos, struct scom_device, link);
+ misc_deregister(&scom->mdev);
+ devm_kfree(&scom->fsi_dev->dev, scom);
+ }
+ fsi_driver_unregister(&scom_drv);
+}
+
+module_init(scom_init);
+module_exit(scom_exit);
+MODULE_LICENSE("GPL");
--
1.8.2.2
--
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 related
* [PATCH v5 20/23] drivers/fsi/gpio: Add tracepoints for GPIO master
From: Christopher Bostic @ 2017-04-05 2:06 UTC (permalink / raw)
To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, rostedt-nx8X9YLhiw1AfugRpC6u6w,
mingo-H+wXaHxf7aLQT0dZR+AlfA,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Jeremy Kerr, joel-U3u1mxZcP9KHXe+LvDLADg,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, andrew-zrmu5oMJ5Fs,
alistair-Y4h6yKqj69EXC2x5gXVKYQ,
benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r, Chris Bostic
In-Reply-To: <20170405020607.79939-1-cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
From: Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>
Add trace points for key GPIO operations of the GPIO based FSI
master.
Signed-off-by: Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>
Signed-off-by: Chris Bostic <cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
---
drivers/fsi/fsi-master-gpio.c | 9 +++++
include/trace/events/fsi_master_gpio.h | 68 ++++++++++++++++++++++++++++++++++
2 files changed, 77 insertions(+)
create mode 100644 include/trace/events/fsi_master_gpio.h
diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
index 9fedfaf..5d9e0b0 100644
--- a/drivers/fsi/fsi-master-gpio.c
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -61,6 +61,9 @@ struct fsi_master_gpio {
struct gpio_desc *gpio_mux; /* Mux control */
};
+#define CREATE_TRACE_POINTS
+#include <trace/events/fsi_master_gpio.h>
+
#define to_fsi_master_gpio(m) container_of(m, struct fsi_master_gpio, master)
struct fsi_gpio_msg {
@@ -128,6 +131,8 @@ static void serial_in(struct fsi_master_gpio *master, struct fsi_gpio_msg *msg,
msg->msg |= ~in_bit & 0x1; /* Data is negative active */
}
msg->bits += num_bits;
+
+ trace_fsi_master_gpio_in(master, num_bits, msg->msg);
}
static void serial_out(struct fsi_master_gpio *master,
@@ -139,6 +144,8 @@ static void serial_out(struct fsi_master_gpio *master,
uint64_t last_bit = ~0;
int next_bit;
+ trace_fsi_master_gpio_out(master, cmd->bits, cmd->msg);
+
if (!cmd->bits) {
dev_warn(master->dev, "trying to output 0 bits\n");
return;
@@ -464,6 +471,8 @@ static int fsi_master_gpio_break(struct fsi_master *_master, int link)
if (link != 0)
return -ENODEV;
+ trace_fsi_master_gpio_break(master);
+
set_sda_output(master, 1);
sda_out(master, 1);
clock_toggle(master, FSI_PRE_BREAK_CLOCKS);
diff --git a/include/trace/events/fsi_master_gpio.h b/include/trace/events/fsi_master_gpio.h
new file mode 100644
index 0000000..11b36c1
--- /dev/null
+++ b/include/trace/events/fsi_master_gpio.h
@@ -0,0 +1,68 @@
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM fsi_master_gpio
+
+#if !defined(_TRACE_FSI_MASTER_GPIO_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_FSI_MASTER_GPIO_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(fsi_master_gpio_in,
+ TP_PROTO(const struct fsi_master_gpio *master, int bits, uint64_t msg),
+ TP_ARGS(master, bits, msg),
+ TP_STRUCT__entry(
+ __field(int, master_idx)
+ __field(int, bits)
+ __field(uint64_t, msg)
+ ),
+ TP_fast_assign(
+ __entry->master_idx = master->master.idx;
+ __entry->bits = bits;
+ __entry->msg = msg & ((1ull<<bits) - 1);
+ ),
+ TP_printk("fsi-gpio%d => %0*llx[%d]",
+ __entry->master_idx,
+ (__entry->bits + 3) / 4,
+ __entry->msg,
+ __entry->bits
+ )
+);
+
+TRACE_EVENT(fsi_master_gpio_out,
+ TP_PROTO(const struct fsi_master_gpio *master, int bits, uint64_t msg),
+ TP_ARGS(master, bits, msg),
+ TP_STRUCT__entry(
+ __field(int, master_idx)
+ __field(int, bits)
+ __field(uint64_t, msg)
+ ),
+ TP_fast_assign(
+ __entry->master_idx = master->master.idx;
+ __entry->bits = bits;
+ __entry->msg = msg & ((1ull<<bits) - 1);
+ ),
+ TP_printk("fsi-gpio%d <= %0*llx[%d]",
+ __entry->master_idx,
+ (__entry->bits + 3) / 4,
+ __entry->msg,
+ __entry->bits
+ )
+);
+
+TRACE_EVENT(fsi_master_gpio_break,
+ TP_PROTO(const struct fsi_master_gpio *master),
+ TP_ARGS(master),
+ TP_STRUCT__entry(
+ __field(int, master_idx)
+ ),
+ TP_fast_assign(
+ __entry->master_idx = master->master.idx;
+ ),
+ TP_printk("fsi-gpio%d ----break---",
+ __entry->master_idx
+ )
+);
+
+#endif /* _TRACE_FSI_MASTER_GPIO_H */
+
+#include <trace/define_trace.h>
--
1.8.2.2
--
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 related
* [PATCH v5 19/23] drivers/fsi: Add GPIO based FSI master
From: Christopher Bostic @ 2017-04-05 2:06 UTC (permalink / raw)
To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
linux-arm-kernel
Cc: Chris Bostic, joel, linux-kernel, andrew, alistair, benh,
Edward A . James, Jeremy Kerr
In-Reply-To: <20170405020607.79939-1-cbostic@linux.vnet.ibm.com>
From: Chris Bostic <cbostic@linux.vnet.ibm.com>
Implement a FSI master using GPIO. Will generate FSI protocol for
read and write commands to particular addresses. Sends master command
and waits for and decodes a slave response.
Includes changes from Edward A. James <eajames@us.ibm.com> and Jeremy
Kerr <jk@ozlabs.org>.
Signed-off-by: Edward A. James <eajames@us.ibm.com>
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Chris Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
drivers/fsi/Kconfig | 11 +
drivers/fsi/Makefile | 1 +
drivers/fsi/fsi-master-gpio.c | 610 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 622 insertions(+)
create mode 100644 drivers/fsi/fsi-master-gpio.c
diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index 04c1a0e..9cf8345 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -9,4 +9,15 @@ config FSI
---help---
FSI - the FRU Support Interface - is a simple bus for low-level
access to POWER-based hardware.
+
+if FSI
+
+config FSI_MASTER_GPIO
+ tristate "GPIO-based FSI master"
+ depends on FSI && GPIOLIB
+ ---help---
+ This option enables a FSI master driver using GPIO lines.
+
+endif
+
endmenu
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index db0e5e7..ed28ac0 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_FSI) += fsi-core.o
+obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
new file mode 100644
index 0000000..9fedfaf
--- /dev/null
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -0,0 +1,610 @@
+/*
+ * A FSI master controller, using a simple GPIO bit-banging interface
+ */
+
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fsi.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "fsi-master.h"
+
+#define FSI_GPIO_STD_DLY 1 /* Standard pin delay in nS */
+#define FSI_ECHO_DELAY_CLOCKS 16 /* Number clocks for echo delay */
+#define FSI_PRE_BREAK_CLOCKS 50 /* Number clocks to prep for break */
+#define FSI_BREAK_CLOCKS 256 /* Number of clocks to issue break */
+#define FSI_POST_BREAK_CLOCKS 16000 /* Number clocks to set up cfam */
+#define FSI_INIT_CLOCKS 5000 /* Clock out any old data */
+#define FSI_GPIO_STD_DELAY 10 /* Standard GPIO delay in nS */
+ /* todo: adjust down as low as */
+ /* possible or eliminate */
+#define FSI_GPIO_CMD_DPOLL 0x2
+#define FSI_GPIO_CMD_TERM 0x3f
+#define FSI_GPIO_CMD_ABS_AR 0x4
+
+#define FSI_GPIO_DPOLL_CLOCKS 100 /* < 21 will cause slave to hang */
+
+/* Bus errors */
+#define FSI_GPIO_ERR_BUSY 1 /* Slave stuck in busy state */
+#define FSI_GPIO_RESP_ERRA 2 /* Any (misc) Error */
+#define FSI_GPIO_RESP_ERRC 3 /* Slave reports master CRC error */
+#define FSI_GPIO_MTOE 4 /* Master time out error */
+#define FSI_GPIO_CRC_INVAL 5 /* Master reports slave CRC error */
+
+/* Normal slave responses */
+#define FSI_GPIO_RESP_BUSY 1
+#define FSI_GPIO_RESP_ACK 0
+#define FSI_GPIO_RESP_ACKD 4
+
+#define FSI_GPIO_MAX_BUSY 100
+#define FSI_GPIO_MTOE_COUNT 1000
+#define FSI_GPIO_DRAIN_BITS 20
+#define FSI_GPIO_CRC_SIZE 4
+#define FSI_GPIO_MSG_ID_SIZE 2
+#define FSI_GPIO_MSG_RESPID_SIZE 2
+#define FSI_GPIO_PRIME_SLAVE_CLOCKS 100
+
+static DEFINE_SPINLOCK(fsi_gpio_cmd_lock); /* lock around fsi commands */
+
+struct fsi_master_gpio {
+ struct fsi_master master;
+ struct device *dev;
+ struct gpio_desc *gpio_clk;
+ struct gpio_desc *gpio_data;
+ struct gpio_desc *gpio_trans; /* Voltage translator */
+ struct gpio_desc *gpio_enable; /* FSI enable */
+ struct gpio_desc *gpio_mux; /* Mux control */
+};
+
+#define to_fsi_master_gpio(m) container_of(m, struct fsi_master_gpio, master)
+
+struct fsi_gpio_msg {
+ uint64_t msg;
+ uint8_t bits;
+};
+
+static void clock_toggle(struct fsi_master_gpio *master, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ ndelay(FSI_GPIO_STD_DLY);
+ gpiod_set_value(master->gpio_clk, 0);
+ ndelay(FSI_GPIO_STD_DLY);
+ gpiod_set_value(master->gpio_clk, 1);
+ }
+}
+
+static int sda_in(struct fsi_master_gpio *master)
+{
+ int in;
+
+ ndelay(FSI_GPIO_STD_DLY);
+ in = gpiod_get_value(master->gpio_data);
+ return in ? 1 : 0;
+}
+
+static void sda_out(struct fsi_master_gpio *master, int value)
+{
+ gpiod_set_value(master->gpio_data, value);
+}
+
+static void set_sda_input(struct fsi_master_gpio *master)
+{
+ gpiod_direction_input(master->gpio_data);
+ if (master->gpio_trans)
+ gpiod_set_value(master->gpio_trans, 0);
+}
+
+static void set_sda_output(struct fsi_master_gpio *master, int value)
+{
+ if (master->gpio_trans)
+ gpiod_set_value(master->gpio_trans, 1);
+ gpiod_direction_output(master->gpio_data, value);
+}
+
+static void clock_zeros(struct fsi_master_gpio *master, int count)
+{
+ set_sda_output(master, 1);
+ clock_toggle(master, count);
+}
+
+static void serial_in(struct fsi_master_gpio *master, struct fsi_gpio_msg *msg,
+ uint8_t num_bits)
+{
+ uint8_t bit, in_bit;
+
+ set_sda_input(master);
+
+ for (bit = 0; bit < num_bits; bit++) {
+ clock_toggle(master, 1);
+ in_bit = sda_in(master);
+ msg->msg <<= 1;
+ msg->msg |= ~in_bit & 0x1; /* Data is negative active */
+ }
+ msg->bits += num_bits;
+}
+
+static void serial_out(struct fsi_master_gpio *master,
+ const struct fsi_gpio_msg *cmd)
+{
+ uint8_t bit;
+ uint64_t msg = ~cmd->msg; /* Data is negative active */
+ uint64_t sda_mask = 0x1ULL << (cmd->bits - 1);
+ uint64_t last_bit = ~0;
+ int next_bit;
+
+ if (!cmd->bits) {
+ dev_warn(master->dev, "trying to output 0 bits\n");
+ return;
+ }
+ set_sda_output(master, 0);
+
+ /* Send the start bit */
+ sda_out(master, 0);
+ clock_toggle(master, 1);
+
+ /* Send the message */
+ for (bit = 0; bit < cmd->bits; bit++) {
+ next_bit = (msg & sda_mask) >> (cmd->bits - 1);
+ if (last_bit ^ next_bit) {
+ sda_out(master, next_bit);
+ last_bit = next_bit;
+ }
+ clock_toggle(master, 1);
+ msg <<= 1;
+ }
+}
+
+static void msg_push_bits(struct fsi_gpio_msg *msg, uint64_t data, int bits)
+{
+ msg->msg <<= bits;
+ msg->msg |= data & ((1ull << bits) - 1);
+ msg->bits += bits;
+}
+
+static void msg_push_crc(struct fsi_gpio_msg *msg)
+{
+ uint8_t crc;
+ int top;
+
+ top = msg->bits & 0x3;
+
+ /* start bit, and any non-aligned top bits */
+ crc = fsi_crc4(0,
+ 1 << top | msg->msg >> (msg->bits - top),
+ top + 1);
+
+ /* aligned bits */
+ crc = fsi_crc4(crc, msg->msg, msg->bits - top);
+
+ msg_push_bits(msg, crc, 4);
+}
+
+static void build_abs_ar_command(struct fsi_gpio_msg *cmd,
+ uint8_t id, uint32_t addr, size_t size, const void *data)
+{
+ bool write = !!data;
+ uint8_t ds;
+ int i;
+
+ cmd->bits = 0;
+ cmd->msg = 0;
+
+ msg_push_bits(cmd, id, 2);
+ msg_push_bits(cmd, FSI_GPIO_CMD_ABS_AR, 3);
+ msg_push_bits(cmd, write ? 0 : 1, 1);
+
+ /*
+ * The read/write size is encoded in the lower bits of the address
+ * (as it must be naturally-aligned), and the following ds bit.
+ *
+ * size addr:1 addr:0 ds
+ * 1 x x 0
+ * 2 x 0 1
+ * 4 0 1 1
+ *
+ */
+ ds = size > 1 ? 1 : 0;
+ addr &= ~(size - 1);
+ if (size == 4)
+ addr |= 1;
+
+ msg_push_bits(cmd, addr & ((1 << 21) - 1), 21);
+ msg_push_bits(cmd, ds, 1);
+ for (i = 0; write && i < size; i++)
+ msg_push_bits(cmd, ((uint8_t *)data)[i], 8);
+
+ msg_push_crc(cmd);
+}
+
+static void build_dpoll_command(struct fsi_gpio_msg *cmd, uint8_t slave_id)
+{
+ cmd->bits = 0;
+ cmd->msg = 0;
+
+ msg_push_bits(cmd, slave_id, 2);
+ msg_push_bits(cmd, FSI_GPIO_CMD_DPOLL, 3);
+ msg_push_crc(cmd);
+}
+
+static void echo_delay(struct fsi_master_gpio *master)
+{
+ set_sda_output(master, 1);
+ clock_toggle(master, FSI_ECHO_DELAY_CLOCKS);
+}
+
+static void build_term_command(struct fsi_gpio_msg *cmd, uint8_t slave_id)
+{
+ cmd->bits = 0;
+ cmd->msg = 0;
+
+ msg_push_bits(cmd, slave_id, 2);
+ msg_push_bits(cmd, FSI_GPIO_CMD_TERM, 6);
+ msg_push_crc(cmd);
+}
+
+/*
+ * Store information on master errors so handler can detect and clean
+ * up the bus
+ */
+static void fsi_master_gpio_error(struct fsi_master_gpio *master, int error)
+{
+
+}
+
+static int read_one_response(struct fsi_master_gpio *master,
+ uint8_t data_size, struct fsi_gpio_msg *msgp, uint8_t *tagp)
+{
+ struct fsi_gpio_msg msg;
+ uint8_t id, tag;
+ uint32_t crc;
+ int i;
+
+ /* wait for the start bit */
+ for (i = 0; i < FSI_GPIO_MTOE_COUNT; i++) {
+ msg.bits = 0;
+ msg.msg = 0;
+ serial_in(master, &msg, 1);
+ if (msg.msg)
+ break;
+ }
+ if (i >= FSI_GPIO_MTOE_COUNT) {
+ dev_dbg(master->dev,
+ "Master time out waiting for response\n");
+ fsi_master_gpio_error(master, FSI_GPIO_MTOE);
+ return -EIO;
+ }
+
+ msg.bits = 0;
+ msg.msg = 0;
+
+ /* Read slave ID & response tag */
+ serial_in(master, &msg, 4);
+
+ id = (msg.msg >> FSI_GPIO_MSG_RESPID_SIZE) & 0x3;
+ tag = msg.msg & 0x3;
+
+ /* if we have an ACK, and we're expecting data, clock the
+ * data in too
+ */
+ if (tag == FSI_GPIO_RESP_ACK && data_size)
+ serial_in(master, &msg, data_size * 8);
+
+ /* read CRC */
+ serial_in(master, &msg, FSI_GPIO_CRC_SIZE);
+
+ /* we have a whole message now; check CRC */
+ crc = fsi_crc4(0, 1, 1);
+ crc = fsi_crc4(crc, msg.msg, msg.bits);
+ if (crc) {
+ dev_dbg(master->dev, "ERR response CRC\n");
+ fsi_master_gpio_error(master, FSI_GPIO_CRC_INVAL);
+ return -EIO;
+ }
+
+ if (msgp)
+ *msgp = msg;
+ if (tagp)
+ *tagp = tag;
+
+ return 0;
+}
+
+static int issue_term(struct fsi_master_gpio *master, uint8_t slave)
+{
+ struct fsi_gpio_msg cmd;
+ uint8_t tag;
+ int rc;
+
+ build_term_command(&cmd, slave);
+ serial_out(master, &cmd);
+ echo_delay(master);
+
+ rc = read_one_response(master, 0, NULL, &tag);
+ if (rc) {
+ dev_err(master->dev,
+ "TERM failed; lost communication with slave\n");
+ return -EIO;
+ } else if (tag != FSI_GPIO_RESP_ACK) {
+ dev_err(master->dev, "TERM failed; response %d\n", tag);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int poll_for_response(struct fsi_master_gpio *master,
+ uint8_t slave, uint8_t size, void *data)
+{
+ struct fsi_gpio_msg response, cmd;
+ int busy_count = 0, rc, i;
+ uint8_t tag;
+
+retry:
+ rc = read_one_response(master, size, &response, &tag);
+ if (rc)
+ return rc;
+
+ switch (tag) {
+ case FSI_GPIO_RESP_ACK:
+ if (size && data) {
+ uint64_t val = response.msg;
+ /* clear crc & mask */
+ val >>= 4;
+ val &= (1ull << (size * 8)) - 1;
+
+ for (i = 0; i < size; i++) {
+ ((uint8_t *)data)[size-i-1] =
+ val & 0xff;
+ val >>= 8;
+ }
+ }
+ break;
+ case FSI_GPIO_RESP_BUSY:
+ /*
+ * Its necessary to clock slave before issuing
+ * d-poll, not indicated in the hardware protocol
+ * spec. < 20 clocks causes slave to hang, 21 ok.
+ */
+ clock_zeros(master, FSI_GPIO_DPOLL_CLOCKS);
+ if (busy_count++ < FSI_GPIO_MAX_BUSY) {
+ build_dpoll_command(&cmd, slave);
+ serial_out(master, &cmd);
+ echo_delay(master);
+ goto retry;
+ }
+ dev_warn(master->dev,
+ "ERR slave is stuck in busy state, issuing TERM\n");
+ issue_term(master, slave);
+ rc = -EIO;
+ break;
+
+ case FSI_GPIO_RESP_ERRA:
+ case FSI_GPIO_RESP_ERRC:
+ dev_dbg(master->dev, "ERR%c received: 0x%x\n",
+ tag == FSI_GPIO_RESP_ERRA ? 'A' : 'C',
+ (int)response.msg);
+ fsi_master_gpio_error(master, response.msg);
+ rc = -EIO;
+ break;
+ }
+
+ /* Clock the slave enough to be ready for next operation */
+ clock_zeros(master, FSI_GPIO_PRIME_SLAVE_CLOCKS);
+ return rc;
+}
+
+static int fsi_master_gpio_xfer(struct fsi_master_gpio *master, uint8_t slave,
+ struct fsi_gpio_msg *cmd, size_t resp_len, void *resp)
+{
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&fsi_gpio_cmd_lock, flags);
+ serial_out(master, cmd);
+ echo_delay(master);
+ rc = poll_for_response(master, slave, resp_len, resp);
+ spin_unlock_irqrestore(&fsi_gpio_cmd_lock, flags);
+
+ return rc;
+}
+
+static int fsi_master_gpio_read(struct fsi_master *_master, int link,
+ uint8_t id, uint32_t addr, void *val, size_t size)
+{
+ struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+ struct fsi_gpio_msg cmd;
+
+ if (link != 0)
+ return -ENODEV;
+
+ build_abs_ar_command(&cmd, id, addr, size, NULL);
+ return fsi_master_gpio_xfer(master, id, &cmd, size, val);
+}
+
+static int fsi_master_gpio_write(struct fsi_master *_master, int link,
+ uint8_t id, uint32_t addr, const void *val, size_t size)
+{
+ struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+ struct fsi_gpio_msg cmd;
+
+ if (link != 0)
+ return -ENODEV;
+
+ build_abs_ar_command(&cmd, id, addr, size, val);
+ return fsi_master_gpio_xfer(master, id, &cmd, 0, NULL);
+}
+
+static int fsi_master_gpio_term(struct fsi_master *_master,
+ int link, uint8_t id)
+{
+ struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+ struct fsi_gpio_msg cmd;
+
+ if (link != 0)
+ return -ENODEV;
+
+ build_term_command(&cmd, id);
+ return fsi_master_gpio_xfer(master, id, &cmd, 0, NULL);
+}
+
+/*
+ * Issue a break command on link
+ */
+static int fsi_master_gpio_break(struct fsi_master *_master, int link)
+{
+ struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+
+ if (link != 0)
+ return -ENODEV;
+
+ set_sda_output(master, 1);
+ sda_out(master, 1);
+ clock_toggle(master, FSI_PRE_BREAK_CLOCKS);
+ sda_out(master, 0);
+ clock_toggle(master, FSI_BREAK_CLOCKS);
+ echo_delay(master);
+ sda_out(master, 1);
+ clock_toggle(master, FSI_POST_BREAK_CLOCKS);
+
+ /* Wait for logic reset to take effect */
+ udelay(200);
+
+ return 0;
+}
+
+static void fsi_master_gpio_init(struct fsi_master_gpio *master)
+{
+ if (master->gpio_mux)
+ gpiod_direction_output(master->gpio_mux, 1);
+ if (master->gpio_trans)
+ gpiod_direction_output(master->gpio_trans, 1);
+ if (master->gpio_enable)
+ gpiod_direction_output(master->gpio_enable, 1);
+ gpiod_direction_output(master->gpio_clk, 1);
+ gpiod_direction_output(master->gpio_data, 1);
+
+ /* todo: evaluate if clocks can be reduced */
+ clock_zeros(master, FSI_INIT_CLOCKS);
+}
+
+static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
+{
+ struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+
+ if (link != 0)
+ return -ENODEV;
+ if (master->gpio_enable)
+ gpiod_set_value(master->gpio_enable, 1);
+
+ return 0;
+}
+
+static void fsi_master_gpio_release(struct device *dev)
+{
+}
+
+static int fsi_master_gpio_probe(struct platform_device *pdev)
+{
+ struct fsi_master_gpio *master;
+ struct gpio_desc *gpio;
+
+ master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return -ENOMEM;
+
+ master->dev = &pdev->dev;
+ master->master.dev.parent = master->dev;
+ master->master.dev.release = fsi_master_gpio_release;
+
+ gpio = devm_gpiod_get(&pdev->dev, "clock", 0);
+ if (IS_ERR(gpio)) {
+ dev_err(&pdev->dev, "failed to get clock gpio\n");
+ return PTR_ERR(gpio);
+ }
+ master->gpio_clk = gpio;
+
+ gpio = devm_gpiod_get(&pdev->dev, "data", 0);
+ if (IS_ERR(gpio)) {
+ dev_err(&pdev->dev, "failed to get data gpio\n");
+ return PTR_ERR(gpio);
+ }
+ master->gpio_data = gpio;
+
+ /* Optional GPIOs */
+ gpio = devm_gpiod_get_optional(&pdev->dev, "trans", 0);
+ if (IS_ERR(gpio)) {
+ dev_err(&pdev->dev, "failed to get trans gpio\n");
+ return PTR_ERR(gpio);
+ }
+ master->gpio_trans = gpio;
+
+ gpio = devm_gpiod_get_optional(&pdev->dev, "enable", 0);
+ if (IS_ERR(gpio)) {
+ dev_err(&pdev->dev, "failed to get enable gpio\n");
+ return PTR_ERR(gpio);
+ }
+ master->gpio_enable = gpio;
+
+ gpio = devm_gpiod_get_optional(&pdev->dev, "mux", 0);
+ if (IS_ERR(gpio)) {
+ dev_err(&pdev->dev, "failed to get mux gpio\n");
+ return PTR_ERR(gpio);
+ }
+ master->gpio_mux = gpio;
+
+ master->master.n_links = 1;
+ master->master.read = fsi_master_gpio_read;
+ master->master.write = fsi_master_gpio_write;
+ master->master.term = fsi_master_gpio_term;
+ master->master.send_break = fsi_master_gpio_break;
+ master->master.link_enable = fsi_master_gpio_link_enable;
+ platform_set_drvdata(pdev, master);
+
+ fsi_master_gpio_init(master);
+
+ fsi_master_register(&master->master);
+
+ return 0;
+}
+
+
+static int fsi_master_gpio_remove(struct platform_device *pdev)
+{
+ struct fsi_master_gpio *master = platform_get_drvdata(pdev);
+
+ devm_gpiod_put(&pdev->dev, master->gpio_clk);
+ devm_gpiod_put(&pdev->dev, master->gpio_data);
+ if (master->gpio_trans)
+ devm_gpiod_put(&pdev->dev, master->gpio_trans);
+ if (master->gpio_enable)
+ devm_gpiod_put(&pdev->dev, master->gpio_enable);
+ if (master->gpio_mux)
+ devm_gpiod_put(&pdev->dev, master->gpio_mux);
+ fsi_master_unregister(&master->master);
+
+ return 0;
+}
+
+static const struct of_device_id fsi_master_gpio_match[] = {
+ { .compatible = "fsi-master-gpio" },
+ { },
+};
+
+static struct platform_driver fsi_master_gpio_driver = {
+ .driver = {
+ .name = "fsi-master-gpio",
+ .of_match_table = fsi_master_gpio_match,
+ },
+ .probe = fsi_master_gpio_probe,
+ .remove = fsi_master_gpio_remove,
+};
+
+module_platform_driver(fsi_master_gpio_driver);
+MODULE_LICENSE("GPL");
--
1.8.2.2
^ permalink raw reply related
* [PATCH v5 18/23] drivers/fsi: Document FSI master sysfs files in ABI
From: Christopher Bostic @ 2017-04-05 2:06 UTC (permalink / raw)
To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
linux-arm-kernel
Cc: Chris Bostic, joel, linux-kernel, andrew, alistair, benh
In-Reply-To: <20170405020607.79939-1-cbostic@linux.vnet.ibm.com>
From: Chris Bostic <cbostic@linux.vnet.ibm.com>
Add info for sysfs scan file in Documentaiton ABI/testing
Signed-off-by: Chris Bostic <cbostic@linux.vnet.ibm.com>
---
Documentation/ABI/testing/sysfs-bus-fsi | 6 ++++++
1 file changed, 6 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-fsi
diff --git a/Documentation/ABI/testing/sysfs-bus-fsi b/Documentation/ABI/testing/sysfs-bus-fsi
new file mode 100644
index 0000000..dfcbc1b
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-fsi
@@ -0,0 +1,6 @@
+What: /sys/bus/platform/devices/fsi-master/scan
+KernelVersion: 4.9
+Contact: cbostic@us.ibm.com
+Description:
+ Initiates a FSI master scan for all connected
+ slave devices on its links.
--
1.8.2.2
^ permalink raw reply related
* [PATCH v5 17/23] drivers/fsi: Add error handling for slave communication errors
From: Christopher Bostic @ 2017-04-05 2:06 UTC (permalink / raw)
To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, rostedt-nx8X9YLhiw1AfugRpC6u6w,
mingo-H+wXaHxf7aLQT0dZR+AlfA,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Jeremy Kerr, joel-U3u1mxZcP9KHXe+LvDLADg,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, andrew-zrmu5oMJ5Fs,
alistair-Y4h6yKqj69EXC2x5gXVKYQ,
benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r, Chris Bostic
In-Reply-To: <20170405020607.79939-1-cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
From: Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>
This change implements error handling in the FSI core, by cleaning up
and retrying failed operations, using the SISC, TERM and BREAK
facilities.
Signed-off-by: Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>
Signed-off-by: Chris Bostic <cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
---
drivers/fsi/fsi-core.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 114 insertions(+), 7 deletions(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 09becec..747d0e3 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -44,7 +44,9 @@
/*
* FSI slave engine control register offsets
*/
-#define FSI_SMODE 0x0 /* R/W: Mode register */
+#define FSI_SMODE 0x0 /* R/W: Mode register */
+#define FSI_SISC 0x8 /* R/W: Interrupt condition */
+#define FSI_SSTAT 0x14 /* R : Slave status */
/*
* SMODE fields
@@ -75,10 +77,14 @@ struct fsi_slave {
#define to_fsi_master(d) container_of(d, struct fsi_master, dev)
#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
+static const int slave_retries = 2;
+static int discard_errors;
+
static int fsi_master_read(struct fsi_master *master, int link,
uint8_t slave_id, uint32_t addr, void *val, size_t size);
static int fsi_master_write(struct fsi_master *master, int link,
uint8_t slave_id, uint32_t addr, const void *val, size_t size);
+static int fsi_master_break(struct fsi_master *master, int link);
/* FSI endpoint-device support */
@@ -180,32 +186,131 @@ static int fsi_slave_calc_addr(struct fsi_slave *slave, uint32_t *addrp,
return 0;
}
+int fsi_slave_report_and_clear_errors(struct fsi_slave *slave)
+{
+ struct fsi_master *master = slave->master;
+ uint32_t irq, stat;
+ int rc, link;
+ uint8_t id;
+
+ link = slave->link;
+ id = slave->id;
+
+ rc = fsi_master_read(master, link, id, FSI_SLAVE_BASE + FSI_SISC,
+ &irq, sizeof(irq));
+ if (rc)
+ return rc;
+
+ rc = fsi_master_read(master, link, id, FSI_SLAVE_BASE + FSI_SSTAT,
+ &stat, sizeof(stat));
+ if (rc)
+ return rc;
+
+ dev_info(&slave->dev, "status: 0x%08x, sisc: 0x%08x\n",
+ be32_to_cpu(stat), be32_to_cpu(irq));
+
+ /* clear interrupts */
+ return fsi_master_write(master, link, id, FSI_SLAVE_BASE + FSI_SISC,
+ &irq, sizeof(irq));
+}
+
+static int fsi_slave_set_smode(struct fsi_master *master, int link, int id);
+
+int fsi_slave_handle_error(struct fsi_slave *slave, bool write, uint32_t addr,
+ size_t size)
+{
+ struct fsi_master *master = slave->master;
+ int rc, link;
+ uint32_t reg;
+ uint8_t id;
+
+ if (discard_errors)
+ return -1;
+
+ link = slave->link;
+ id = slave->id;
+
+ dev_dbg(&slave->dev, "handling error on %s to 0x%08x[%zd]",
+ write ? "write" : "read", addr, size);
+
+ /* try a simple clear of error conditions, which may fail if we've lost
+ * communication with the slave
+ */
+ rc = fsi_slave_report_and_clear_errors(slave);
+ if (!rc)
+ return 0;
+
+ /* send a TERM and retry */
+ if (master->term) {
+ rc = master->term(master, link, id);
+ if (!rc) {
+ rc = fsi_master_read(master, link, id, 0,
+ ®, sizeof(reg));
+ if (!rc)
+ rc = fsi_slave_report_and_clear_errors(slave);
+ if (!rc)
+ return 0;
+ }
+ }
+
+ /* getting serious, reset the slave via BREAK */
+ rc = fsi_master_break(master, link);
+ if (rc)
+ return rc;
+
+ rc = fsi_slave_set_smode(master, link, id);
+ if (rc)
+ return rc;
+
+ return fsi_slave_report_and_clear_errors(slave);
+}
+
int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
void *val, size_t size)
{
uint8_t id = slave->id;
- int rc;
+ int rc, err_rc, i;
rc = fsi_slave_calc_addr(slave, &addr, &id);
if (rc)
return rc;
- return fsi_master_read(slave->master, slave->link, id,
- addr, val, size);
+ for (i = 0; i < slave_retries; i++) {
+ rc = fsi_master_read(slave->master, slave->link,
+ id, addr, val, size);
+ if (!rc)
+ break;
+
+ err_rc = fsi_slave_handle_error(slave, false, addr, size);
+ if (err_rc)
+ break;
+ }
+
+ return rc;
}
int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
const void *val, size_t size)
{
uint8_t id = slave->id;
- int rc;
+ int rc, err_rc, i;
rc = fsi_slave_calc_addr(slave, &addr, &id);
if (rc)
return rc;
- return fsi_master_write(slave->master, slave->link, id,
- addr, val, size);
+ for (i = 0; i < slave_retries; i++) {
+ rc = fsi_master_write(slave->master, slave->link,
+ id, addr, val, size);
+ if (!rc)
+ break;
+
+ err_rc = fsi_slave_handle_error(slave, true, addr, size);
+ if (err_rc)
+ break;
+ }
+
+ return rc;
}
extern int fsi_slave_claim_range(struct fsi_slave *slave,
@@ -765,3 +870,5 @@ static void fsi_exit(void)
module_init(fsi_init);
module_exit(fsi_exit);
+module_param(discard_errors, int, 0664);
+MODULE_PARM_DESC(discard_errors, "Don't invoke error handling on bus accesses");
--
1.8.2.2
--
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 related
* [PATCH v5 16/23] drivers/fsi: Add tracepoints for low-level operations
From: Christopher Bostic @ 2017-04-05 2:06 UTC (permalink / raw)
To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
linux-arm-kernel
Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh,
Chris Bostic
In-Reply-To: <20170405020607.79939-1-cbostic@linux.vnet.ibm.com>
From: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Chris Bostic <cbostic@linux.vnet.ibm.com>
---
drivers/fsi/fsi-core.c | 27 +++++++---
include/trace/events/fsi.h | 127 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 148 insertions(+), 6 deletions(-)
create mode 100644 include/trace/events/fsi.h
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index f7e55e7..09becec 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -21,6 +21,9 @@
#include "fsi-master.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/fsi.h>
+
#define FSI_SLAVE_CONF_NEXT_MASK 0x80000000
#define FSI_SLAVE_CONF_SLOTS_MASK 0x00ff0000
#define FSI_SLAVE_CONF_SLOTS_SHIFT 16
@@ -541,11 +544,16 @@ static int fsi_master_read(struct fsi_master *master, int link,
{
int rc;
+ trace_fsi_master_read(master, link, slave_id, addr, size);
+
rc = fsi_check_access(addr, size);
- if (rc)
- return rc;
+ if (!rc)
+ rc = master->read(master, link, slave_id, addr, val, size);
+
+ trace_fsi_master_rw_result(master, link, slave_id, addr, size,
+ false, val, rc);
- return master->read(master, link, slave_id, addr, val, size);
+ return rc;
}
static int fsi_master_write(struct fsi_master *master, int link,
@@ -553,11 +561,16 @@ static int fsi_master_write(struct fsi_master *master, int link,
{
int rc;
+ trace_fsi_master_write(master, link, slave_id, addr, size, val);
+
rc = fsi_check_access(addr, size);
- if (rc)
- return rc;
+ if (!rc)
+ rc = master->write(master, link, slave_id, addr, val, size);
- return master->write(master, link, slave_id, addr, val, size);
+ trace_fsi_master_rw_result(master, link, slave_id, addr, size,
+ true, val, rc);
+
+ return rc;
}
static int fsi_master_link_enable(struct fsi_master *master, int link)
@@ -573,6 +586,8 @@ static int fsi_master_link_enable(struct fsi_master *master, int link)
*/
static int fsi_master_break(struct fsi_master *master, int link)
{
+ trace_fsi_master_break(master, link);
+
if (master->send_break)
return master->send_break(master, link);
diff --git a/include/trace/events/fsi.h b/include/trace/events/fsi.h
new file mode 100644
index 0000000..ac74a30
--- /dev/null
+++ b/include/trace/events/fsi.h
@@ -0,0 +1,127 @@
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM fsi
+
+#if !defined(_TRACE_FSI_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_FSI_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(fsi_master_read,
+ TP_PROTO(const struct fsi_master *master, int link, int id,
+ uint32_t addr, size_t size),
+ TP_ARGS(master, link, id, addr, size),
+ TP_STRUCT__entry(
+ __field(int, master_idx)
+ __field(int, link)
+ __field(int, id)
+ __field(__u32, addr)
+ __field(size_t, size)
+ ),
+ TP_fast_assign(
+ __entry->master_idx = master->idx;
+ __entry->link = link;
+ __entry->id = id;
+ __entry->addr = addr;
+ __entry->size = size;
+ ),
+ TP_printk("fsi%d:%02d:%02d %08x[%zd]",
+ __entry->master_idx,
+ __entry->link,
+ __entry->id,
+ __entry->addr,
+ __entry->size
+ )
+);
+
+TRACE_EVENT(fsi_master_write,
+ TP_PROTO(const struct fsi_master *master, int link, int id,
+ uint32_t addr, size_t size, const void *data),
+ TP_ARGS(master, link, id, addr, size, data),
+ TP_STRUCT__entry(
+ __field(int, master_idx)
+ __field(int, link)
+ __field(int, id)
+ __field(__u32, addr)
+ __field(size_t, size)
+ __field(__u32, data)
+ ),
+ TP_fast_assign(
+ __entry->master_idx = master->idx;
+ __entry->link = link;
+ __entry->id = id;
+ __entry->addr = addr;
+ __entry->size = size;
+ __entry->data = 0;
+ memcpy(&__entry->data, data, size);
+ ),
+ TP_printk("fsi%d:%02d:%02d %08x[%zd] <= {%*ph}",
+ __entry->master_idx,
+ __entry->link,
+ __entry->id,
+ __entry->addr,
+ __entry->size,
+ __entry->size, &__entry->data
+ )
+);
+
+TRACE_EVENT(fsi_master_rw_result,
+ TP_PROTO(const struct fsi_master *master, int link, int id,
+ uint32_t addr, size_t size,
+ bool write, const void *data, int ret),
+ TP_ARGS(master, link, id, addr, size, write, data, ret),
+ TP_STRUCT__entry(
+ __field(int, master_idx)
+ __field(int, link)
+ __field(int, id)
+ __field(__u32, addr)
+ __field(size_t, size)
+ __field(bool, write)
+ __field(__u32, data)
+ __field(int, ret)
+ ),
+ TP_fast_assign(
+ __entry->master_idx = master->idx;
+ __entry->link = link;
+ __entry->id = id;
+ __entry->addr = addr;
+ __entry->size = size;
+ __entry->write = write;
+ __entry->data = 0;
+ __entry->ret = ret;
+ if (__entry->write || !__entry->ret)
+ memcpy(&__entry->data, data, size);
+ ),
+ TP_printk("fsi%d:%02d:%02d %08x[%zd] %s {%*ph} ret %d",
+ __entry->master_idx,
+ __entry->link,
+ __entry->id,
+ __entry->addr,
+ __entry->size,
+ __entry->write ? "<=" : "=>",
+ __entry->size, &__entry->data,
+ __entry->ret
+ )
+);
+
+TRACE_EVENT(fsi_master_break,
+ TP_PROTO(const struct fsi_master *master, int link),
+ TP_ARGS(master, link),
+ TP_STRUCT__entry(
+ __field(int, master_idx)
+ __field(int, link)
+ ),
+ TP_fast_assign(
+ __entry->master_idx = master->idx;
+ __entry->link = link;
+ ),
+ TP_printk("fsi%d:%d",
+ __entry->master_idx,
+ __entry->link
+ )
+);
+
+
+#endif /* _TRACE_FSI_H */
+
+#include <trace/define_trace.h>
--
1.8.2.2
^ permalink raw reply related
* [PATCH v5 15/23] drivers/fsi: expose direct-access slave API
From: Christopher Bostic @ 2017-04-05 2:05 UTC (permalink / raw)
To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
linux-arm-kernel
Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh,
Chris Bostic
In-Reply-To: <20170405020607.79939-1-cbostic@linux.vnet.ibm.com>
From: Jeremy Kerr <jk@ozlabs.org>
Allow drivers to access the slave address ranges.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Chris Bostic <cbostic@linux.vnet.ibm.com>
---
drivers/fsi/fsi-core.c | 26 ++++++++++++++++++++------
include/linux/fsi.h | 12 ++++++++++++
2 files changed, 32 insertions(+), 6 deletions(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index f0f0556..f7e55e7 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -76,10 +76,6 @@ static int fsi_master_read(struct fsi_master *master, int link,
uint8_t slave_id, uint32_t addr, void *val, size_t size);
static int fsi_master_write(struct fsi_master *master, int link,
uint8_t slave_id, uint32_t addr, const void *val, size_t size);
-static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
- void *val, size_t size);
-static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
- const void *val, size_t size);
/* FSI endpoint-device support */
@@ -181,7 +177,7 @@ static int fsi_slave_calc_addr(struct fsi_slave *slave, uint32_t *addrp,
return 0;
}
-static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
+int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
void *val, size_t size)
{
uint8_t id = slave->id;
@@ -195,7 +191,7 @@ static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
addr, val, size);
}
-static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
+int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
const void *val, size_t size)
{
uint8_t id = slave->id;
@@ -209,6 +205,24 @@ static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
addr, val, size);
}
+extern int fsi_slave_claim_range(struct fsi_slave *slave,
+ uint32_t addr, uint32_t size)
+{
+ if (addr + size < addr)
+ return -EINVAL;
+
+ if (addr + size > slave->size)
+ return -EINVAL;
+
+ /* todo: check for overlapping claims */
+ return 0;
+}
+
+extern void fsi_slave_release_range(struct fsi_slave *slave,
+ uint32_t addr, uint32_t size)
+{
+}
+
static int fsi_slave_scan(struct fsi_slave *slave)
{
uint32_t engine_addr;
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index 34f1e9a..141fd38 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -66,6 +66,18 @@ struct fsi_driver {
module_driver(__fsi_driver, fsi_driver_register, \
fsi_driver_unregister)
+/* direct slave API */
+extern int fsi_slave_claim_range(struct fsi_slave *slave,
+ uint32_t addr, uint32_t size);
+extern void fsi_slave_release_range(struct fsi_slave *slave,
+ uint32_t addr, uint32_t size);
+extern int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
+ void *val, size_t size);
+extern int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
+ const void *val, size_t size);
+
+
+
extern struct bus_type fsi_bus_type;
#endif /* LINUX_FSI_H */
--
1.8.2.2
^ permalink raw reply related
* [PATCH v5 14/23] drivers/fsi: Add sysfs files for FSI master & slave accesses
From: Christopher Bostic @ 2017-04-05 2:05 UTC (permalink / raw)
To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
linux-arm-kernel
Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh,
Chris Bostic
In-Reply-To: <20170405020607.79939-1-cbostic@linux.vnet.ibm.com>
From: Jeremy Kerr <jk@ozlabs.org>
This change adds a 'raw' file for reads & writes, and a 'term' file for
the TERM command, and a 'break' file for issuing a BREAK.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Chris Bostic <cbostic@linux.vnet.ibm.com>
---
drivers/fsi/fsi-core.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 4359e26..f0f0556 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -294,6 +294,95 @@ static int fsi_slave_scan(struct fsi_slave *slave)
return 0;
}
+static ssize_t fsi_slave_sysfs_raw_read(struct file *file,
+ struct kobject *kobj, struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct fsi_slave *slave = to_fsi_slave(kobj_to_dev(kobj));
+ size_t total_len, read_len;
+ int rc;
+
+ if (off < 0)
+ return -EINVAL;
+
+ if (off > 0xffffffff || count > 0xffffffff || off + count > 0xffffffff)
+ return -EINVAL;
+
+ for (total_len = 0; total_len < count; total_len += read_len) {
+ read_len = min_t(size_t, count, 4);
+ read_len -= off & 0x3;
+
+ rc = fsi_slave_read(slave, off, buf + total_len, read_len);
+ if (rc)
+ return rc;
+
+ off += read_len;
+ }
+
+ return count;
+}
+
+static ssize_t fsi_slave_sysfs_raw_write(struct file *file,
+ struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct fsi_slave *slave = to_fsi_slave(kobj_to_dev(kobj));
+ size_t total_len, write_len;
+ int rc;
+
+ if (off < 0)
+ return -EINVAL;
+
+ if (off > 0xffffffff || count > 0xffffffff || off + count > 0xffffffff)
+ return -EINVAL;
+
+ for (total_len = 0; total_len < count; total_len += write_len) {
+ write_len = min_t(size_t, count, 4);
+ write_len -= off & 0x3;
+
+ rc = fsi_slave_write(slave, off, buf + total_len, write_len);
+ if (rc)
+ return rc;
+
+ off += write_len;
+ }
+
+ return count;
+}
+
+static struct bin_attribute fsi_slave_raw_attr = {
+ .attr = {
+ .name = "raw",
+ .mode = 0600,
+ },
+ .size = 0,
+ .read = fsi_slave_sysfs_raw_read,
+ .write = fsi_slave_sysfs_raw_write,
+};
+
+static ssize_t fsi_slave_sysfs_term_write(struct file *file,
+ struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct fsi_slave *slave = to_fsi_slave(kobj_to_dev(kobj));
+ struct fsi_master *master = slave->master;
+
+ if (!master->term)
+ return -ENODEV;
+
+ master->term(master, slave->link, slave->id);
+ return count;
+}
+
+static struct bin_attribute fsi_slave_term_attr = {
+ .attr = {
+ .name = "term",
+ .mode = 0200,
+ },
+ .size = 0,
+ .write = fsi_slave_sysfs_term_write,
+};
+
/* Encode slave local bus echo delay */
static inline uint32_t fsi_smode_echodly(int x)
{
@@ -409,6 +498,14 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
return rc;
}
+ rc = device_create_bin_file(&slave->dev, &fsi_slave_raw_attr);
+ if (rc)
+ dev_warn(&slave->dev, "failed to create raw attr: %d\n", rc);
+
+ rc = device_create_bin_file(&slave->dev, &fsi_slave_term_attr);
+ if (rc)
+ dev_warn(&slave->dev, "failed to create term attr: %d\n", rc);
+
fsi_slave_scan(slave);
return 0;
}
@@ -523,6 +620,18 @@ static ssize_t master_rescan_store(struct device *dev,
static DEVICE_ATTR(rescan, 0200, NULL, master_rescan_store);
+static ssize_t master_break_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fsi_master *master = to_fsi_master(dev);
+
+ fsi_master_break(master, 0);
+
+ return count;
+}
+
+static DEVICE_ATTR(break, 0200, NULL, master_break_store);
+
int fsi_master_register(struct fsi_master *master)
{
int rc;
@@ -546,6 +655,13 @@ int fsi_master_register(struct fsi_master *master)
return rc;
}
+ rc = device_create_file(&master->dev, &dev_attr_break);
+ if (rc) {
+ device_unregister(&master->dev);
+ ida_simple_remove(&master_ida, master->idx);
+ return rc;
+ }
+
fsi_master_scan(master);
return 0;
--
1.8.2.2
^ permalink raw reply related
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