On Wed, Jan 05, 2022 at 06:29:00PM -0600, Alex Elder wrote: > On 12/2/21 5:35 AM, Manivannan Sadhasivam wrote: > > Add support for managing the Memory Mapped Input Output (MMIO) registers > > of the MHI bus. All MHI operations are carried out using the MMIO registers > > by both host and the endpoint device. > > > > The MMIO registers reside inside the endpoint device memory (fixed > > location based on the platform) and the address is passed by the MHI EP > > controller driver during its registration. > > > > Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@xxxxxxxxxx> > > --- > > drivers/bus/mhi/ep/Makefile | 2 +- > > drivers/bus/mhi/ep/internal.h | 36 ++++ > > drivers/bus/mhi/ep/main.c | 6 +- > > drivers/bus/mhi/ep/mmio.c | 303 ++++++++++++++++++++++++++++++++++ > > include/linux/mhi_ep.h | 18 ++ > > 5 files changed, 363 insertions(+), 2 deletions(-) > > create mode 100644 drivers/bus/mhi/ep/mmio.c > > > > diff --git a/drivers/bus/mhi/ep/Makefile b/drivers/bus/mhi/ep/Makefile > > index 64e29252b608..a1555ae287ad 100644 > > --- a/drivers/bus/mhi/ep/Makefile > > +++ b/drivers/bus/mhi/ep/Makefile > > @@ -1,2 +1,2 @@ > > obj-$(CONFIG_MHI_BUS_EP) += mhi_ep.o > > -mhi_ep-y := main.o > > +mhi_ep-y := main.o mmio.o > > diff --git a/drivers/bus/mhi/ep/internal.h b/drivers/bus/mhi/ep/internal.h > > index 7b164daf4332..39eeb5f384e2 100644 > > --- a/drivers/bus/mhi/ep/internal.h > > +++ b/drivers/bus/mhi/ep/internal.h > > @@ -91,6 +91,12 @@ struct mhi_generic_ctx { > > __u64 wp __packed __aligned(4); > > }; > > Maybe add a comment defining SBL as "secondary boot loader" and AMSS > as "advanced modem subsystem". > Sure, will add kernel doc. But from modem terms, AMSS refers to "Advanced Mode Subscriber Software". > > +enum mhi_ep_execenv { > > + MHI_EP_SBL_EE = 1, > > + MHI_EP_AMSS_EE = 2, > > + MHI_EP_UNRESERVED > > +}; > > + > > enum mhi_ep_ring_state { > > RING_STATE_UINT = 0, > > RING_STATE_IDLE, > > @@ -155,4 +161,34 @@ struct mhi_ep_chan { > > bool skip_td; > > }; > > +/* MMIO related functions */ > > I would *really* rather have the mmio_read functions *return* the read > value, rather than having the address of the location to store it passed > as argument. Your MMIO calls never fail, so there's no need to return > anything else. Returning the value also makes it more obvious that the > *result* is getting assigned (rather than sort of implying it by passing > in the address of the result). And there's no possibility of someone > passing a bad pointer that way either. > > > +void mhi_ep_mmio_read(struct mhi_ep_cntrl *mhi_cntrl, u32 offset, u32 *regval); > > In other words: > > u32 mhi_ep_mmio_read(struct mhi_ep_ctrl *mhi_ctrl, u32 offset); > > > +void mhi_ep_mmio_write(struct mhi_ep_cntrl *mhi_cntrl, u32 offset, u32 val); > > +void mhi_ep_mmio_masked_write(struct mhi_ep_cntrl *mhi_cntrl, u32 offset, > > + u32 mask, u32 shift, u32 val); > > +int mhi_ep_mmio_masked_read(struct mhi_ep_cntrl *dev, u32 offset, > > + u32 mask, u32 shift, u32 *regval); > > +void mhi_ep_mmio_enable_ctrl_interrupt(struct mhi_ep_cntrl *mhi_cntrl); > > +void mhi_ep_mmio_disable_ctrl_interrupt(struct mhi_ep_cntrl *mhi_cntrl); > > +void mhi_ep_mmio_enable_cmdb_interrupt(struct mhi_ep_cntrl *mhi_cntrl); > > +void mhi_ep_mmio_disable_cmdb_interrupt(struct mhi_ep_cntrl *mhi_cntrl); > > +void mhi_ep_mmio_enable_chdb_a7(struct mhi_ep_cntrl *mhi_cntrl, u32 chdb_id); > > +void mhi_ep_mmio_disable_chdb_a7(struct mhi_ep_cntrl *mhi_cntrl, u32 chdb_id); > > +void mhi_ep_mmio_enable_chdb_interrupts(struct mhi_ep_cntrl *mhi_cntrl); > > +void mhi_ep_mmio_read_chdb_status_interrupts(struct mhi_ep_cntrl *mhi_cntrl); > > +void mhi_ep_mmio_mask_interrupts(struct mhi_ep_cntrl *mhi_cntrl); > > +void mhi_ep_mmio_get_chc_base(struct mhi_ep_cntrl *mhi_cntrl); > > +void mhi_ep_mmio_get_erc_base(struct mhi_ep_cntrl *mhi_cntrl); > > +void mhi_ep_mmio_get_crc_base(struct mhi_ep_cntrl *mhi_cntrl); > > +void mhi_ep_mmio_get_ch_db(struct mhi_ep_ring *ring, u64 *wr_offset); > > +void mhi_ep_mmio_get_er_db(struct mhi_ep_ring *ring, u64 *wr_offset); > > +void mhi_ep_mmio_get_cmd_db(struct mhi_ep_ring *ring, u64 *wr_offset); > > +void mhi_ep_mmio_set_env(struct mhi_ep_cntrl *mhi_cntrl, u32 value); > > +void mhi_ep_mmio_clear_reset(struct mhi_ep_cntrl *mhi_cntrl); > > +void mhi_ep_mmio_reset(struct mhi_ep_cntrl *mhi_cntrl); > > +void mhi_ep_mmio_get_mhi_state(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state *state, > > + bool *mhi_reset); > > +void mhi_ep_mmio_init(struct mhi_ep_cntrl *mhi_cntrl); > > +void mhi_ep_mmio_update_ner(struct mhi_ep_cntrl *mhi_cntrl); > > + > > #endif > > diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c > > index f0b5f49db95a..fddf75dfb9c7 100644 > > --- a/drivers/bus/mhi/ep/main.c > > +++ b/drivers/bus/mhi/ep/main.c > > @@ -209,7 +209,7 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl, > > struct mhi_ep_device *mhi_dev; > > int ret; > > - if (!mhi_cntrl || !mhi_cntrl->cntrl_dev) > > + if (!mhi_cntrl || !mhi_cntrl->cntrl_dev || !mhi_cntrl->mmio) > > return -EINVAL; > > ret = parse_ch_cfg(mhi_cntrl, config); > > @@ -222,6 +222,10 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl, > > goto err_free_ch; > > } > > + /* Set MHI version and AMSS EE before enumeration */ > > + mhi_ep_mmio_write(mhi_cntrl, MHIVER, config->mhi_version); > > + mhi_ep_mmio_set_env(mhi_cntrl, MHI_EP_AMSS_EE); > > + > > /* Set controller index */ > > mhi_cntrl->index = ida_alloc(&mhi_ep_cntrl_ida, GFP_KERNEL); > > if (mhi_cntrl->index < 0) { > > diff --git a/drivers/bus/mhi/ep/mmio.c b/drivers/bus/mhi/ep/mmio.c > > new file mode 100644 > > index 000000000000..157ef1240f6f > > --- /dev/null > > +++ b/drivers/bus/mhi/ep/mmio.c > > @@ -0,0 +1,303 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Copyright (C) 2021 Linaro Ltd. > > + * Author: Manivannan Sadhasivam <manivannan.sadhasivam@xxxxxxxxxx> > > + */ > > + > > +#include <linux/bitfield.h> > > +#include <linux/io.h> > > +#include <linux/mhi_ep.h> > > + > > +#include "internal.h" > > + > > +void mhi_ep_mmio_read(struct mhi_ep_cntrl *mhi_cntrl, u32 offset, u32 *regval) > > +{ > > + *regval = readl(mhi_cntrl->mmio + offset); > > return readl(...); > done > > +} > > + > > +void mhi_ep_mmio_write(struct mhi_ep_cntrl *mhi_cntrl, u32 offset, u32 val) > > +{ > > + writel(val, mhi_cntrl->mmio + offset); > > +} > > + > > +void mhi_ep_mmio_masked_write(struct mhi_ep_cntrl *mhi_cntrl, u32 offset, u32 mask, > > + u32 shift, u32 val) > > There is no need for a shift argument here. I would like to say > "use the bitfield functions" but at the moment they require the > mask to be constant. You could still do that, by having all > these be defined as static inline functions in a header though. > Maybe you can use FIELD_GET() though, I don't know. > I've used __ffs to determine the shift. > Anyway, try to get rid of these shifts; they shouldn't be > necessary. > > > +{ > > + u32 regval; > > + > > + mhi_ep_mmio_read(mhi_cntrl, offset, ®val); > > + regval &= ~mask; > > + regval |= ((val << shift) & mask); > > + mhi_ep_mmio_write(mhi_cntrl, offset, regval); > > +} > > + > > +int mhi_ep_mmio_masked_read(struct mhi_ep_cntrl *dev, u32 offset, > > + u32 mask, u32 shift, u32 *regval) > > +{ > > + mhi_ep_mmio_read(dev, offset, regval); > > + *regval &= mask; > > + *regval >>= shift; > > + > > + return 0; > > There is no point in returning 0 from this function. > > > +} > > + > > +void mhi_ep_mmio_get_mhi_state(struct mhi_ep_cntrl *mhi_cntrl, enum mhi_state *state, > > + bool *mhi_reset) > > +{ > > + u32 regval; > > + > > + mhi_ep_mmio_read(mhi_cntrl, MHICTRL, ®val); > > + *state = FIELD_GET(MHICTRL_MHISTATE_MASK, regval); > > + *mhi_reset = !!FIELD_GET(MHICTRL_RESET_MASK, regval); > > +} > > + > > +static void mhi_ep_mmio_mask_set_chdb_int_a7(struct mhi_ep_cntrl *mhi_cntrl, > > + u32 chdb_id, bool enable) > > +{ > > + u32 chid_mask, chid_idx, chid_shft, val = 0; > > + > > + chid_shft = chdb_id % 32; > > + chid_mask = BIT(chid_shft); > > + chid_idx = chdb_id / 32; > > + > > + if (chid_idx >= MHI_MASK_ROWS_CH_EV_DB) > > + return; > > The above should maybe issue a warning? > ack > > + > > + if (enable) > > + val = 1; > > + > > + mhi_ep_mmio_masked_write(mhi_cntrl, MHI_CHDB_INT_MASK_A7_n(chid_idx), > > + chid_mask, chid_shft, val); > > + mhi_ep_mmio_read(mhi_cntrl, MHI_CHDB_INT_MASK_A7_n(chid_idx), > > + &mhi_cntrl->chdb[chid_idx].mask); > > Why do you read after writing? Is this to be sure the write completes > over PCIe or something? Even then I don't think that would be needed > because the memory is on "this side" of PCIe (right?). > This is done to update the mask. We could also do the bit managment stuff here instead of reading from the register (I guess that'll be faster). Thanks, Mani > > +} > > + > > +void mhi_ep_mmio_enable_chdb_a7(struct mhi_ep_cntrl *mhi_cntrl, u32 chdb_id) > > +{ > > + mhi_ep_mmio_mask_set_chdb_int_a7(mhi_cntrl, chdb_id, true); > > +} > > + > > +void mhi_ep_mmio_disable_chdb_a7(struct mhi_ep_cntrl *mhi_cntrl, u32 chdb_id) > > +{ > > + mhi_ep_mmio_mask_set_chdb_int_a7(mhi_cntrl, chdb_id, false); > > +} > > + > > +static void mhi_ep_mmio_set_chdb_interrupts(struct mhi_ep_cntrl *mhi_cntrl, bool enable) > > +{ > > + u32 val = 0, i = 0; > > No need for assigning 0 to i. > > -Alex > > > + > > + if (enable) > > + val = MHI_CHDB_INT_MASK_A7_n_EN_ALL; > > + > > + for (i = 0; i < MHI_MASK_ROWS_CH_EV_DB; i++) { > > + mhi_ep_mmio_write(mhi_cntrl, MHI_CHDB_INT_MASK_A7_n(i), val); > > + mhi_cntrl->chdb[i].mask = val; > > + } > > +} > > + > > . . .