[PATCH linux 2/4] drivers/fsi: FSI master initialization
Christopher Bostic
christopher.lee.bostic at gmail.com
Thu Aug 4 03:15:46 AEST 2016
Hi Joel,
Thanks for your comments. To answer some of your questions:
>> MODULE_AUTHOR("Christopher Bostic cbostic at us.ibm.com");
>> MODULE_DESCRIPTION("FSI master device driver");
>> @@ -23,9 +25,13 @@ MODULE_DESCRIPTION("FSI master device driver");
>> #define FSIDD_VERNO 4000
>> #define FSIDD_VER(x) FSIDD_TOSTR(x)
>>
>> +struct primaster prim;
>
> Why are we statically allocating this? What happens if my system has
> two fsi masters in it?
>
FSI hardware, as it exists presently, does not allow for more than one
master on a given bus. There must be a first master in the chain to
initiate communications, here its called 'primary' master. There can
be other masters downstream but that function is not planned for
addition to the core function. That's an extended capability.
>> +EXPORT_SYMBOL(prim);
>
> Why are you exporting this?
>
The primary master is a useful entry point to the FSI device tree and
will be referenced by various source files within drivers/fsi. Its
not intended to be accessed by any client drivers outside of
drivers/fsi
>> +/*
>> + * Read/write functions to access primary FSI master registers
>> + */
>> +static int local_readreg(volatile u32 *base, int regnm, u32 *value)
>
>
> Finally, why does this function exist? I suggest you use a generic
> function such as readl/writel.
>
For OpenFSI at the start there is no master hardware logic - we won't
have that until ast2600 is released. This is intended for access to a
virtual FSI master which is just a data structure that represents all
the registers in the hardware. Any code accessing it then doesn't
need to know if its running on real or virtual hardware. Various
registers within the FSI master have rules on how you can write to
them: Full write, write under mask (IS_SU_REG below) , clear under
mask (IS_CU_REG) below.
>> +}
>> +
>> +static int local_writereg(volatile u32 *base, int regnm, u32 value)
>> +{
>> + u32 new_value = value, r_value;
>> +
>> + if (regnm == FSI_N_MRESB0)
>> + new_value = 0;
>> + else if (FSI_N_IS_CU_REG(regnm)) {
>> + local_readreg(base, regnm, &r_value);
>> + new_value = r_value &= ~value;
>> + } else if (FSI_N_IS_SU_REG(regnm)) {
>> + local_readreg(base, regnm, &r_value);
>> + new_value = r_value |= value;
>> + }
>> + *(base + regnm) = new_value;
>> + wmb();
>
>> +/* FSI master register numbers */
>> +#define FSI_N_MMODE 0 /* 0x0 R/W: mode register */
>> +#define FSI_N_MDLYR 1 /* 0x4 R/W: delay register */
>> +#define FSI_N_MCRSP0 2 /* 0x8 R/W: clock rate selector register 0 */
>> +#define FSI_N_MCRSP32 3 /* 0xC R/W: clock rate selector register 1 */
>> +#define FSI_N_MENP0 4 /* 0x10 R/W: enable clock register 0 */
>> +#define FSI_N_MENP32 5 /* 0x14 R/W: enable clock register 1 */
>> +#define FSI_N_MLEVP0 6 /* 0x18 R: static level register 0 */
>> +#define FSI_N_MLEVP32 7 /* 0x1C R: static level register 1 */
>> +#define FSI_N_MSENP0 6 /* 0x18 S: set enable clock register 0 */
>> +#define FSI_N_MREFP0 8 /* 0x20 R: link reference register 0 */
>> +#define FSI_N_MCENP0 8 /* 0x20 W: clear enable port register 0 */
>
> These duplicated names are confusing. Could we use the one definition
> for the register?
>
Not sure I understand what you mean by duplicated names. Each offset
can have a different
register name and function depending on if you're reading it or
writing it. For example,
the FSI bridge register MESRB allows you to read the bridge state but
when you write this
performs a bridge reset function (MRESB) These mnemonics are taken
verbatim from the
hardware spec.
>> +#define FSI_N_MHPMP0 10 /* 0x28 R: hot plug reference register 0 */
>> +#define FSI_N_MCHPMP0 10 /* 0x28 W: clear hot plug reference reg 0 */
>> +#define FSI_N_MSIEP0 12 /* 0x30 R/W: Ipoll register 0 */
>> +#define FSI_N_MSIEP32 16 /* 0x40 R/W: Ipoll register 4 */
>> +#define FSI_N_MAESP0 20 /* 0x50 R: any error port register 0 */
>> +#define FSI_N_MAESP32 24 /* 0x60 R: any error port register 4 */
>> +#define FSI_N_MSSIEP0 20 /* 0x50 W: set Ipoll register 0 */
>
>
> At this point hte registers stop going up by 4. I assume those above
> are memory mapped, and those below are something different?
>
Everything defined with FSI_N_... is memory mapped. Just turned out
that hardware placed these
registers at odd memory offsets.
>> +#define FSI_N_MAEB 28 /* 0x70 R: any error bridge */
>> +#define FSI_N_MVER 29 /* 0x74 R: version register */
>> +#define FSI_N_MBSYP0 30 /* 0x78 R: port busy register 0 */
>> +#define FSI_N_MCSIEP0 28 /* 0x70 W: clear Ipoll register 0 */
>> +#define FSI_N_MDRSB1 36 /* 0x90 R/W: DMA select register master 1 */
>> +#define FSI_N_MSTAP0 52 /* 0xd0 R: port status reg 0-63 (0xd0-0x1cc) */
>> +#define FSI_N_MRESP0 52 /* 0xd0 W: port reset regr 0-63 (0xd0-0x1cc) */
>> +#define FSI_N_MESRB0 116 /* 0x1d0 R: error syndrome register 0-16 */
>> +#define FSI_N_MRESB0 116 /* 0x1d0 W: reset reg master 0-16 (x1d0-x210) */
>> +#define FSI_N_MSCSB0 117 /* 0x1d4 R: master sub cmd stack register 0 */
>> +#define FSI_N_MATRB0 118 /* 0x1d8 R: master address trace register 0 */
>> +#define FSI_N_MDTRB0 119 /* 0x1dc R: master data trace register 0 */
>> +#define FSI_N_MECTRL 184 /* 0x2e0 R/W: error control register master 0 */
>> +#define FSI_N_MAESP_SZ 8 /* # of error port register 0-7 */
>> +
>> +#define FSI_MSIEP_REG_COUNT 8
>> +#define PRI_MAX_LINKS FSI_MAX_LINKS
>
> What is PRI?
>
PRI stands for primary master, the first master in the chain.
>
>> + unsigned char myid; /* FSI master identifier for traces */
>> + unsigned char type; /* Type FSI master */
>> + unsigned char hw_version; /* FSI master hardware version */
>> + unsigned char maxlinks; /* FSI master links */
>> + struct fsilink *link[FSI_MAX_LINKS];
>> + int (*write_f)(volatile u32 *, int, u32); /* Write function */
>> + int (*read_f)(volatile u32 *, int, u32 *); /* Read function */
>> + int (*write_f2)(volatile u32 *, int, u32 *); /* Write function */
>> + int (*read_f2)(volatile u32 *, int, u32 *); /* Read function */
>> + int (*read_f4)(volatile u32 *, int, u32 *); /* Read function */
>> + int (*write_f8)(volatile u32 *, int, u32 *); /* Write function */
>
> Will these be used outside of drivers/fsi/fsimaster.c? If not, I think
> we could call the functions directly instead of through pointers.
>
These are function pointers because depending on master type each of
these functions
will have different behavior. Right how with core function there is
only the primary FSI
master but later on there will be different types such as hub masters.
The construct is
to identify master type on discovery and then initialize these
pointers with the appropriate
function. This type of initialization with function pointers is done
for various devices
throughout the driver.
More information about the openbmc
mailing list