Skip to content

Commit

Permalink
bus: mhi: core: Add support for registering MHI client drivers
Browse files Browse the repository at this point in the history
This commit adds support for registering MHI client drivers with the
MHI stack. MHI client drivers binds to one or more MHI devices inorder
to sends and receive the upper-layer protocol packets like IP packets,
modem control messages, and diagnostics messages over MHI bus.

This is based on the patch submitted by Sujeev Dias:
https://lkml.org/lkml/2018/7/9/987

Signed-off-by: Sujeev Dias <sdias@codeaurora.org>
Signed-off-by: Siddartha Mohanadoss <smohanad@codeaurora.org>
[mani: splitted and cleaned up for upstream]
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Reviewed-by: Jeffrey Hugo <jhugo@codeaurora.org>
Tested-by: Jeffrey Hugo <jhugo@codeaurora.org>
Link: https://lore.kernel.org/r/20200220095854.4804-4-manivannan.sadhasivam@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Manivannan Sadhasivam authored and Greg Kroah-Hartman committed Mar 19, 2020
1 parent 0cbf260 commit e755cad
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 0 deletions.
149 changes: 149 additions & 0 deletions drivers/bus/mhi/core/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -374,8 +374,157 @@ struct mhi_device *mhi_alloc_device(struct mhi_controller *mhi_cntrl)
return mhi_dev;
}

static int mhi_driver_probe(struct device *dev)
{
struct mhi_device *mhi_dev = to_mhi_device(dev);
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
struct device_driver *drv = dev->driver;
struct mhi_driver *mhi_drv = to_mhi_driver(drv);
struct mhi_event *mhi_event;
struct mhi_chan *ul_chan = mhi_dev->ul_chan;
struct mhi_chan *dl_chan = mhi_dev->dl_chan;

if (ul_chan) {
/*
* If channel supports LPM notifications then status_cb should
* be provided
*/
if (ul_chan->lpm_notify && !mhi_drv->status_cb)
return -EINVAL;

/* For non-offload channels then xfer_cb should be provided */
if (!ul_chan->offload_ch && !mhi_drv->ul_xfer_cb)
return -EINVAL;

ul_chan->xfer_cb = mhi_drv->ul_xfer_cb;
}

if (dl_chan) {
/*
* If channel supports LPM notifications then status_cb should
* be provided
*/
if (dl_chan->lpm_notify && !mhi_drv->status_cb)
return -EINVAL;

/* For non-offload channels then xfer_cb should be provided */
if (!dl_chan->offload_ch && !mhi_drv->dl_xfer_cb)
return -EINVAL;

mhi_event = &mhi_cntrl->mhi_event[dl_chan->er_index];

/*
* If the channel event ring is managed by client, then
* status_cb must be provided so that the framework can
* notify pending data
*/
if (mhi_event->cl_manage && !mhi_drv->status_cb)
return -EINVAL;

dl_chan->xfer_cb = mhi_drv->dl_xfer_cb;
}

/* Call the user provided probe function */
return mhi_drv->probe(mhi_dev, mhi_dev->id);
}

static int mhi_driver_remove(struct device *dev)
{
struct mhi_device *mhi_dev = to_mhi_device(dev);
struct mhi_driver *mhi_drv = to_mhi_driver(dev->driver);
struct mhi_chan *mhi_chan;
enum mhi_ch_state ch_state[] = {
MHI_CH_STATE_DISABLED,
MHI_CH_STATE_DISABLED
};
int dir;

/* Skip if it is a controller device */
if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
return 0;

/* Reset both channels */
for (dir = 0; dir < 2; dir++) {
mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;

if (!mhi_chan)
continue;

/* Wake all threads waiting for completion */
write_lock_irq(&mhi_chan->lock);
mhi_chan->ccs = MHI_EV_CC_INVALID;
complete_all(&mhi_chan->completion);
write_unlock_irq(&mhi_chan->lock);

/* Set the channel state to disabled */
mutex_lock(&mhi_chan->mutex);
write_lock_irq(&mhi_chan->lock);
ch_state[dir] = mhi_chan->ch_state;
mhi_chan->ch_state = MHI_CH_STATE_SUSPENDED;
write_unlock_irq(&mhi_chan->lock);

mutex_unlock(&mhi_chan->mutex);
}

mhi_drv->remove(mhi_dev);

/* De-init channel if it was enabled */
for (dir = 0; dir < 2; dir++) {
mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;

if (!mhi_chan)
continue;

mutex_lock(&mhi_chan->mutex);

mhi_chan->ch_state = MHI_CH_STATE_DISABLED;

mutex_unlock(&mhi_chan->mutex);
}

return 0;
}

int mhi_driver_register(struct mhi_driver *mhi_drv)
{
struct device_driver *driver = &mhi_drv->driver;

if (!mhi_drv->probe || !mhi_drv->remove)
return -EINVAL;

driver->bus = &mhi_bus_type;
driver->probe = mhi_driver_probe;
driver->remove = mhi_driver_remove;

return driver_register(driver);
}
EXPORT_SYMBOL_GPL(mhi_driver_register);

void mhi_driver_unregister(struct mhi_driver *mhi_drv)
{
driver_unregister(&mhi_drv->driver);
}
EXPORT_SYMBOL_GPL(mhi_driver_unregister);

static int mhi_match(struct device *dev, struct device_driver *drv)
{
struct mhi_device *mhi_dev = to_mhi_device(dev);
struct mhi_driver *mhi_drv = to_mhi_driver(drv);
const struct mhi_device_id *id;

/*
* If the device is a controller type then there is no client driver
* associated with it
*/
if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER)
return 0;

for (id = mhi_drv->id_table; id->chan[0]; id++)
if (!strcmp(mhi_dev->chan_name, id->chan)) {
mhi_dev->id = id;
return 1;
}

return 0;
};

Expand Down
39 changes: 39 additions & 0 deletions include/linux/mhi.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,8 @@ struct mhi_controller {
* @dl_chan: DL channel for the device
* @dev: Driver model device node for the MHI device
* @dev_type: MHI device type
* @ul_chan_id: MHI channel id for UL transfer
* @dl_chan_id: MHI channel id for DL transfer
* @dev_wake: Device wakeup counter
*/
struct mhi_device {
Expand All @@ -364,6 +366,8 @@ struct mhi_device {
struct mhi_chan *dl_chan;
struct device dev;
enum mhi_device_type dev_type;
int ul_chan_id;
int dl_chan_id;
u32 dev_wake;
};

Expand All @@ -381,6 +385,29 @@ struct mhi_result {
int transaction_status;
};

/**
* struct mhi_driver - Structure representing a MHI client driver
* @probe: CB function for client driver probe function
* @remove: CB function for client driver remove function
* @ul_xfer_cb: CB function for UL data transfer
* @dl_xfer_cb: CB function for DL data transfer
* @status_cb: CB functions for asynchronous status
* @driver: Device driver model driver
*/
struct mhi_driver {
const struct mhi_device_id *id_table;
int (*probe)(struct mhi_device *mhi_dev,
const struct mhi_device_id *id);
void (*remove)(struct mhi_device *mhi_dev);
void (*ul_xfer_cb)(struct mhi_device *mhi_dev,
struct mhi_result *result);
void (*dl_xfer_cb)(struct mhi_device *mhi_dev,
struct mhi_result *result);
void (*status_cb)(struct mhi_device *mhi_dev, enum mhi_callback mhi_cb);
struct device_driver driver;
};

#define to_mhi_driver(drv) container_of(drv, struct mhi_driver, driver)
#define to_mhi_device(dev) container_of(dev, struct mhi_device, dev)

/**
Expand All @@ -397,4 +424,16 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
*/
void mhi_unregister_controller(struct mhi_controller *mhi_cntrl);

/**
* mhi_driver_register - Register driver with MHI framework
* @mhi_drv: Driver associated with the device
*/
int mhi_driver_register(struct mhi_driver *mhi_drv);

/**
* mhi_driver_unregister - Unregister a driver for mhi_devices
* @mhi_drv: Driver associated with the device
*/
void mhi_driver_unregister(struct mhi_driver *mhi_drv);

#endif /* _MHI_H_ */

0 comments on commit e755cad

Please sign in to comment.