[U-Boot] [linux-sunxi] [PATCH 1/9] sun6i: Add new p2wi controller driver

Chen-Yu Tsai wens at csie.org
Tue Nov 4 07:31:02 CET 2014


Hi,

On Tue, Nov 4, 2014 at 2:10 PM, Olliver Schinagl <oliver at schinagl.nl> wrote:
> Hy Wens,
>
> This is some really old work, and I'm assuming hans picked it up, as I never
> tested the code (didn't have hardware at the time)

I believe Hans did pick it up for u-boot-sunxi, and now we're just mainlining
it.

> I'll comment as I remember it and hope that helps.
>
> On a side note, do we know what p2wi stands for? I'm guessing 2wi is
> allwinners try at combining the names twi and i2c, as i2c is some philips
> trademark and maybe they think twi is an avr amongst others? so using 2wi is
> save? And the P, 'program twi? Powermanagment twi?

It stands for Push-pull 2 wire interface.

>
> If that is whats happening here, I'd be inclined to rename it to to just
> regular twi, maybe axp_twi if we must?

It's not the same. Normal TWI uses an open-drain setup. Other specifics
may be different as well, like request framing. Also P2WI does not have
the concept of device addresses.

> Olliver
>
>
> On 03-11-14 16:59, Chen-Yu Tsai wrote:
>>
>> Hi,
>>
>> On Mon, Nov 3, 2014 at 11:34 PM, Hans de Goede <hdegoede at redhat.com>
>> wrote:
>>>
>>> From: Oliver Schinagl <oliver at schinagl.nl>
>>>
>>> The A31 uses a new push-pull two wire interface, which features higher
>>> transfer speeds (upto 6 MHz) in theory. While the hardware can burst 8
>>> bytes each time, this driver will only see very little use and thus is
>>> limited to single byte transmission only.
>>>
>>> Signed-off-by: Oliver Schinagl <oliver at schinagl.nl>
>>> Signed-off-by: Hans de Goede <hdegoede at redhat.com>
>>> ---
>>>   arch/arm/cpu/armv7/sunxi/Makefile      |   1 +
>>>   arch/arm/cpu/armv7/sunxi/p2wi.c        | 117
>>> +++++++++++++++++++++++++++
>>>   arch/arm/include/asm/arch-sunxi/gpio.h |   3 +
>>>   arch/arm/include/asm/arch-sunxi/p2wi.h | 140
>>> +++++++++++++++++++++++++++++++++
>>>   4 files changed, 261 insertions(+)
>>>   create mode 100644 arch/arm/cpu/armv7/sunxi/p2wi.c
>>>   create mode 100644 arch/arm/include/asm/arch-sunxi/p2wi.h
>>>
>>> diff --git a/arch/arm/cpu/armv7/sunxi/Makefile
>>> b/arch/arm/cpu/armv7/sunxi/Makefile
>>> index 82dbf76..b3a3601 100644
>>> --- a/arch/arm/cpu/armv7/sunxi/Makefile
>>> +++ b/arch/arm/cpu/armv7/sunxi/Makefile
>>> @@ -13,6 +13,7 @@ obj-y += clock.o
>>>   obj-y  += pinmux.o
>>>   obj-$(CONFIG_MACH_SUN6I)       += prcm.o
>>>   obj-$(CONFIG_MACH_SUN8I)       += prcm.o
>>> +obj-$(CONFIG_MACH_SUN6I)       += p2wi.o
>>>   obj-$(CONFIG_MACH_SUN4I)       += clock_sun4i.o
>>>   obj-$(CONFIG_MACH_SUN5I)       += clock_sun4i.o
>>>   obj-$(CONFIG_MACH_SUN6I)       += clock_sun6i.o
>>> diff --git a/arch/arm/cpu/armv7/sunxi/p2wi.c
>>> b/arch/arm/cpu/armv7/sunxi/p2wi.c
>>> new file mode 100644
>>> index 0000000..10df348
>>> --- /dev/null
>>> +++ b/arch/arm/cpu/armv7/sunxi/p2wi.c
>>> @@ -0,0 +1,117 @@
>>> +/*
>>> + * Sunxi A31 Power Management Unit
>>> + *
>>> + * (C) Copyright 2013 Oliver Schinagl <oliver at schinagl.nl>
>>> + * http://linux-sunxi.org
>>> + *
>>> + * Based on sun6i sources and earlier U-Boot Allwiner A10 SPL work
>>> + *
>>> + * (C) Copyright 2006-2013
>>> + * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
>>> + * Berg Xing <bergxing at allwinnertech.com>
>>> + * Tom Cubie <tangliang at allwinnertech.com>
>>> + *
>>> + * SPDX-License-Identifier:    GPL-2.0+
>>> + */
>>> +
>>> +#include <common.h>
>>> +#include <errno.h>
>>> +#include <asm/io.h>
>>> +#include <asm/arch/cpu.h>
>>> +#include <asm/arch/gpio.h>
>>> +#include <asm/arch/p2wi.h>
>>> +#include <asm/arch/prcm.h>
>>> +#include <asm/arch/clock.h>
>>> +#include <asm/arch/sys_proto.h>
>>> +
>>> +void p2wi_init(void)
>>> +{
>>> +       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg
>>> *)SUNXI_P2WI_BASE;
>>> +
>>> +       /* Enable p2wi and PIO clk, and de-assert their resets */
>>> +       prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_P2WI);
>>> +
>>> +       sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUNXI_GPL0_R_P2WI_SCK);
>>> +       sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUNXI_GPL1_R_P2WI_SDA);
>>> +
>>> +       /* Reset p2wi controller and set clock to CLKIN(12)/8 = 1.5 MHz
>>> */
>>> +       writel(P2WI_CTRL_RESET, &p2wi->ctrl);
>>> +       sdelay(0x100);
>>> +       writel(P2WI_CC_SDA_OUT_DELAY(1) | P2WI_CC_CLK_DIV(8),
>>> +              &p2wi->cc);
>>> +}
>>> +
>>> +int p2wi_set_pmu_address(u8 slave_addr, u8 ctrl_reg, u8 init_data)
>>
>> set_pmu_address is actually a misnomer. What it actually does is
>> write <init_data> to <ctrl_reg> on device found at <slave_addr>,
>> probably using I2C, with the sole purpose of putting the device
>> into P2WI mode (or RSB mode for later AXPs).
>>
>> According to the docs/code we have, it is always writing 0x3e to
>> register 0x3e.
>
> Back then, we didn't have no official docs, nor official code ;)
>
> I think this is what boot0 for a31 called it, or something close to it. Do
> you know what other AXP's take for this <ctrl_reg>?

The AXP806 datasheet says 0x7c, which is 0x3e left shifted by 1 bit.
And the RSB driver for U-boot from Allwinner does use 0x7c.

So my guess is 0x3e for P2WI and 0x7c for RSB.

Also note that with RSB, it is still written to 0x3e on the slaves,
but to all slaves, using the slave address 0x00.

> What are the chances, that 0x3e (in this chip) does what you say, enable
> p2wi or RSB mode? If that would be the case, a more generic function would
> be nice indeed, axp_init_mode(AXP_ADDR, AXP_MODE) or something comes to
> mind?

Probably better than "set_pmu_address", which it does not do. And for
RSB, there's yet another function to set the runtime address.

RSB driver uses "rsb_send_initseq".

>>> +{
>>> +       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg
>>> *)SUNXI_P2WI_BASE;
>>> +       unsigned long tmo = timer_get_us() + 1000000;
>>> +
>>> +       writel(P2WI_PM_DEV_ADDR(slave_addr) |
>>> +              P2WI_PM_CTRL_ADDR(ctrl_reg) |
>>> +              P2WI_PM_INIT_DATA(init_data) |
>>> +              P2WI_PM_INIT_SEND,
>>> +              &p2wi->pm);
>>> +
>>> +       while ((readl(&p2wi->pm) & P2WI_PM_INIT_SEND)) {
>>> +               if (timer_get_us() > tmo)
>>> +                       return -EFAULT;
>>> +       }
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static int p2wi_await_trans(void)
>>> +{
>>> +       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg
>>> *)SUNXI_P2WI_BASE;
>>> +       unsigned long tmo = timer_get_us() + 1000000;
>>> +       int ret;
>>> +       u8 reg;
>>> +
>>> +       while (1) {
>>> +               reg = readl(&p2wi->status);
>>> +               if (reg & P2WI_STAT_TRANS_ERR) {
>>> +                       ret = -EIO;
>>> +                       break;
>>> +               }
>>> +               if (reg & P2WI_STAT_TRANS_DONE) {
>>> +                       ret = 0;
>>> +                       break;
>>> +               }
>>> +               if (timer_get_us() > tmo) {
>>> +                       ret = -ETIME;
>>> +                       break;
>>> +               }
>>> +       }
>>> +       writel(reg, &p2wi->status); /* Clear status bits */
>>> +       return ret;
>>> +}
>>> +
>>> +int p2wi_read(const u8 addr, u8 *data)
>>> +{
>>> +       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg
>>> *)SUNXI_P2WI_BASE;
>>> +       int ret;
>>> +
>>> +       writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0);
>>> +       writel(P2WI_DATA_NUM_BYTES(1) |
>>> +              P2WI_DATA_NUM_BYTES_READ, &p2wi->numbytes);
>>> +       writel(P2WI_STAT_TRANS_DONE, &p2wi->status);
>>> +       writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl);
>>> +
>>> +       ret = p2wi_await_trans();
>>> +
>>> +       *data = readl(&p2wi->data0) & P2WI_DATA_BYTE_1_MASK;
>>> +       return ret;
>>> +}
>>> +
>>> +int p2wi_write(const u8 addr, u8 data)
>>> +{
>>> +       struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg
>>> *)SUNXI_P2WI_BASE;
>>> +
>>> +       writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0);
>>> +       writel(P2WI_DATA_BYTE_1(data), &p2wi->data0);
>>> +       writel(P2WI_DATA_NUM_BYTES(1), &p2wi->numbytes);
>>> +       writel(P2WI_STAT_TRANS_DONE, &p2wi->status);
>>> +       writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl);
>>> +
>>> +       return p2wi_await_trans();
>>> +}
>>> diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h
>>> b/arch/arm/include/asm/arch-sunxi/gpio.h
>>> index 437dd35..c734cf0 100644
>>> --- a/arch/arm/include/asm/arch-sunxi/gpio.h
>>> +++ b/arch/arm/include/asm/arch-sunxi/gpio.h
>>> @@ -172,6 +172,9 @@ enum sunxi_gpio_number {
>>>
>>>   #define SUN4I_GPI4_SDC3                2
>>>
>>> +#define SUNXI_GPL0_R_P2WI_SCK  3
>>> +#define SUNXI_GPL1_R_P2WI_SDA  3
>>> +
>>>   #define SUN8I_GPL2_R_UART_TX   2
>>>   #define SUN8I_GPL3_R_UART_RX   2
>>>
>>> diff --git a/arch/arm/include/asm/arch-sunxi/p2wi.h
>>> b/arch/arm/include/asm/arch-sunxi/p2wi.h
>>> new file mode 100644
>>> index 0000000..1788fa4
>>> --- /dev/null
>>> +++ b/arch/arm/include/asm/arch-sunxi/p2wi.h
>>> @@ -0,0 +1,140 @@
>>> +/*
>>> + * Sunxi platform Push-Push i2c register definition.
>>> + *
>>> + * (c) Copyright 2013 Oliver Schinagl <oliver at schinagl.nl>
>>> + * http://linux-sunxi.org
>>> + *
>>> + * (c)Copyright 2006-2013
>>> + * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
>>> + * Berg Xing <bergxing at allwinnertech.com>
>>> + * Tom Cubie <tangliang at allwinnertech.com>
>>> + *
>>> + * SPDX-License-Identifier:    GPL-2.0+
>>> + */
>>> +
>>> +#ifndef _SUNXI_P2WI_H
>>> +#define _SUNXI_P2WI_H
>>> +
>>> +#include <linux/types.h>
>>> +
>>> +#define P2WI_CTRL_RESET (0x1 << 0)
>>> +#define P2WI_CTRL_IRQ_EN (0x1 << 1)
>>> +#define P2WI_CTRL_TRANS_ABORT (0x1 << 6)
>>> +#define P2WI_CTRL_TRANS_START (0x1 << 7)
>>> +
>>> +#define __P2WI_CC_CLK(n) (((n) & 0xff) << 0)
>>> +#define P2WI_CC_CLK_MASK __P2WI_CC_CLK_DIV(0xff)
>>> +#define __P2WI_CC_CLK_DIV(n) (((n) >> 1) - 1)
>>> +#define P2WI_CC_CLK_DIV(n) \
>>> +       __P2WI_CC_CLK(__P2WI_CC_CLK_DIV(n))
>>> +#define P2WI_CC_SDA_OUT_DELAY(n) (((n) & 0x7) << 8)
>>> +#define P2WI_CC_SDA_OUT_DELAY_MASK P2WI_CC_SDA_OUT_DELAY(0x7)
>>> +
>>> +#define P2WI_IRQ_TRANS_DONE (0x1 << 0)
>>> +#define P2WI_IRQ_TRANS_ERR (0x1 << 1)
>>> +#define P2WI_IRQ_LOAD_BUSY (0x1 << 2)
>>> +
>>> +#define P2WI_STAT_TRANS_DONE (0x1 << 0)
>>> +#define P2WI_STAT_TRANS_ERR (0x1 << 1)
>>> +#define P2WI_STAT_LOAD_BUSY (0x1 << 2)
>>> +#define __P2WI_STAT_TRANS_ERR(n) (((n) & 0xff) << 8)
>>> +#define P2WI_STAT_TRANS_ERR_MASK __P2WI_STAT_TRANS_ERR_ID(0xff)
>>> +#define __P2WI_STAT_TRANS_ERR_BYTE_1 0x01
>>> +#define __P2WI_STAT_TRANS_ERR_BYTE_2 0x02
>>> +#define __P2WI_STAT_TRANS_ERR_BYTE_3 0x04
>>> +#define __P2WI_STAT_TRANS_ERR_BYTE_4 0x08
>>> +#define __P2WI_STAT_TRANS_ERR_BYTE_5 0x10
>>> +#define __P2WI_STAT_TRANS_ERR_BYTE_6 0x20
>>> +#define __P2WI_STAT_TRANS_ERR_BYTE_7 0x40
>>> +#define __P2WI_STAT_TRANS_ERR_BYTE_8 0x80
>>> +#define P2WI_STAT_TRANS_ERR_BYTE_1 \
>>> +       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_1)
>>> +#define P2WI_STAT_TRANS_ERR_BYTE_2 \
>>> +       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_2)
>>> +#define P2WI_STAT_TRANS_ERR_BYTE_3 \
>>> +       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_3)
>>> +#define P2WI_STAT_TRANS_ERR_BYTE_4 \
>>> +       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_4)
>>> +#define P2WI_STAT_TRANS_ERR_BYTE_5 \
>>> +       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_5)
>>> +#define P2WI_STAT_TRANS_ERR_BYTE_6 \
>>> +       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_6)
>>> +#define P2WI_STAT_TRANS_ERR_BYTE_7 \
>>> +       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_7)
>>> +#define P2WI_STAT_TRANS_ERR_BYTE_8 \
>>> +       __P2WI_STAT_TRANS_ERR(__P2WI_STAT_TRANS_ERR_BYTE_8)
>>> +
>>> +#define P2WI_DATADDR_BYTE_1(n) (((n) & 0xff) << 0)
>>> +#define P2WI_DATADDR_BYTE_1_MASK P2WI_DATADDR_BYTE_1(0xff)
>>> +#define P2WI_DATADDR_BYTE_2(n) (((n) & 0xff) << 8)
>>> +#define P2WI_DATADDR_BYTE_2_MASK P2WI_DATADDR_BYTE_2(0xff)
>>> +#define P2WI_DATADDR_BYTE_3(n) (((n) & 0xff) << 16)
>>> +#define P2WI_DATADDR_BYTE_3_MASK P2WI_DATADDR_BYTE_3(0xff)
>>> +#define P2WI_DATADDR_BYTE_4(n) (((n) & 0xff) << 24)
>>> +#define P2WI_DATADDR_BYTE_4_MASK P2WI_DATADDR_BYTE_4(0xff)
>>> +#define P2WI_DATADDR_BYTE_5(n) (((n) & 0xff) << 0)
>>> +#define P2WI_DATADDR_BYTE_5_MASK P2WI_DATADDR_BYTE_5(0xff)
>>> +#define P2WI_DATADDR_BYTE_6(n) (((n) & 0xff) << 8)
>>> +#define P2WI_DATADDR_BYTE_6_MASK P2WI_DATADDR_BYTE_6(0xff)
>>> +#define P2WI_DATADDR_BYTE_7(n) (((n) & 0xff) << 16)
>>> +#define P2WI_DATADDR_BYTE_7_MASK P2WI_DATADDR_BYTE_7(0xff)
>>> +#define P2WI_DATADDR_BYTE_8(n) (((n) & 0xff) << 24)
>>> +#define P2WI_DATADDR_BYTE_8_MASK P2WI_DATADDR_BYTE_8(0xff)
>>> +
>>> +#define __P2WI_DATA_NUM_BYTES(n) (((n) & 0x7) << 0)
>>> +#define P2WI_DATA_NUM_BYTES_MASK __P2WI_DATA_NUM_BYTES(0x7)
>>> +#define P2WI_DATA_NUM_BYTES(n) __P2WI_DATA_NUM_BYTES((n) - 1)
>>> +#define P2WI_DATA_NUM_BYTES_READ (0x1 << 4)
>>> +
>>> +#define P2WI_DATA_BYTE_1(n) (((n) & 0xff) << 0)
>>> +#define P2WI_DATA_BYTE_1_MASK P2WI_DATA_BYTE_1(0xff)
>>> +#define P2WI_DATA_BYTE_2(n) (((n) & 0xff) << 8)
>>> +#define P2WI_DATA_BYTE_2_MASK P2WI_DATA_BYTE_2(0xff)
>>> +#define P2WI_DATA_BYTE_3(n) (((n) & 0xff) << 16)
>>> +#define P2WI_DATA_BYTE_3_MASK P2WI_DATA_BYTE_3(0xff)
>>> +#define P2WI_DATA_BYTE_4(n) (((n) & 0xff) << 24)
>>> +#define P2WI_DATA_BYTE_4_MASK P2WI_DATA_BYTE_4(0xff)
>>> +#define P2WI_DATA_BYTE_5(n) (((n) & 0xff) << 0)
>>> +#define P2WI_DATA_BYTE_5_MASK P2WI_DATA_BYTE_5(0xff)
>>> +#define P2WI_DATA_BYTE_6(n) (((n) & 0xff) << 8)
>>> +#define P2WI_DATA_BYTE_6_MASK P2WI_DATA_BYTE_6(0xff)
>>> +#define P2WI_DATA_BYTE_7(n) (((n) & 0xff) << 16)
>>> +#define P2WI_DATA_BYTE_7_MASK P2WI_DATA_BYTE_7(0xff)
>>> +#define P2WI_DATA_BYTE_8(n) (((n) & 0xff) << 24)
>>> +#define P2WI_DATA_BYTE_8_MASK P2WI_DATA_BYTE_8(0xff)
>>> +
>>> +#define P2WI_LINECTRL_SDA_CTRL_EN (0x1 << 0)
>>> +#define P2WI_LINECTRL_SDA_OUT_HIGH (0x1 << 1)
>>> +#define P2WI_LINECTRL_SCL_CTRL_EN (0x1 << 2)
>>> +#define P2WI_LINECTRL_SCL_OUT_HIGH (0x1 << 3)
>>> +#define P2WI_LINECTRL_SDA_STATE_HIGH (0x1 << 4)
>>> +#define P2WI_LINECTRL_SCL_STATE_HIGH (0x1 << 5)
>>> +
>>> +#define P2WI_PM_DEV_ADDR(n) (((n) & 0xff) << 0)
>>> +#define P2WI_PM_DEV_ADDR_MASK P2WI_PM_DEV_ADDR(0xff)
>>> +#define P2WI_PM_CTRL_ADDR(n) (((n) & 0xff) << 8)
>>> +#define P2WI_PM_CTRL_ADDR_MASK P2WI_PM_CTRL_ADDR(0xff)
>>> +#define P2WI_PM_INIT_DATA(n) (((n) & 0xff) << 16)
>>> +#define P2WI_PM_INIT_DATA_MASK P2WI_PM_INIT_DATA(0xff)
>>> +#define P2WI_PM_INIT_SEND (0x1 << 31)
>>> +
>>> +struct sunxi_p2wi_reg {
>>> +       u32 ctrl;       /* 0x00 control */
>>> +       u32 cc;         /* 0x04 clock control */
>>> +       u32 irq;        /* 0x08 interrupt */
>>> +       u32 status;     /* 0x0c status */
>>> +       u32 dataddr0;   /* 0x10 data address 0 */
>>> +       u32 dataddr1;   /* 0x14 data address 1 */
>>> +       u32 numbytes;   /* 0x18 num bytes */
>>> +       u32 data0;      /* 0x1c data buffer 0 */
>>> +       u32 data1;      /* 0x20 data buffer 1 */
>>> +       u32 linectrl;   /* 0x24 line control */
>>> +       u32 pm;         /* 0x28 power management */
>>> +};
>>
>> Is there any reason not to put all of the above in the driver (.c) itself?
>
> no reason, I just followed what was common about a year + ago :) I'm still
> not sure what the general consensus is about structs for register data to
> begin with :)
>>
>>
>>> +
>>> +void p2wi_init(void);
>>> +int p2wi_set_pmu_address(u8 slave_addr, u8 ctrl_reg, u8 init_data);
>>> +int p2wi_read(const u8 addr, u8 *data);
>>> +int p2wi_write(const u8 addr, u8 data);
>>> +
>>> +#endif /* _SUNXI_P2WI_H */
>>> --
>>> 2.1.0
>>
>> Thanks for working on this! I haven't had time to mainline the p2wi
>> driver.
>
> Yes! Thanks for picking this up Hans, it feels good to see this being used
> :D
>>
>>
>> ChenYu
>
>
> --
> 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 at googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.


More information about the U-Boot mailing list