From 59796edcf21c7c19d58a223001f9ed13746c51c2 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Thu, 10 Sep 2015 10:17:59 +0300 Subject: mei: make modules.alias UUID information easier to read scripts/mod/file2alias.c:add_uuid() convert UUID into a single string which does not conform to the standard little endian UUID formatting. This patch changes add_uuid() to output same format as %pUL and modifies the mei driver to match the change. Signed-off-by: Prarit Bhargava Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- include/linux/mod_devicetable.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include') diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 688997a24aad..5e8a0ad22cbc 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -601,10 +601,6 @@ struct ipack_device_id { #define MEI_CL_MODULE_PREFIX "mei:" #define MEI_CL_NAME_SIZE 32 -#define MEI_CL_UUID_FMT "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" -#define MEI_CL_UUID_ARGS(_u) \ - _u[0], _u[1], _u[2], _u[3], _u[4], _u[5], _u[6], _u[7], \ - _u[8], _u[9], _u[10], _u[11], _u[12], _u[13], _u[14], _u[15] /** * struct mei_cl_device_id - MEI client device identifier -- cgit v1.2.3 From b26864cad1c9f66f4966726ba7bc81d2b9b8f990 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 10 Sep 2015 10:18:01 +0300 Subject: mei: bus: add client protocol version to the device alias The device alias now looks like mei:S:uuid:N:* In that way we can bind different drivers to clients with different protocol versions if required. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 28 +++++++++++++++++++++------- drivers/nfc/microread/mei.c | 2 +- drivers/nfc/pn544/mei.c | 2 +- include/linux/mod_devicetable.h | 3 +++ scripts/mod/devicetable-offsets.c | 1 + scripts/mod/file2alias.c | 4 +++- 6 files changed, 30 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index d92017fa1630..38bc4380ad08 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -453,17 +453,26 @@ struct mei_cl_device_id *mei_cl_device_find(struct mei_cl_device *cldev, { const struct mei_cl_device_id *id; const uuid_le *uuid; + u8 version; + bool match; uuid = mei_me_cl_uuid(cldev->me_cl); + version = mei_me_cl_ver(cldev->me_cl); id = cldrv->id_table; while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) { if (!uuid_le_cmp(*uuid, id->uuid)) { + match = true; - if (!cldev->name[0]) - return id; + if (cldev->name[0]) + if (strncmp(cldev->name, id->name, + sizeof(id->name))) + match = false; - if (!strncmp(cldev->name, id->name, sizeof(id->name))) + if (id->version != MEI_CL_VERSION_ANY) + if (id->version != version) + match = false; + if (match) return id; } @@ -647,7 +656,8 @@ static int mei_cl_device_uevent(struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "MEI_CL_NAME=%s", cldev->name)) return -ENOMEM; - if (add_uevent_var(env, "MODALIAS=mei:%s:%pUl:", cldev->name, uuid)) + if (add_uevent_var(env, "MODALIAS=mei:%s:%pUl:%02X:", + cldev->name, uuid, version)) return -ENOMEM; return 0; @@ -737,8 +747,10 @@ static bool mei_cl_dev_setup(struct mei_device *bus, mei_cl_dev_fixup(cldev); if (cldev->do_match) - dev_set_name(&cldev->dev, "mei:%s:%pUl", - cldev->name, mei_me_cl_uuid(cldev->me_cl)); + dev_set_name(&cldev->dev, "mei:%s:%pUl:%02X", + cldev->name, + mei_me_cl_uuid(cldev->me_cl), + mei_me_cl_ver(cldev->me_cl)); return cldev->do_match == 1; } @@ -754,7 +766,9 @@ static int mei_cl_bus_dev_add(struct mei_cl_device *cldev) { int ret; - dev_dbg(cldev->bus->dev, "adding %pUL\n", mei_me_cl_uuid(cldev->me_cl)); + dev_dbg(cldev->bus->dev, "adding %pUL:%02X\n", + mei_me_cl_uuid(cldev->me_cl), + mei_me_cl_ver(cldev->me_cl)); ret = device_add(&cldev->dev); if (!ret) cldev->is_added = 1; diff --git a/drivers/nfc/microread/mei.c b/drivers/nfc/microread/mei.c index f9f5fc97cdd7..93328bd45110 100644 --- a/drivers/nfc/microread/mei.c +++ b/drivers/nfc/microread/mei.c @@ -67,7 +67,7 @@ static int microread_mei_remove(struct mei_cl_device *device) } static struct mei_cl_device_id microread_mei_tbl[] = { - { MICROREAD_DRIVER_NAME, MEI_NFC_UUID}, + { MICROREAD_DRIVER_NAME, MEI_NFC_UUID, MEI_CL_VERSION_ANY}, /* required last entry */ { } diff --git a/drivers/nfc/pn544/mei.c b/drivers/nfc/pn544/mei.c index 101a37e12efa..80f897b4a401 100644 --- a/drivers/nfc/pn544/mei.c +++ b/drivers/nfc/pn544/mei.c @@ -67,7 +67,7 @@ static int pn544_mei_remove(struct mei_cl_device *device) } static struct mei_cl_device_id pn544_mei_tbl[] = { - { PN544_DRIVER_NAME, MEI_NFC_UUID}, + { PN544_DRIVER_NAME, MEI_NFC_UUID, MEI_CL_VERSION_ANY}, /* required last entry */ { } diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 5e8a0ad22cbc..6975cbf1435b 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -601,11 +601,13 @@ struct ipack_device_id { #define MEI_CL_MODULE_PREFIX "mei:" #define MEI_CL_NAME_SIZE 32 +#define MEI_CL_VERSION_ANY 0xff /** * struct mei_cl_device_id - MEI client device identifier * @name: helper name * @uuid: client uuid + * @version: client protocol version * @driver_info: information used by the driver. * * identifies mei client device by uuid and name @@ -613,6 +615,7 @@ struct ipack_device_id { struct mei_cl_device_id { char name[MEI_CL_NAME_SIZE]; uuid_le uuid; + __u8 version; kernel_ulong_t driver_info; }; diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index e70fcd12eeeb..5a6edacc85d9 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -185,6 +185,7 @@ int main(void) DEVID(mei_cl_device_id); DEVID_FIELD(mei_cl_device_id, name); DEVID_FIELD(mei_cl_device_id, uuid); + DEVID_FIELD(mei_cl_device_id, version); DEVID(rio_device_id); DEVID_FIELD(rio_device_id, did); diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index fa79d113f34c..9bc2cfe0ee37 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1202,16 +1202,18 @@ static int do_cpu_entry(const char *filename, void *symval, char *alias) } ADD_TO_DEVTABLE("cpu", cpu_feature, do_cpu_entry); -/* Looks like: mei:S:uuid */ +/* Looks like: mei:S:uuid:N:* */ static int do_mei_entry(const char *filename, void *symval, char *alias) { DEF_FIELD_ADDR(symval, mei_cl_device_id, name); DEF_FIELD_ADDR(symval, mei_cl_device_id, uuid); + DEF_FIELD(symval, mei_cl_device_id, version); sprintf(alias, MEI_CL_MODULE_PREFIX); sprintf(alias + strlen(alias), "%s:", (*name)[0] ? *name : "*"); add_uuid(alias, *uuid); + ADD(alias, ":", version != MEI_CL_VERSION_ANY, version); strcat(alias, ":*"); -- cgit v1.2.3 From baeacd0376975bee0fdf6378e1c24587ab0b6ad4 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 10 Sep 2015 10:18:02 +0300 Subject: mei: bus: export uuid and protocol version to mei_cl bus drivers Export the uuid and the protocol version of the underlying me client for me client bus drivers usage. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 26 ++++++++++++++++++++++++++ include/linux/mei_cl_bus.h | 3 +++ 2 files changed, 29 insertions(+) (limited to 'include') diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 38bc4380ad08..53525ca50337 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -341,6 +341,32 @@ void mei_cl_set_drvdata(struct mei_cl_device *cldev, void *data) } EXPORT_SYMBOL_GPL(mei_cl_set_drvdata); +/** + * mei_cldev_uuid - return uuid of the underlying me client + * + * @cldev: mei client device + * + * Return: me client uuid + */ +const uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev) +{ + return mei_me_cl_uuid(cldev->me_cl); +} +EXPORT_SYMBOL_GPL(mei_cldev_uuid); + +/** + * mei_cldev_ver - return protocol version of the underlying me client + * + * @cldev: mei client device + * + * Return: me client protocol version + */ +u8 mei_cldev_ver(const struct mei_cl_device *cldev) +{ + return mei_me_cl_ver(cldev->me_cl); +} +EXPORT_SYMBOL_GPL(mei_cldev_ver); + /** * mei_cl_enable_device - enable me client device * create connection with me client diff --git a/include/linux/mei_cl_bus.h b/include/linux/mei_cl_bus.h index 0962b2ca628a..9834f832b098 100644 --- a/include/linux/mei_cl_bus.h +++ b/include/linux/mei_cl_bus.h @@ -85,6 +85,9 @@ int mei_cl_register_event_cb(struct mei_cl_device *device, #define MEI_CL_EVENT_TX 1 #define MEI_CL_EVENT_NOTIF 2 +const uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev); +u8 mei_cldev_ver(const struct mei_cl_device *cldev); + void *mei_cl_get_drvdata(const struct mei_cl_device *device); void mei_cl_set_drvdata(struct mei_cl_device *device, void *data); -- cgit v1.2.3 From 01a14edeaf0456c28e2b9af3afdc0807ec6c20bd Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 10 Sep 2015 10:18:03 +0300 Subject: mei: bus: export mei_cldev_enabled function Let me client device driver query of the device is connected and hence enabled. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 13 +++++++++++++ include/linux/mei_cl_bus.h | 1 + 2 files changed, 14 insertions(+) (limited to 'include') diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 53525ca50337..4edf71acfed6 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -367,6 +367,19 @@ u8 mei_cldev_ver(const struct mei_cl_device *cldev) } EXPORT_SYMBOL_GPL(mei_cldev_ver); +/** + * mei_cldev_enabled - check whether the device is enabled + * + * @cldev: mei client device + * + * Return: true if me client is initialized and connected + */ +bool mei_cldev_enabled(struct mei_cl_device *cldev) +{ + return cldev->cl && mei_cl_is_connected(cldev->cl); +} +EXPORT_SYMBOL_GPL(mei_cldev_enabled); + /** * mei_cl_enable_device - enable me client device * create connection with me client diff --git a/include/linux/mei_cl_bus.h b/include/linux/mei_cl_bus.h index 9834f832b098..30d1c8b94a27 100644 --- a/include/linux/mei_cl_bus.h +++ b/include/linux/mei_cl_bus.h @@ -93,5 +93,6 @@ void mei_cl_set_drvdata(struct mei_cl_device *device, void *data); int mei_cl_enable_device(struct mei_cl_device *device); int mei_cl_disable_device(struct mei_cl_device *device); +bool mei_cldev_enabled(struct mei_cl_device *cldev); #endif /* _LINUX_MEI_CL_BUS_H */ -- cgit v1.2.3 From 893913822e829f7a37824f6041ff964076374191 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 10 Sep 2015 10:18:04 +0300 Subject: mei: bus: complete variable rename of type struct mei_cl_device In the commit 5c079ae11921 ("mei: bus: fix drivers and devices names confusion") we set the variables of type struct mei_cl_device to 'cldev' but few places were left out, namely mei_cl_bus.h header and the mei nfc drivers. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/nfc/mei_phy.c | 30 +++++++++++++++--------------- drivers/nfc/mei_phy.h | 4 ++-- drivers/nfc/microread/mei.c | 8 ++++---- drivers/nfc/pn544/mei.c | 8 ++++---- include/linux/mei_cl_bus.h | 29 ++++++++++++++--------------- 5 files changed, 39 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c index 754a9bb0f58d..ecd2a0dce797 100644 --- a/drivers/nfc/mei_phy.c +++ b/drivers/nfc/mei_phy.c @@ -118,7 +118,7 @@ static int mei_nfc_if_version(struct nfc_mei_phy *phy) cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION; MEI_DUMP_NFC_HDR("version", &cmd.hdr); - r = mei_cl_send(phy->device, (u8 *)&cmd, sizeof(struct mei_nfc_cmd)); + r = mei_cl_send(phy->cldev, (u8 *)&cmd, sizeof(struct mei_nfc_cmd)); if (r < 0) { pr_err("Could not send IF version cmd\n"); return r; @@ -132,7 +132,7 @@ static int mei_nfc_if_version(struct nfc_mei_phy *phy) if (!reply) return -ENOMEM; - bytes_recv = mei_cl_recv(phy->device, (u8 *)reply, if_version_length); + bytes_recv = mei_cl_recv(phy->cldev, (u8 *)reply, if_version_length); if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) { pr_err("Could not read IF version\n"); r = -EIO; @@ -186,13 +186,13 @@ static int mei_nfc_connect(struct nfc_mei_phy *phy) connect->vendor_id = phy->vendor_id; MEI_DUMP_NFC_HDR("connect request", &cmd->hdr); - r = mei_cl_send(phy->device, (u8 *)cmd, connect_length); + r = mei_cl_send(phy->cldev, (u8 *)cmd, connect_length); if (r < 0) { pr_err("Could not send connect cmd %d\n", r); goto err; } - bytes_recv = mei_cl_recv(phy->device, (u8 *)reply, connect_resp_length); + bytes_recv = mei_cl_recv(phy->cldev, (u8 *)reply, connect_resp_length); if (bytes_recv < 0) { r = bytes_recv; pr_err("Could not read connect response %d\n", r); @@ -238,7 +238,7 @@ static int mei_nfc_send(struct nfc_mei_phy *phy, u8 *buf, size_t length) MEI_DUMP_NFC_HDR("send", hdr); memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length); - err = mei_cl_send(phy->device, mei_buf, length + MEI_NFC_HEADER_SIZE); + err = mei_cl_send(phy->cldev, mei_buf, length + MEI_NFC_HEADER_SIZE); if (err < 0) goto out; @@ -278,7 +278,7 @@ static int mei_nfc_recv(struct nfc_mei_phy *phy, u8 *buf, size_t length) struct mei_nfc_hdr *hdr; int received_length; - received_length = mei_cl_recv(phy->device, buf, length); + received_length = mei_cl_recv(phy->cldev, buf, length); if (received_length < 0) return received_length; @@ -296,7 +296,7 @@ static int mei_nfc_recv(struct nfc_mei_phy *phy, u8 *buf, size_t length) } -static void nfc_mei_event_cb(struct mei_cl_device *device, u32 events, +static void nfc_mei_event_cb(struct mei_cl_device *cldev, u32 events, void *context) { struct nfc_mei_phy *phy = context; @@ -337,7 +337,7 @@ static int nfc_mei_phy_enable(void *phy_id) if (phy->powered == 1) return 0; - r = mei_cl_enable_device(phy->device); + r = mei_cl_enable_device(phy->cldev); if (r < 0) { pr_err("Could not enable device %d\n", r); return r; @@ -355,7 +355,7 @@ static int nfc_mei_phy_enable(void *phy_id) goto err; } - r = mei_cl_register_event_cb(phy->device, BIT(MEI_CL_EVENT_RX), + r = mei_cl_register_event_cb(phy->cldev, BIT(MEI_CL_EVENT_RX), nfc_mei_event_cb, phy); if (r) { pr_err("Event cb registration failed %d\n", r); @@ -368,7 +368,7 @@ static int nfc_mei_phy_enable(void *phy_id) err: phy->powered = 0; - mei_cl_disable_device(phy->device); + mei_cl_disable_device(phy->cldev); return r; } @@ -378,7 +378,7 @@ static void nfc_mei_phy_disable(void *phy_id) pr_info("%s\n", __func__); - mei_cl_disable_device(phy->device); + mei_cl_disable_device(phy->cldev); phy->powered = 0; } @@ -390,7 +390,7 @@ struct nfc_phy_ops mei_phy_ops = { }; EXPORT_SYMBOL_GPL(mei_phy_ops); -struct nfc_mei_phy *nfc_mei_phy_alloc(struct mei_cl_device *device) +struct nfc_mei_phy *nfc_mei_phy_alloc(struct mei_cl_device *cldev) { struct nfc_mei_phy *phy; @@ -398,9 +398,9 @@ struct nfc_mei_phy *nfc_mei_phy_alloc(struct mei_cl_device *device) if (!phy) return NULL; - phy->device = device; + phy->cldev = cldev; init_waitqueue_head(&phy->send_wq); - mei_cl_set_drvdata(device, phy); + mei_cl_set_drvdata(cldev, phy); return phy; } @@ -408,7 +408,7 @@ EXPORT_SYMBOL_GPL(nfc_mei_phy_alloc); void nfc_mei_phy_free(struct nfc_mei_phy *phy) { - mei_cl_disable_device(phy->device); + mei_cl_disable_device(phy->cldev); kfree(phy); } EXPORT_SYMBOL_GPL(nfc_mei_phy_free); diff --git a/drivers/nfc/mei_phy.h b/drivers/nfc/mei_phy.h index fbfa3e61738f..acd3a1fc69e6 100644 --- a/drivers/nfc/mei_phy.h +++ b/drivers/nfc/mei_phy.h @@ -13,7 +13,7 @@ /** * struct nfc_mei_phy * - * @device: mei device + * @cldev: mei client device * @hdev: nfc hci device * @send_wq: send completion wait queue @@ -28,7 +28,7 @@ * and prevents normal operation. */ struct nfc_mei_phy { - struct mei_cl_device *device; + struct mei_cl_device *cldev; struct nfc_hci_dev *hdev; wait_queue_head_t send_wq; diff --git a/drivers/nfc/microread/mei.c b/drivers/nfc/microread/mei.c index 93328bd45110..994871c02b7b 100644 --- a/drivers/nfc/microread/mei.c +++ b/drivers/nfc/microread/mei.c @@ -29,7 +29,7 @@ #define MICROREAD_DRIVER_NAME "microread" -static int microread_mei_probe(struct mei_cl_device *device, +static int microread_mei_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { struct nfc_mei_phy *phy; @@ -37,7 +37,7 @@ static int microread_mei_probe(struct mei_cl_device *device, pr_info("Probing NFC microread\n"); - phy = nfc_mei_phy_alloc(device); + phy = nfc_mei_phy_alloc(cldev); if (!phy) { pr_err("Cannot allocate memory for microread mei phy.\n"); return -ENOMEM; @@ -55,9 +55,9 @@ static int microread_mei_probe(struct mei_cl_device *device, return 0; } -static int microread_mei_remove(struct mei_cl_device *device) +static int microread_mei_remove(struct mei_cl_device *cldev) { - struct nfc_mei_phy *phy = mei_cl_get_drvdata(device); + struct nfc_mei_phy *phy = mei_cl_get_drvdata(cldev); microread_remove(phy->hdev); diff --git a/drivers/nfc/pn544/mei.c b/drivers/nfc/pn544/mei.c index 80f897b4a401..2a2c9304e64c 100644 --- a/drivers/nfc/pn544/mei.c +++ b/drivers/nfc/pn544/mei.c @@ -27,7 +27,7 @@ #define PN544_DRIVER_NAME "pn544" -static int pn544_mei_probe(struct mei_cl_device *device, +static int pn544_mei_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { struct nfc_mei_phy *phy; @@ -35,7 +35,7 @@ static int pn544_mei_probe(struct mei_cl_device *device, pr_info("Probing NFC pn544\n"); - phy = nfc_mei_phy_alloc(device); + phy = nfc_mei_phy_alloc(cldev); if (!phy) { pr_err("Cannot allocate memory for pn544 mei phy.\n"); return -ENOMEM; @@ -53,9 +53,9 @@ static int pn544_mei_probe(struct mei_cl_device *device, return 0; } -static int pn544_mei_remove(struct mei_cl_device *device) +static int pn544_mei_remove(struct mei_cl_device *cldev) { - struct nfc_mei_phy *phy = mei_cl_get_drvdata(device); + struct nfc_mei_phy *phy = mei_cl_get_drvdata(cldev); pr_info("Removing pn544\n"); diff --git a/include/linux/mei_cl_bus.h b/include/linux/mei_cl_bus.h index 30d1c8b94a27..c364df750405 100644 --- a/include/linux/mei_cl_bus.h +++ b/include/linux/mei_cl_bus.h @@ -8,7 +8,7 @@ struct mei_cl_device; struct mei_device; -typedef void (*mei_cl_event_cb_t)(struct mei_cl_device *device, +typedef void (*mei_cl_event_cb_t)(struct mei_cl_device *cldev, u32 events, void *context); /** @@ -62,22 +62,21 @@ struct mei_cl_driver { const struct mei_cl_device_id *id_table; - int (*probe)(struct mei_cl_device *dev, + int (*probe)(struct mei_cl_device *cldev, const struct mei_cl_device_id *id); - int (*remove)(struct mei_cl_device *dev); + int (*remove)(struct mei_cl_device *cldev); }; -int __mei_cl_driver_register(struct mei_cl_driver *driver, - struct module *owner); -#define mei_cl_driver_register(driver) \ - __mei_cl_driver_register(driver, THIS_MODULE) +int __mei_cl_driver_register(struct mei_cl_driver *cldrv, struct module *owner); +#define mei_cl_driver_register(cldrv) \ + __mei_cl_driver_register(cldrv, THIS_MODULE) -void mei_cl_driver_unregister(struct mei_cl_driver *driver); +void mei_cl_driver_unregister(struct mei_cl_driver *cldrv); -ssize_t mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length); -ssize_t mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length); +ssize_t mei_cl_send(struct mei_cl_device *cldev, u8 *buf, size_t length); +ssize_t mei_cl_recv(struct mei_cl_device *cldev, u8 *buf, size_t length); -int mei_cl_register_event_cb(struct mei_cl_device *device, +int mei_cl_register_event_cb(struct mei_cl_device *cldev, unsigned long event_mask, mei_cl_event_cb_t read_cb, void *context); @@ -88,11 +87,11 @@ int mei_cl_register_event_cb(struct mei_cl_device *device, const uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev); u8 mei_cldev_ver(const struct mei_cl_device *cldev); -void *mei_cl_get_drvdata(const struct mei_cl_device *device); -void mei_cl_set_drvdata(struct mei_cl_device *device, void *data); +void *mei_cl_get_drvdata(const struct mei_cl_device *cldev); +void mei_cl_set_drvdata(struct mei_cl_device *cldev, void *data); -int mei_cl_enable_device(struct mei_cl_device *device); -int mei_cl_disable_device(struct mei_cl_device *device); +int mei_cl_enable_device(struct mei_cl_device *cldev); +int mei_cl_disable_device(struct mei_cl_device *cldev); bool mei_cldev_enabled(struct mei_cl_device *cldev); #endif /* _LINUX_MEI_CL_BUS_H */ -- cgit v1.2.3 From d49dc5e76fc917e5dfef76cb56fe3b3868deed5d Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 10 Sep 2015 10:18:05 +0300 Subject: mei: bus: use mei_cldev_ prefix for the API functions Use mei_cldev_ prefix for all mei client bus api functions in order to resolve prefix conflict with functions that handle client function and are defined in client.c Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 67 +++++++++++++++++++++++---------------------- drivers/nfc/mei_phy.c | 25 +++++++++-------- drivers/nfc/microread/mei.c | 6 ++-- drivers/nfc/pn544/mei.c | 6 ++-- include/linux/mei_cl_bus.h | 33 +++++++++++----------- 5 files changed, 70 insertions(+), 67 deletions(-) (limited to 'include') diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 4edf71acfed6..0406e7201fe4 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -165,7 +165,7 @@ out: } /** - * mei_cl_send - me device send (write) + * mei_cldev_send - me device send (write) * * @cldev: me client device * @buf: buffer to send @@ -173,7 +173,7 @@ out: * * Return: written size in bytes or < 0 on error */ -ssize_t mei_cl_send(struct mei_cl_device *cldev, u8 *buf, size_t length) +ssize_t mei_cldev_send(struct mei_cl_device *cldev, u8 *buf, size_t length) { struct mei_cl *cl = cldev->cl; @@ -182,10 +182,10 @@ ssize_t mei_cl_send(struct mei_cl_device *cldev, u8 *buf, size_t length) return __mei_cl_send(cl, buf, length, 1); } -EXPORT_SYMBOL_GPL(mei_cl_send); +EXPORT_SYMBOL_GPL(mei_cldev_send); /** - * mei_cl_recv - client receive (read) + * mei_cldev_recv - client receive (read) * * @cldev: me client device * @buf: buffer to send @@ -193,7 +193,7 @@ EXPORT_SYMBOL_GPL(mei_cl_send); * * Return: read size in bytes of < 0 on error */ -ssize_t mei_cl_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) +ssize_t mei_cldev_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) { struct mei_cl *cl = cldev->cl; @@ -202,15 +202,15 @@ ssize_t mei_cl_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) return __mei_cl_recv(cl, buf, length); } -EXPORT_SYMBOL_GPL(mei_cl_recv); +EXPORT_SYMBOL_GPL(mei_cldev_recv); /** - * mei_bus_event_work - dispatch rx event for a bus device + * mei_cl_bus_event_work - dispatch rx event for a bus device * and schedule new work * * @work: work */ -static void mei_bus_event_work(struct work_struct *work) +static void mei_cl_bus_event_work(struct work_struct *work) { struct mei_cl_device *cldev; @@ -272,7 +272,7 @@ void mei_cl_bus_rx_event(struct mei_cl *cl) } /** - * mei_cl_register_event_cb - register event callback + * mei_cldev_register_event_cb - register event callback * * @cldev: me client devices * @event_cb: callback function @@ -283,9 +283,9 @@ void mei_cl_bus_rx_event(struct mei_cl *cl) * -EALREADY if an callback is already registered * <0 on other errors */ -int mei_cl_register_event_cb(struct mei_cl_device *cldev, - unsigned long events_mask, - mei_cl_event_cb_t event_cb, void *context) +int mei_cldev_register_event_cb(struct mei_cl_device *cldev, + unsigned long events_mask, + mei_cldev_event_cb_t event_cb, void *context) { int ret; @@ -296,7 +296,7 @@ int mei_cl_register_event_cb(struct mei_cl_device *cldev, cldev->events_mask = events_mask; cldev->event_cb = event_cb; cldev->event_context = context; - INIT_WORK(&cldev->event_work, mei_bus_event_work); + INIT_WORK(&cldev->event_work, mei_cl_bus_event_work); if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) { ret = mei_cl_read_start(cldev->cl, 0, NULL); @@ -314,32 +314,32 @@ int mei_cl_register_event_cb(struct mei_cl_device *cldev, return 0; } -EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); +EXPORT_SYMBOL_GPL(mei_cldev_register_event_cb); /** - * mei_cl_get_drvdata - driver data getter + * mei_cldev_get_drvdata - driver data getter * * @cldev: mei client device * * Return: driver private data */ -void *mei_cl_get_drvdata(const struct mei_cl_device *cldev) +void *mei_cldev_get_drvdata(const struct mei_cl_device *cldev) { return dev_get_drvdata(&cldev->dev); } -EXPORT_SYMBOL_GPL(mei_cl_get_drvdata); +EXPORT_SYMBOL_GPL(mei_cldev_get_drvdata); /** - * mei_cl_set_drvdata - driver data setter + * mei_cldev_set_drvdata - driver data setter * * @cldev: mei client device * @data: data to store */ -void mei_cl_set_drvdata(struct mei_cl_device *cldev, void *data) +void mei_cldev_set_drvdata(struct mei_cl_device *cldev, void *data) { dev_set_drvdata(&cldev->dev, data); } -EXPORT_SYMBOL_GPL(mei_cl_set_drvdata); +EXPORT_SYMBOL_GPL(mei_cldev_set_drvdata); /** * mei_cldev_uuid - return uuid of the underlying me client @@ -381,14 +381,14 @@ bool mei_cldev_enabled(struct mei_cl_device *cldev) EXPORT_SYMBOL_GPL(mei_cldev_enabled); /** - * mei_cl_enable_device - enable me client device + * mei_cldev_enable_device - enable me client device * create connection with me client * * @cldev: me client device * * Return: 0 on success and < 0 on error */ -int mei_cl_enable_device(struct mei_cl_device *cldev) +int mei_cldev_enable(struct mei_cl_device *cldev) { struct mei_device *bus = cldev->bus; struct mei_cl *cl; @@ -428,17 +428,17 @@ out: return ret; } -EXPORT_SYMBOL_GPL(mei_cl_enable_device); +EXPORT_SYMBOL_GPL(mei_cldev_enable); /** - * mei_cl_disable_device - disable me client device + * mei_cldev_disable - disable me client device * disconnect form the me client * * @cldev: me client device * * Return: 0 on success and < 0 on error */ -int mei_cl_disable_device(struct mei_cl_device *cldev) +int mei_cldev_disable(struct mei_cl_device *cldev) { struct mei_device *bus; struct mei_cl *cl; @@ -476,7 +476,7 @@ out: mutex_unlock(&bus->device_lock); return err; } -EXPORT_SYMBOL_GPL(mei_cl_disable_device); +EXPORT_SYMBOL_GPL(mei_cldev_disable); /** * mei_cl_device_find - find matching entry in the driver id table @@ -663,14 +663,14 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a, } static DEVICE_ATTR_RO(modalias); -static struct attribute *mei_cl_dev_attrs[] = { +static struct attribute *mei_cldev_attrs[] = { &dev_attr_name.attr, &dev_attr_uuid.attr, &dev_attr_version.attr, &dev_attr_modalias.attr, NULL, }; -ATTRIBUTE_GROUPS(mei_cl_dev); +ATTRIBUTE_GROUPS(mei_cldev); /** * mei_cl_device_uevent - me client bus uevent handler @@ -704,7 +704,7 @@ static int mei_cl_device_uevent(struct device *dev, struct kobj_uevent_env *env) static struct bus_type mei_cl_bus_type = { .name = "mei", - .dev_groups = mei_cl_dev_groups, + .dev_groups = mei_cldev_groups, .match = mei_cl_device_match, .probe = mei_cl_device_probe, .remove = mei_cl_device_remove, @@ -937,7 +937,8 @@ void mei_cl_bus_rescan(struct mei_device *bus) dev_dbg(bus->dev, "rescan end"); } -int __mei_cl_driver_register(struct mei_cl_driver *cldrv, struct module *owner) +int __mei_cldev_driver_register(struct mei_cl_driver *cldrv, + struct module *owner) { int err; @@ -953,15 +954,15 @@ int __mei_cl_driver_register(struct mei_cl_driver *cldrv, struct module *owner) return 0; } -EXPORT_SYMBOL_GPL(__mei_cl_driver_register); +EXPORT_SYMBOL_GPL(__mei_cldev_driver_register); -void mei_cl_driver_unregister(struct mei_cl_driver *cldrv) +void mei_cldev_driver_unregister(struct mei_cl_driver *cldrv) { driver_unregister(&cldrv->driver); pr_debug("mei: driver [%s] unregistered\n", cldrv->driver.name); } -EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); +EXPORT_SYMBOL_GPL(mei_cldev_driver_unregister); int __init mei_cl_bus_init(void) diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c index ecd2a0dce797..83deda4bb4d6 100644 --- a/drivers/nfc/mei_phy.c +++ b/drivers/nfc/mei_phy.c @@ -118,7 +118,7 @@ static int mei_nfc_if_version(struct nfc_mei_phy *phy) cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION; MEI_DUMP_NFC_HDR("version", &cmd.hdr); - r = mei_cl_send(phy->cldev, (u8 *)&cmd, sizeof(struct mei_nfc_cmd)); + r = mei_cldev_send(phy->cldev, (u8 *)&cmd, sizeof(struct mei_nfc_cmd)); if (r < 0) { pr_err("Could not send IF version cmd\n"); return r; @@ -132,7 +132,7 @@ static int mei_nfc_if_version(struct nfc_mei_phy *phy) if (!reply) return -ENOMEM; - bytes_recv = mei_cl_recv(phy->cldev, (u8 *)reply, if_version_length); + bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply, if_version_length); if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) { pr_err("Could not read IF version\n"); r = -EIO; @@ -186,13 +186,14 @@ static int mei_nfc_connect(struct nfc_mei_phy *phy) connect->vendor_id = phy->vendor_id; MEI_DUMP_NFC_HDR("connect request", &cmd->hdr); - r = mei_cl_send(phy->cldev, (u8 *)cmd, connect_length); + r = mei_cldev_send(phy->cldev, (u8 *)cmd, connect_length); if (r < 0) { pr_err("Could not send connect cmd %d\n", r); goto err; } - bytes_recv = mei_cl_recv(phy->cldev, (u8 *)reply, connect_resp_length); + bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply, + connect_resp_length); if (bytes_recv < 0) { r = bytes_recv; pr_err("Could not read connect response %d\n", r); @@ -238,7 +239,7 @@ static int mei_nfc_send(struct nfc_mei_phy *phy, u8 *buf, size_t length) MEI_DUMP_NFC_HDR("send", hdr); memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length); - err = mei_cl_send(phy->cldev, mei_buf, length + MEI_NFC_HEADER_SIZE); + err = mei_cldev_send(phy->cldev, mei_buf, length + MEI_NFC_HEADER_SIZE); if (err < 0) goto out; @@ -278,7 +279,7 @@ static int mei_nfc_recv(struct nfc_mei_phy *phy, u8 *buf, size_t length) struct mei_nfc_hdr *hdr; int received_length; - received_length = mei_cl_recv(phy->cldev, buf, length); + received_length = mei_cldev_recv(phy->cldev, buf, length); if (received_length < 0) return received_length; @@ -337,7 +338,7 @@ static int nfc_mei_phy_enable(void *phy_id) if (phy->powered == 1) return 0; - r = mei_cl_enable_device(phy->cldev); + r = mei_cldev_enable(phy->cldev); if (r < 0) { pr_err("Could not enable device %d\n", r); return r; @@ -355,7 +356,7 @@ static int nfc_mei_phy_enable(void *phy_id) goto err; } - r = mei_cl_register_event_cb(phy->cldev, BIT(MEI_CL_EVENT_RX), + r = mei_cldev_register_event_cb(phy->cldev, BIT(MEI_CL_EVENT_RX), nfc_mei_event_cb, phy); if (r) { pr_err("Event cb registration failed %d\n", r); @@ -368,7 +369,7 @@ static int nfc_mei_phy_enable(void *phy_id) err: phy->powered = 0; - mei_cl_disable_device(phy->cldev); + mei_cldev_disable(phy->cldev); return r; } @@ -378,7 +379,7 @@ static void nfc_mei_phy_disable(void *phy_id) pr_info("%s\n", __func__); - mei_cl_disable_device(phy->cldev); + mei_cldev_disable(phy->cldev); phy->powered = 0; } @@ -400,7 +401,7 @@ struct nfc_mei_phy *nfc_mei_phy_alloc(struct mei_cl_device *cldev) phy->cldev = cldev; init_waitqueue_head(&phy->send_wq); - mei_cl_set_drvdata(cldev, phy); + mei_cldev_set_drvdata(cldev, phy); return phy; } @@ -408,7 +409,7 @@ EXPORT_SYMBOL_GPL(nfc_mei_phy_alloc); void nfc_mei_phy_free(struct nfc_mei_phy *phy) { - mei_cl_disable_device(phy->cldev); + mei_cldev_disable(phy->cldev); kfree(phy); } EXPORT_SYMBOL_GPL(nfc_mei_phy_free); diff --git a/drivers/nfc/microread/mei.c b/drivers/nfc/microread/mei.c index 994871c02b7b..3092501f26c4 100644 --- a/drivers/nfc/microread/mei.c +++ b/drivers/nfc/microread/mei.c @@ -57,7 +57,7 @@ static int microread_mei_probe(struct mei_cl_device *cldev, static int microread_mei_remove(struct mei_cl_device *cldev) { - struct nfc_mei_phy *phy = mei_cl_get_drvdata(cldev); + struct nfc_mei_phy *phy = mei_cldev_get_drvdata(cldev); microread_remove(phy->hdev); @@ -88,7 +88,7 @@ static int microread_mei_init(void) pr_debug(DRIVER_DESC ": %s\n", __func__); - r = mei_cl_driver_register(µread_driver); + r = mei_cldev_driver_register(µread_driver); if (r) { pr_err(MICROREAD_DRIVER_NAME ": driver registration failed\n"); return r; @@ -99,7 +99,7 @@ static int microread_mei_init(void) static void microread_mei_exit(void) { - mei_cl_driver_unregister(µread_driver); + mei_cldev_driver_unregister(µread_driver); } module_init(microread_mei_init); diff --git a/drivers/nfc/pn544/mei.c b/drivers/nfc/pn544/mei.c index 2a2c9304e64c..46d0eb24eef9 100644 --- a/drivers/nfc/pn544/mei.c +++ b/drivers/nfc/pn544/mei.c @@ -55,7 +55,7 @@ static int pn544_mei_probe(struct mei_cl_device *cldev, static int pn544_mei_remove(struct mei_cl_device *cldev) { - struct nfc_mei_phy *phy = mei_cl_get_drvdata(cldev); + struct nfc_mei_phy *phy = mei_cldev_get_drvdata(cldev); pr_info("Removing pn544\n"); @@ -88,7 +88,7 @@ static int pn544_mei_init(void) pr_debug(DRIVER_DESC ": %s\n", __func__); - r = mei_cl_driver_register(&pn544_driver); + r = mei_cldev_driver_register(&pn544_driver); if (r) { pr_err(PN544_DRIVER_NAME ": driver registration failed\n"); return r; @@ -99,7 +99,7 @@ static int pn544_mei_init(void) static void pn544_mei_exit(void) { - mei_cl_driver_unregister(&pn544_driver); + mei_cldev_driver_unregister(&pn544_driver); } module_init(pn544_mei_init); diff --git a/include/linux/mei_cl_bus.h b/include/linux/mei_cl_bus.h index c364df750405..e746919530f5 100644 --- a/include/linux/mei_cl_bus.h +++ b/include/linux/mei_cl_bus.h @@ -8,8 +8,8 @@ struct mei_cl_device; struct mei_device; -typedef void (*mei_cl_event_cb_t)(struct mei_cl_device *cldev, - u32 events, void *context); +typedef void (*mei_cldev_event_cb_t)(struct mei_cl_device *cldev, + u32 events, void *context); /** * struct mei_cl_device - MEI device handle @@ -45,7 +45,7 @@ struct mei_cl_device { char name[MEI_CL_NAME_SIZE]; struct work_struct event_work; - mei_cl_event_cb_t event_cb; + mei_cldev_event_cb_t event_cb; void *event_context; unsigned long events_mask; unsigned long events; @@ -67,18 +67,19 @@ struct mei_cl_driver { int (*remove)(struct mei_cl_device *cldev); }; -int __mei_cl_driver_register(struct mei_cl_driver *cldrv, struct module *owner); -#define mei_cl_driver_register(cldrv) \ - __mei_cl_driver_register(cldrv, THIS_MODULE) +int __mei_cldev_driver_register(struct mei_cl_driver *cldrv, + struct module *owner); +#define mei_cldev_driver_register(cldrv) \ + __mei_cldev_driver_register(cldrv, THIS_MODULE) -void mei_cl_driver_unregister(struct mei_cl_driver *cldrv); +void mei_cldev_driver_unregister(struct mei_cl_driver *cldrv); -ssize_t mei_cl_send(struct mei_cl_device *cldev, u8 *buf, size_t length); -ssize_t mei_cl_recv(struct mei_cl_device *cldev, u8 *buf, size_t length); +ssize_t mei_cldev_send(struct mei_cl_device *cldev, u8 *buf, size_t length); +ssize_t mei_cldev_recv(struct mei_cl_device *cldev, u8 *buf, size_t length); -int mei_cl_register_event_cb(struct mei_cl_device *cldev, - unsigned long event_mask, - mei_cl_event_cb_t read_cb, void *context); +int mei_cldev_register_event_cb(struct mei_cl_device *cldev, + unsigned long event_mask, + mei_cldev_event_cb_t read_cb, void *context); #define MEI_CL_EVENT_RX 0 #define MEI_CL_EVENT_TX 1 @@ -87,11 +88,11 @@ int mei_cl_register_event_cb(struct mei_cl_device *cldev, const uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev); u8 mei_cldev_ver(const struct mei_cl_device *cldev); -void *mei_cl_get_drvdata(const struct mei_cl_device *cldev); -void mei_cl_set_drvdata(struct mei_cl_device *cldev, void *data); +void *mei_cldev_get_drvdata(const struct mei_cl_device *cldev); +void mei_cldev_set_drvdata(struct mei_cl_device *cldev, void *data); -int mei_cl_enable_device(struct mei_cl_device *cldev); -int mei_cl_disable_device(struct mei_cl_device *cldev); +int mei_cldev_enable(struct mei_cl_device *cldev); +int mei_cldev_disable(struct mei_cl_device *cldev); bool mei_cldev_enabled(struct mei_cl_device *cldev); #endif /* _LINUX_MEI_CL_BUS_H */ -- cgit v1.2.3 From ff6f46483f0e14b829cd5a71bafcbb1c136a84b6 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 10 Sep 2015 15:06:20 -0700 Subject: spmi: Auto-populate driver.owner in spmi_driver_register() Populate the owner field of the spmi driver when spmi_driver_register() is called in a similar fashion to how other *_driver_register() functions do it. This saves driver writers from having to do this themselves. Cc: Andy Gross Cc: Gilad Avidov Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi.c | 5 +++-- include/linux/spmi.h | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c index 11467e17bdd8..6b3da1bb0d63 100644 --- a/drivers/spmi/spmi.c +++ b/drivers/spmi/spmi.c @@ -560,12 +560,13 @@ EXPORT_SYMBOL_GPL(spmi_controller_remove); * This API will register the client driver with the SPMI framework. * It is typically called from the driver's module-init function. */ -int spmi_driver_register(struct spmi_driver *sdrv) +int __spmi_driver_register(struct spmi_driver *sdrv, struct module *owner) { sdrv->driver.bus = &spmi_bus_type; + sdrv->driver.owner = owner; return driver_register(&sdrv->driver); } -EXPORT_SYMBOL_GPL(spmi_driver_register); +EXPORT_SYMBOL_GPL(__spmi_driver_register); static void __exit spmi_exit(void) { diff --git a/include/linux/spmi.h b/include/linux/spmi.h index f84212cd3b7d..1396a255d2a2 100644 --- a/include/linux/spmi.h +++ b/include/linux/spmi.h @@ -153,7 +153,9 @@ static inline struct spmi_driver *to_spmi_driver(struct device_driver *d) return container_of(d, struct spmi_driver, driver); } -int spmi_driver_register(struct spmi_driver *sdrv); +#define spmi_driver_register(sdrv) \ + __spmi_driver_register(sdrv, THIS_MODULE) +int __spmi_driver_register(struct spmi_driver *sdrv, struct module *owner); /** * spmi_driver_unregister() - unregister an SPMI client driver -- cgit v1.2.3 From b05b7c7cc0324524dcda7fa7c2be1255290ee416 Mon Sep 17 00:00:00 2001 From: Muhammad Hamza Farooq Date: Fri, 11 Sep 2015 16:42:38 +0200 Subject: ti-st: use worker instead of calling st_int_write in wake up The wake up method is called with the port lock held. The st_int_write method calls port->ops->write with tries to acquire the lock again, causing CPU to wait infinitely. Right way to do is to write data to port in worker thread. Signed-off-by: Muhammad Hamza Farooq Signed-off-by: Jacob Siverskog Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ti-st/st_core.c | 18 ++++++++++++++++-- include/linux/ti_wilink_st.h | 1 + 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index c8c6a363069c..6e3af8b42cdd 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -460,6 +460,13 @@ static void st_int_enqueue(struct st_data_s *st_gdata, struct sk_buff *skb) * - TTY layer when write's finished * - st_write (in context of the protocol stack) */ +static void work_fn_write_wakeup(struct work_struct *work) +{ + struct st_data_s *st_gdata = container_of(work, struct st_data_s, + work_write_wakeup); + + st_tx_wakeup((void *)st_gdata); +} void st_tx_wakeup(struct st_data_s *st_data) { struct sk_buff *skb; @@ -812,8 +819,12 @@ static void st_tty_wakeup(struct tty_struct *tty) /* don't do an wakeup for now */ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - /* call our internal wakeup */ - st_tx_wakeup((void *)st_gdata); + /* + * schedule the internal wakeup instead of calling directly to + * avoid lockup (port->lock needed in tty->ops->write is + * already taken here + */ + schedule_work(&st_gdata->work_write_wakeup); } static void st_tty_flush_buffer(struct tty_struct *tty) @@ -881,6 +892,9 @@ int st_core_init(struct st_data_s **core_data) pr_err("unable to un-register ldisc"); return err; } + + INIT_WORK(&st_gdata->work_write_wakeup, work_fn_write_wakeup); + *core_data = st_gdata; return 0; } diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h index d4217eff489f..0a0d56834c8e 100644 --- a/include/linux/ti_wilink_st.h +++ b/include/linux/ti_wilink_st.h @@ -158,6 +158,7 @@ struct st_data_s { unsigned long ll_state; void *kim_data; struct tty_struct *tty; + struct work_struct work_write_wakeup; }; /* -- cgit v1.2.3 From e76e397a7f99bffbd91fb21f96370cbb165b5bcd Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 16 Sep 2015 10:42:13 +0100 Subject: mfd: arizona: Add registers for ADC microphone detection The newer devices support using a software comparison to determine whether a 3/4 pole jack is present. Add the registers necessary for this. Signed-off-by: Charles Keepax Acked-by: Lee Jones Signed-off-by: Chanwoo Choi --- drivers/mfd/wm5110-tables.c | 2 ++ include/dt-bindings/mfd/arizona.h | 2 ++ include/linux/mfd/arizona/pdata.h | 3 +++ include/linux/mfd/arizona/registers.h | 6 +++--- 4 files changed, 10 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index c4b9374efd76..5f604e00aaf1 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -1811,6 +1811,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_MIC_DETECT_1: case ARIZONA_MIC_DETECT_2: case ARIZONA_MIC_DETECT_3: + case ARIZONA_MIC_DETECT_4: case ARIZONA_MIC_DETECT_LEVEL_1: case ARIZONA_MIC_DETECT_LEVEL_2: case ARIZONA_MIC_DETECT_LEVEL_3: @@ -2847,6 +2848,7 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS: case ARIZONA_ASYNC_SAMPLE_RATE_2_STATUS: case ARIZONA_MIC_DETECT_3: + case ARIZONA_MIC_DETECT_4: case ARIZONA_HP_CTRL_1L: case ARIZONA_HP_CTRL_1R: case ARIZONA_HEADPHONE_DETECT_2: diff --git a/include/dt-bindings/mfd/arizona.h b/include/dt-bindings/mfd/arizona.h index 7b2000cead43..c40f665e2712 100644 --- a/include/dt-bindings/mfd/arizona.h +++ b/include/dt-bindings/mfd/arizona.h @@ -107,5 +107,7 @@ #define ARIZONA_ACCDET_MODE_MIC 0 #define ARIZONA_ACCDET_MODE_HPL 1 #define ARIZONA_ACCDET_MODE_HPR 2 +#define ARIZONA_ACCDET_MODE_HPM 4 +#define ARIZONA_ACCDET_MODE_ADC 7 #endif diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 1dc385850ba2..92f31ffaf866 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -124,6 +124,9 @@ struct arizona_pdata { /** Channel to use for headphone detection */ unsigned int hpdet_channel; + /** Use software comparison to determine mic presence */ + bool micd_software_compare; + /** Extra debounce timeout used during initial mic detection (ms) */ unsigned int micd_detect_debounce; diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index fdd70b3c7418..ab219c9b0b08 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -2359,9 +2359,9 @@ #define ARIZONA_ACCDET_SRC_MASK 0x2000 /* ACCDET_SRC */ #define ARIZONA_ACCDET_SRC_SHIFT 13 /* ACCDET_SRC */ #define ARIZONA_ACCDET_SRC_WIDTH 1 /* ACCDET_SRC */ -#define ARIZONA_ACCDET_MODE_MASK 0x0003 /* ACCDET_MODE - [1:0] */ -#define ARIZONA_ACCDET_MODE_SHIFT 0 /* ACCDET_MODE - [1:0] */ -#define ARIZONA_ACCDET_MODE_WIDTH 2 /* ACCDET_MODE - [1:0] */ +#define ARIZONA_ACCDET_MODE_MASK 0x0007 /* ACCDET_MODE - [2:0] */ +#define ARIZONA_ACCDET_MODE_SHIFT 0 /* ACCDET_MODE - [2:0] */ +#define ARIZONA_ACCDET_MODE_WIDTH 3 /* ACCDET_MODE - [2:0] */ /* * R667 (0x29B) - Headphone Detect 1 -- cgit v1.2.3 From 1ce376897e02e623ba357594604ca655df61053b Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 16 Sep 2015 10:42:14 +0100 Subject: mfd: arizona: Add register bits for general purpose switch The switch is typically used in conjunction with the MICDET clamp in order to suppress pops and clicks associated with jack insertion. Signed-off-by: Charles Keepax Acked-by: Lee Jones Signed-off-by: Chanwoo Choi --- drivers/mfd/wm5110-tables.c | 2 ++ include/linux/mfd/arizona/pdata.h | 3 +++ 2 files changed, 5 insertions(+) (limited to 'include') diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index 5f604e00aaf1..aae17d6997b1 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -1481,6 +1481,7 @@ static const struct reg_default wm5110_reg_default[] = { { 0x00000C04, 0xA101 }, /* R3076 - GPIO5 CTRL */ { 0x00000C0F, 0x0400 }, /* R3087 - IRQ CTRL 1 */ { 0x00000C10, 0x1000 }, /* R3088 - GPIO Debounce Config */ + { 0x00000C18, 0x0000 }, /* R3096 - GP Switch 1 */ { 0x00000C20, 0x8002 }, /* R3104 - Misc Pad Ctrl 1 */ { 0x00000C21, 0x8001 }, /* R3105 - Misc Pad Ctrl 2 */ { 0x00000C22, 0x0000 }, /* R3106 - Misc Pad Ctrl 3 */ @@ -2528,6 +2529,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_GPIO5_CTRL: case ARIZONA_IRQ_CTRL_1: case ARIZONA_GPIO_DEBOUNCE_CONFIG: + case ARIZONA_GP_SWITCH_1: case ARIZONA_MISC_PAD_CTRL_1: case ARIZONA_MISC_PAD_CTRL_2: case ARIZONA_MISC_PAD_CTRL_3: diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 92f31ffaf866..57b45caaea80 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -184,6 +184,9 @@ struct arizona_pdata { /** GPIO for primary IRQ (used for edge triggered emulation) */ int irq_gpio; + + /** General purpose switch control */ + unsigned int gpsw; }; #endif -- cgit v1.2.3 From b8ba9edb911102b452c815879f520bfe2713fab6 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 16 Sep 2015 10:42:15 +0100 Subject: mfd: arizona: Add TST_CAP bits for headphone detection On wm5110/8280 some additional settings are required to get accurate measurements at the top end of the headphone detection range. This patch adds the bits required for this. Signed-off-by: Charles Keepax Acked-by: Lee Jones Signed-off-by: Chanwoo Choi --- drivers/mfd/wm5110-tables.c | 2 ++ include/linux/mfd/arizona/registers.h | 8 ++++++++ 2 files changed, 10 insertions(+) (limited to 'include') diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index aae17d6997b1..28f2ae30507a 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -1912,6 +1912,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_HP1_SHORT_CIRCUIT_CTRL: case ARIZONA_HP2_SHORT_CIRCUIT_CTRL: case ARIZONA_HP3_SHORT_CIRCUIT_CTRL: + case ARIZONA_HP_TEST_CTRL_1: case ARIZONA_AIF1_BCLK_CTRL: case ARIZONA_AIF1_TX_PIN_CTRL: case ARIZONA_AIF1_RX_PIN_CTRL: @@ -2857,6 +2858,7 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_INPUT_ENABLES_STATUS: case ARIZONA_OUTPUT_STATUS_1: case ARIZONA_RAW_OUTPUT_STATUS_1: + case ARIZONA_HP_TEST_CTRL_1: case ARIZONA_SLIMBUS_RX_PORT_STATUS: case ARIZONA_SLIMBUS_TX_PORT_STATUS: case ARIZONA_INTERRUPT_STATUS_1: diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index ab219c9b0b08..c7c11c900196 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -242,6 +242,7 @@ #define ARIZONA_HP1_SHORT_CIRCUIT_CTRL 0x4A0 #define ARIZONA_HP2_SHORT_CIRCUIT_CTRL 0x4A1 #define ARIZONA_HP3_SHORT_CIRCUIT_CTRL 0x4A2 +#define ARIZONA_HP_TEST_CTRL_1 0x4A4 #define ARIZONA_SPK_CTRL_2 0x4B5 #define ARIZONA_SPK_CTRL_3 0x4B6 #define ARIZONA_DAC_COMP_1 0x4DC @@ -3701,6 +3702,13 @@ #define ARIZONA_HP3_SC_ENA_SHIFT 12 /* HP3_SC_ENA */ #define ARIZONA_HP3_SC_ENA_WIDTH 1 /* HP3_SC_ENA */ +/* + * R1188 (0x4A4) HP Test Ctrl 1 + */ +#define ARIZONA_HP1_TST_CAP_SEL_MASK 0x0003 /* HP1_TST_CAP_SEL - [1:0] */ +#define ARIZONA_HP1_TST_CAP_SEL_SHIFT 0 /* HP1_TST_CAP_SEL - [1:0] */ +#define ARIZONA_HP1_TST_CAP_SEL_WIDTH 2 /* HP1_TST_CAP_SEL - [1:0] */ + /* * R1244 (0x4DC) - DAC comp 1 */ -- cgit v1.2.3 From ce6f7496050e29108bcefe0413df1789648fb38f Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 29 Sep 2015 22:57:24 +0900 Subject: extcon: gpio: Add the missing supported_cable parameter to devm_extcon_dev_allocate() The commit 2a9de9c0 ("extcon: Use the unique id for external connector instead of string") defines the unique id of each external connector to identify the type of external connector instead of string name. So, devm_extcon_dev_allocate() should include the second parameter (unsigned int *supported_cable). This patch adds the supported_cable parameter which is passed by platform data. Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-gpio.c | 6 ++---- include/linux/extcon/extcon-gpio.h | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index 2a89c89dfaf5..ba927df2fb91 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -70,17 +70,15 @@ static int gpio_extcon_probe(struct platform_device *pdev) if (!pdata) return -EBUSY; - if (!pdata->irq_flags) { - dev_err(&pdev->dev, "IRQ flag is not specified.\n"); + if (!pdata->irq_flags || pdata->extcon_id > EXTCON_NONE) return -EINVAL; - } data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data), GFP_KERNEL); if (!data) return -ENOMEM; - data->edev = devm_extcon_dev_allocate(&pdev->dev, NULL); + data->edev = devm_extcon_dev_allocate(&pdev->dev, &pdata->extcon_id); if (IS_ERR(data->edev)) { dev_err(&pdev->dev, "failed to allocate extcon device\n"); return -ENOMEM; diff --git a/include/linux/extcon/extcon-gpio.h b/include/linux/extcon/extcon-gpio.h index 0b17ad43fbfc..232bb8f80b51 100644 --- a/include/linux/extcon/extcon-gpio.h +++ b/include/linux/extcon/extcon-gpio.h @@ -26,6 +26,7 @@ /** * struct gpio_extcon_platform_data - A simple GPIO-controlled extcon device. * @name: The name of this GPIO extcon device. + * @extcon_id: The unique id of specific external connector. * @gpio: Corresponding GPIO. * @gpio_active_low: Boolean describing whether gpio active state is 1 or 0 * If true, low state of gpio means active. @@ -45,6 +46,7 @@ */ struct gpio_extcon_platform_data { const char *name; + unsigned int extcon_id; unsigned gpio; bool gpio_active_low; unsigned long debounce; -- cgit v1.2.3 From 6ba129974a3d6bcb236cbd96aa1db6001d163678 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 29 Sep 2015 22:59:55 +0900 Subject: extcon: gpio: Fix minor coding style and remove the unused fields. This patch fixes the minor coding style about indentation and removes the unused fields from struct gpio_extcon_platform_data. Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-gpio.c | 11 ++++------- include/linux/extcon/extcon-gpio.h | 22 ++++------------------ 2 files changed, 8 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index ba927df2fb91..27de6aee890f 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -1,7 +1,5 @@ /* - * drivers/extcon/extcon_gpio.c - * - * Single-state GPIO extcon driver based on extcon class + * extcon_gpio.c - Single-state GPIO extcon driver based on extcon class * * Copyright (C) 2008 Google, Inc. * Author: Mike Lockwood @@ -17,8 +15,7 @@ * 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 #include @@ -37,7 +34,7 @@ struct gpio_extcon_data { struct delayed_work work; unsigned long debounce_jiffies; - struct gpio_extcon_platform_data *pdata; + struct gpio_extcon_pdata *pdata; }; static void gpio_extcon_work(struct work_struct *work) @@ -64,7 +61,7 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id) static int gpio_extcon_probe(struct platform_device *pdev) { - struct gpio_extcon_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct gpio_extcon_pdata *pdata = dev_get_platdata(&pdev->dev); struct gpio_extcon_data *data; int ret; diff --git a/include/linux/extcon/extcon-gpio.h b/include/linux/extcon/extcon-gpio.h index 232bb8f80b51..7cacafb78b09 100644 --- a/include/linux/extcon/extcon-gpio.h +++ b/include/linux/extcon/extcon-gpio.h @@ -1,5 +1,5 @@ /* - * External connector (extcon) class generic GPIO driver + * Single-state GPIO extcon driver based on extcon class * * Copyright (C) 2012 Samsung Electronics * Author: MyungJoo Ham @@ -16,16 +16,14 @@ * 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. - * -*/ + */ #ifndef __EXTCON_GPIO_H__ #define __EXTCON_GPIO_H__ __FILE__ #include /** - * struct gpio_extcon_platform_data - A simple GPIO-controlled extcon device. - * @name: The name of this GPIO extcon device. + * struct gpio_extcon_pdata - A simple GPIO-controlled extcon device. * @extcon_id: The unique id of specific external connector. * @gpio: Corresponding GPIO. * @gpio_active_low: Boolean describing whether gpio active state is 1 or 0 @@ -33,28 +31,16 @@ * If false, high state of gpio means active. * @debounce: Debounce time for GPIO IRQ in ms. * @irq_flags: IRQ Flags (e.g., IRQF_TRIGGER_LOW). - * @state_on: print_state is overriden with state_on if attached. - * If NULL, default method of extcon class is used. - * @state_off: print_state is overriden with state_off if detached. - * If NUll, default method of extcon class is used. * @check_on_resume: Boolean describing whether to check the state of gpio * while resuming from sleep. - * - * Note that in order for state_on or state_off to be valid, both state_on - * and state_off should be not NULL. If at least one of them is NULL, - * the print_state is not overriden. */ -struct gpio_extcon_platform_data { - const char *name; +struct gpio_extcon_pdata { unsigned int extcon_id; unsigned gpio; bool gpio_active_low; unsigned long debounce; unsigned long irq_flags; - /* if NULL, "0" or "1" will be printed */ - const char *state_on; - const char *state_off; bool check_on_resume; }; -- cgit v1.2.3 From 09e8b485ee4212f78bf27c8d727845919f85cf1f Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 29 Sep 2015 18:07:02 -0700 Subject: iommu: iova: Move iova cache management to the iova library This is necessary to separate intel-iommu from the iova library. Signed-off-by: Sakari Ailus Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/intel-iommu.c | 6 ++-- drivers/iommu/iova.c | 83 ++++++++++++++++++++++++++------------------- include/linux/iova.h | 4 +-- 3 files changed, 54 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 2d7349a3ee14..7b27bcdc30ad 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -3711,7 +3711,7 @@ static inline int iommu_devinfo_cache_init(void) static int __init iommu_init_mempool(void) { int ret; - ret = iommu_iova_cache_init(); + ret = iova_cache_get(); if (ret) return ret; @@ -3725,7 +3725,7 @@ static int __init iommu_init_mempool(void) kmem_cache_destroy(iommu_domain_cache); domain_error: - iommu_iova_cache_destroy(); + iova_cache_put(); return -ENOMEM; } @@ -3734,7 +3734,7 @@ static void __init iommu_exit_mempool(void) { kmem_cache_destroy(iommu_devinfo_cache); kmem_cache_destroy(iommu_domain_cache); - iommu_iova_cache_destroy(); + iova_cache_put(); } static void quirk_ioat_snb_local_iommu(struct pci_dev *pdev) diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c index b7c3d923f3e1..400f4d1b566f 100644 --- a/drivers/iommu/iova.c +++ b/drivers/iommu/iova.c @@ -20,40 +20,6 @@ #include #include -static struct kmem_cache *iommu_iova_cache; - -int iommu_iova_cache_init(void) -{ - int ret = 0; - - iommu_iova_cache = kmem_cache_create("iommu_iova", - sizeof(struct iova), - 0, - SLAB_HWCACHE_ALIGN, - NULL); - if (!iommu_iova_cache) { - pr_err("Couldn't create iova cache\n"); - ret = -ENOMEM; - } - - return ret; -} - -void iommu_iova_cache_destroy(void) -{ - kmem_cache_destroy(iommu_iova_cache); -} - -struct iova *alloc_iova_mem(void) -{ - return kmem_cache_alloc(iommu_iova_cache, GFP_ATOMIC); -} - -void free_iova_mem(struct iova *iova) -{ - kmem_cache_free(iommu_iova_cache, iova); -} - void init_iova_domain(struct iova_domain *iovad, unsigned long granule, unsigned long start_pfn, unsigned long pfn_32bit) @@ -242,6 +208,55 @@ iova_insert_rbtree(struct rb_root *root, struct iova *iova) rb_insert_color(&iova->node, root); } +static struct kmem_cache *iova_cache; +static unsigned int iova_cache_users; +static DEFINE_MUTEX(iova_cache_mutex); + +struct iova *alloc_iova_mem(void) +{ + return kmem_cache_alloc(iova_cache, GFP_ATOMIC); +} +EXPORT_SYMBOL(alloc_iova_mem); + +void free_iova_mem(struct iova *iova) +{ + kmem_cache_free(iova_cache, iova); +} +EXPORT_SYMBOL(free_iova_mem); + +int iova_cache_get(void) +{ + mutex_lock(&iova_cache_mutex); + if (!iova_cache_users) { + iova_cache = kmem_cache_create( + "iommu_iova", sizeof(struct iova), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!iova_cache) { + mutex_unlock(&iova_cache_mutex); + printk(KERN_ERR "Couldn't create iova cache\n"); + return -ENOMEM; + } + } + + iova_cache_users++; + mutex_unlock(&iova_cache_mutex); + + return 0; +} + +void iova_cache_put(void) +{ + mutex_lock(&iova_cache_mutex); + if (WARN_ON(!iova_cache_users)) { + mutex_unlock(&iova_cache_mutex); + return; + } + iova_cache_users--; + if (!iova_cache_users) + kmem_cache_destroy(iova_cache); + mutex_unlock(&iova_cache_mutex); +} + /** * alloc_iova - allocates an iova * @iovad: - iova domain in question diff --git a/include/linux/iova.h b/include/linux/iova.h index 3920a19d8194..92f7177db2ce 100644 --- a/include/linux/iova.h +++ b/include/linux/iova.h @@ -68,8 +68,8 @@ static inline unsigned long iova_pfn(struct iova_domain *iovad, dma_addr_t iova) return iova >> iova_shift(iovad); } -int iommu_iova_cache_init(void); -void iommu_iova_cache_destroy(void); +int iova_cache_get(void); +void iova_cache_put(void); struct iova *alloc_iova_mem(void); void free_iova_mem(struct iova *iova); -- cgit v1.2.3 From ff39988abd70bcd1b14a4c81f2d102e67b8db580 Mon Sep 17 00:00:00 2001 From: Siva Yerramreddy Date: Tue, 29 Sep 2015 18:09:37 -0700 Subject: dma: Add support to program MIC x100 status descriptiors The MIC X100 DMA engine has a special status descriptor which writes an 8 byte value to a destination location. This is used to signal completion of all DMA descriptors prior to the status descriptor. This patch add a new DMA engine API which enables updating a destination address with an 8 byte immediate data value. Reviewed-by: Nikhil Rao Reviewed-by: Ashutosh Dixit Signed-off-by: Lawrynowicz, Jacek Signed-off-by: Sudeep Dutt Signed-off-by: Siva Yerramreddy Signed-off-by: Greg Kroah-Hartman --- drivers/dma/mic_x100_dma.c | 39 ++++++++++++++++++++++++++++++++++++++- include/linux/dmaengine.h | 4 ++++ 2 files changed, 42 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/dma/mic_x100_dma.c b/drivers/dma/mic_x100_dma.c index 74d9db05a5ad..068e920ecb68 100644 --- a/drivers/dma/mic_x100_dma.c +++ b/drivers/dma/mic_x100_dma.c @@ -193,8 +193,16 @@ static void mic_dma_prog_intr(struct mic_dma_chan *ch) static int mic_dma_do_dma(struct mic_dma_chan *ch, int flags, dma_addr_t src, dma_addr_t dst, size_t len) { - if (-ENOMEM == mic_dma_prog_memcpy_desc(ch, src, dst, len)) + if (len && -ENOMEM == mic_dma_prog_memcpy_desc(ch, src, dst, len)) { return -ENOMEM; + } else { + /* 3 is the maximum number of status descriptors */ + int ret = mic_dma_avail_desc_ring_space(ch, 3); + + if (ret < 0) + return ret; + } + /* Above mic_dma_prog_memcpy_desc() makes sure we have enough space */ if (flags & DMA_PREP_FENCE) { mic_dma_prep_status_desc(&ch->desc_ring[ch->head], 0, @@ -270,6 +278,33 @@ allocate_tx(struct mic_dma_chan *ch) return tx; } +/* Program a status descriptor with dst as address and value to be written */ +static struct dma_async_tx_descriptor * +mic_dma_prep_status_lock(struct dma_chan *ch, dma_addr_t dst, u64 src_val, + unsigned long flags) +{ + struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); + int result; + + spin_lock(&mic_ch->prep_lock); + result = mic_dma_avail_desc_ring_space(mic_ch, 4); + if (result < 0) + goto error; + mic_dma_prep_status_desc(&mic_ch->desc_ring[mic_ch->head], src_val, dst, + false); + mic_dma_hw_ring_inc_head(mic_ch); + result = mic_dma_do_dma(mic_ch, flags, 0, 0, 0); + if (result < 0) + goto error; + + return allocate_tx(mic_ch); +error: + dev_err(mic_dma_ch_to_device(mic_ch), + "Error enqueueing dma status descriptor, error=%d\n", result); + spin_unlock(&mic_ch->prep_lock); + return NULL; +} + /* * Prepare a memcpy descriptor to be added to the ring. * Note that the temporary descriptor adds an extra overhead of copying the @@ -587,6 +622,8 @@ static int mic_dma_register_dma_device(struct mic_dma_device *mic_dma_dev, mic_dma_free_chan_resources; mic_dma_dev->dma_dev.device_tx_status = mic_dma_tx_status; mic_dma_dev->dma_dev.device_prep_dma_memcpy = mic_dma_prep_memcpy_lock; + mic_dma_dev->dma_dev.device_prep_dma_imm_data = + mic_dma_prep_status_lock; mic_dma_dev->dma_dev.device_prep_dma_interrupt = mic_dma_prep_interrupt_lock; mic_dma_dev->dma_dev.device_issue_pending = mic_dma_issue_pending; diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 7ea9184eaa13..c47c68e535e8 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -645,6 +645,7 @@ enum dmaengine_alignment { * The function takes a buffer of size buf_len. The callback function will * be called after period_len bytes have been transferred. * @device_prep_interleaved_dma: Transfer expression in a generic way. + * @device_prep_dma_imm_data: DMA's 8 byte immediate data to the dst address * @device_config: Pushes a new configuration to a channel, return 0 or an error * code * @device_pause: Pauses any transfer happening on a channel. Returns @@ -727,6 +728,9 @@ struct dma_device { struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)( struct dma_chan *chan, struct dma_interleaved_template *xt, unsigned long flags); + struct dma_async_tx_descriptor *(*device_prep_dma_imm_data)( + struct dma_chan *chan, dma_addr_t dst, u64 data, + unsigned long flags); int (*device_config)(struct dma_chan *chan, struct dma_slave_config *config); -- cgit v1.2.3 From b7f944411b4a628443f84a542858a8c78847bb48 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 29 Sep 2015 18:10:44 -0700 Subject: misc: mic: SCIF poll SCIF poll allows both user and kernel mode clients to wait on events on a SCIF endpoint. These events include availability of space or data in the SCIF ring buffer, availability of connection requests on a listening endpoint and completion of connections when using async connects. Reviewed-by: Nikhil Rao Reviewed-by: Sudeep Dutt Signed-off-by: Ashutosh Dixit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_api.c | 158 +++++++++++++++++++++++++++++++++++++- drivers/misc/mic/scif/scif_epd.h | 22 ++++++ drivers/misc/mic/scif/scif_fd.c | 9 +++ drivers/misc/mic/scif/scif_main.h | 2 + include/linux/scif.h | 74 ++++++++++++++++++ 5 files changed, 264 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/misc/mic/scif/scif_api.c b/drivers/misc/mic/scif/scif_api.c index f39d3135a9ef..bf2d70fcd055 100644 --- a/drivers/misc/mic/scif/scif_api.c +++ b/drivers/misc/mic/scif/scif_api.c @@ -37,9 +37,21 @@ enum conn_async_state { ASYNC_CONN_FLUSH_WORK /* async work flush in progress */ }; +/* + * File operations for anonymous inode file associated with a SCIF endpoint, + * used in kernel mode SCIF poll. Kernel mode SCIF poll calls portions of the + * poll API in the kernel and these take in a struct file *. Since a struct + * file is not available to kernel mode SCIF, it uses an anonymous file for + * this purpose. + */ +const struct file_operations scif_anon_fops = { + .owner = THIS_MODULE, +}; + scif_epd_t scif_open(void) { struct scif_endpt *ep; + int err; might_sleep(); ep = kzalloc(sizeof(*ep), GFP_KERNEL); @@ -50,6 +62,10 @@ scif_epd_t scif_open(void) if (!ep->qp_info.qp) goto err_qp_alloc; + err = scif_anon_inode_getfile(ep); + if (err) + goto err_anon_inode; + spin_lock_init(&ep->lock); mutex_init(&ep->sendlock); mutex_init(&ep->recvlock); @@ -59,6 +75,8 @@ scif_epd_t scif_open(void) "SCIFAPI open: ep %p success\n", ep); return ep; +err_anon_inode: + kfree(ep->qp_info.qp); err_qp_alloc: kfree(ep); err_ep_alloc: @@ -279,6 +297,7 @@ int scif_close(scif_epd_t epd) } } scif_put_port(ep->port.port); + scif_anon_inode_fput(ep); scif_teardown_ep(ep); scif_add_epd_to_zombie_list(ep, !SCIF_EPLOCK_HELD); return 0; @@ -558,8 +577,10 @@ void scif_conn_handler(struct work_struct *work) list_del(&ep->conn_list); } spin_unlock(&scif_info.nb_connect_lock); - if (ep) + if (ep) { ep->conn_err = scif_conn_func(ep); + wake_up_interruptible(&ep->conn_pend_wq); + } } while (ep); } @@ -660,6 +681,7 @@ int __scif_connect(scif_epd_t epd, struct scif_port_id *dst, bool non_block) ep->remote_dev = &scif_dev[dst->node]; ep->qp_info.qp->magic = SCIFEP_MAGIC; if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) { + init_waitqueue_head(&ep->conn_pend_wq); spin_lock(&scif_info.nb_connect_lock); list_add_tail(&ep->conn_list, &scif_info.nb_connect_list); spin_unlock(&scif_info.nb_connect_lock); @@ -788,6 +810,10 @@ retry_connection: goto scif_accept_error_qpalloc; } + err = scif_anon_inode_getfile(cep); + if (err) + goto scif_accept_error_anon_inode; + cep->qp_info.qp->magic = SCIFEP_MAGIC; spdev = scif_get_peer_dev(cep->remote_dev); if (IS_ERR(spdev)) { @@ -858,6 +884,8 @@ retry: spin_unlock(&cep->lock); return 0; scif_accept_error_map: + scif_anon_inode_fput(cep); +scif_accept_error_anon_inode: scif_teardown_ep(cep); scif_accept_error_qpalloc: kfree(cep); @@ -1247,6 +1275,134 @@ int scif_recv(scif_epd_t epd, void *msg, int len, int flags) } EXPORT_SYMBOL_GPL(scif_recv); +static inline void _scif_poll_wait(struct file *f, wait_queue_head_t *wq, + poll_table *p, struct scif_endpt *ep) +{ + /* + * Because poll_wait makes a GFP_KERNEL allocation, give up the lock + * and regrab it afterwards. Because the endpoint state might have + * changed while the lock was given up, the state must be checked + * again after re-acquiring the lock. The code in __scif_pollfd(..) + * does this. + */ + spin_unlock(&ep->lock); + poll_wait(f, wq, p); + spin_lock(&ep->lock); +} + +unsigned int +__scif_pollfd(struct file *f, poll_table *wait, struct scif_endpt *ep) +{ + unsigned int mask = 0; + + dev_dbg(scif_info.mdev.this_device, + "SCIFAPI pollfd: ep %p %s\n", ep, scif_ep_states[ep->state]); + + spin_lock(&ep->lock); + + /* Endpoint is waiting for a non-blocking connect to complete */ + if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) { + _scif_poll_wait(f, &ep->conn_pend_wq, wait, ep); + if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) { + if (ep->state == SCIFEP_CONNECTED || + ep->state == SCIFEP_DISCONNECTED || + ep->conn_err) + mask |= POLLOUT; + goto exit; + } + } + + /* Endpoint is listening for incoming connection requests */ + if (ep->state == SCIFEP_LISTENING) { + _scif_poll_wait(f, &ep->conwq, wait, ep); + if (ep->state == SCIFEP_LISTENING) { + if (ep->conreqcnt) + mask |= POLLIN; + goto exit; + } + } + + /* Endpoint is connected or disconnected */ + if (ep->state == SCIFEP_CONNECTED || ep->state == SCIFEP_DISCONNECTED) { + if (poll_requested_events(wait) & POLLIN) + _scif_poll_wait(f, &ep->recvwq, wait, ep); + if (poll_requested_events(wait) & POLLOUT) + _scif_poll_wait(f, &ep->sendwq, wait, ep); + if (ep->state == SCIFEP_CONNECTED || + ep->state == SCIFEP_DISCONNECTED) { + /* Data can be read without blocking */ + if (scif_rb_count(&ep->qp_info.qp->inbound_q, 1)) + mask |= POLLIN; + /* Data can be written without blocking */ + if (scif_rb_space(&ep->qp_info.qp->outbound_q)) + mask |= POLLOUT; + /* Return POLLHUP if endpoint is disconnected */ + if (ep->state == SCIFEP_DISCONNECTED) + mask |= POLLHUP; + goto exit; + } + } + + /* Return POLLERR if the endpoint is in none of the above states */ + mask |= POLLERR; +exit: + spin_unlock(&ep->lock); + return mask; +} + +/** + * scif_poll() - Kernel mode SCIF poll + * @ufds: Array of scif_pollepd structures containing the end points + * and events to poll on + * @nfds: Size of the ufds array + * @timeout_msecs: Timeout in msecs, -ve implies infinite timeout + * + * The code flow in this function is based on do_poll(..) in select.c + * + * Returns the number of endpoints which have pending events or 0 in + * the event of a timeout. If a signal is used for wake up, -EINTR is + * returned. + */ +int +scif_poll(struct scif_pollepd *ufds, unsigned int nfds, long timeout_msecs) +{ + struct poll_wqueues table; + poll_table *pt; + int i, mask, count = 0, timed_out = timeout_msecs == 0; + u64 timeout = timeout_msecs < 0 ? MAX_SCHEDULE_TIMEOUT + : msecs_to_jiffies(timeout_msecs); + + poll_initwait(&table); + pt = &table.pt; + while (1) { + for (i = 0; i < nfds; i++) { + pt->_key = ufds[i].events | POLLERR | POLLHUP; + mask = __scif_pollfd(ufds[i].epd->anon, + pt, ufds[i].epd); + mask &= ufds[i].events | POLLERR | POLLHUP; + if (mask) { + count++; + pt->_qproc = NULL; + } + ufds[i].revents = mask; + } + pt->_qproc = NULL; + if (!count) { + count = table.error; + if (signal_pending(current)) + count = -EINTR; + } + if (count || timed_out) + break; + + if (!schedule_timeout_interruptible(timeout)) + timed_out = 1; + } + poll_freewait(&table); + return count; +} +EXPORT_SYMBOL_GPL(scif_poll); + int scif_get_node_ids(u16 *nodes, int len, u16 *self) { int online = 0; diff --git a/drivers/misc/mic/scif/scif_epd.h b/drivers/misc/mic/scif/scif_epd.h index 331322a25213..4dc4ccdbc236 100644 --- a/drivers/misc/mic/scif/scif_epd.h +++ b/drivers/misc/mic/scif/scif_epd.h @@ -96,7 +96,9 @@ struct scif_endpt_qp_info { * @conn_port: Connection port * @conn_err: Errors during connection * @conn_async_state: Async connection + * @conn_pend_wq: Used by poll while waiting for incoming connections * @conn_list: List of async connection requests + * @anon: anonymous file for use in kernel mode scif poll */ struct scif_endpt { enum scif_epd_state state; @@ -125,7 +127,9 @@ struct scif_endpt { struct scif_port_id conn_port; int conn_err; int conn_async_state; + wait_queue_head_t conn_pend_wq; struct list_head conn_list; + struct file *anon; }; static inline int scifdev_alive(struct scif_endpt *ep) @@ -133,6 +137,22 @@ static inline int scifdev_alive(struct scif_endpt *ep) return _scifdev_alive(ep->remote_dev); } +static inline int scif_anon_inode_getfile(scif_epd_t epd) +{ + epd->anon = anon_inode_getfile("scif", &scif_anon_fops, NULL, 0); + if (IS_ERR(epd->anon)) + return PTR_ERR(epd->anon); + return 0; +} + +static inline void scif_anon_inode_fput(scif_epd_t epd) +{ + if (epd->anon) { + fput(epd->anon); + epd->anon = NULL; + } +} + void scif_cleanup_zombie_epd(void); void scif_teardown_ep(void *endpt); void scif_cleanup_ep_qp(struct scif_endpt *ep); @@ -157,4 +177,6 @@ void scif_clientsend(struct scif_dev *scifdev, struct scifmsg *msg); void scif_clientrcvd(struct scif_dev *scifdev, struct scifmsg *msg); int __scif_connect(scif_epd_t epd, struct scif_port_id *dst, bool non_block); int __scif_flush(scif_epd_t epd); +unsigned int __scif_pollfd(struct file *f, poll_table *wait, + struct scif_endpt *ep); #endif /* SCIF_EPD_H */ diff --git a/drivers/misc/mic/scif/scif_fd.c b/drivers/misc/mic/scif/scif_fd.c index eccf7e7135f9..24e47f73e45a 100644 --- a/drivers/misc/mic/scif/scif_fd.c +++ b/drivers/misc/mic/scif/scif_fd.c @@ -34,6 +34,13 @@ static int scif_fdclose(struct inode *inode, struct file *f) return scif_close(priv); } +static unsigned int scif_fdpoll(struct file *f, poll_table *wait) +{ + struct scif_endpt *priv = f->private_data; + + return __scif_pollfd(f, wait, priv); +} + static int scif_fdflush(struct file *f, fl_owner_t id) { struct scif_endpt *ep = f->private_data; @@ -193,6 +200,7 @@ static long scif_fdioctl(struct file *f, unsigned int cmd, unsigned long arg) spin_unlock(&scif_info.eplock); /* Free the resources automatically created from the open. */ + scif_anon_inode_fput(priv); scif_teardown_ep(priv); scif_add_epd_to_zombie_list(priv, !SCIF_EPLOCK_HELD); f->private_data = newep; @@ -298,6 +306,7 @@ const struct file_operations scif_fops = { .open = scif_fdopen, .release = scif_fdclose, .unlocked_ioctl = scif_fdioctl, + .poll = scif_fdpoll, .flush = scif_fdflush, .owner = THIS_MODULE, }; diff --git a/drivers/misc/mic/scif/scif_main.h b/drivers/misc/mic/scif/scif_main.h index 580bc63e1b23..87c13279a8f0 100644 --- a/drivers/misc/mic/scif/scif_main.h +++ b/drivers/misc/mic/scif/scif_main.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -184,6 +185,7 @@ extern struct scif_info scif_info; extern struct idr scif_ports; extern struct scif_dev *scif_dev; extern const struct file_operations scif_fops; +extern const struct file_operations scif_anon_fops; /* Size of the RB for the Node QP */ #define SCIF_NODE_QP_SIZE 0x10000 diff --git a/include/linux/scif.h b/include/linux/scif.h index 44f4f3898bbe..b1923ad2ddd3 100644 --- a/include/linux/scif.h +++ b/include/linux/scif.h @@ -93,6 +93,18 @@ enum { typedef struct scif_endpt *scif_epd_t; +/** + * struct scif_pollepd - SCIF endpoint to be monitored via scif_poll + * @epd: SCIF endpoint + * @events: requested events + * @revents: returned events + */ +struct scif_pollepd { + scif_epd_t epd; + short events; + short revents; +}; + #define SCIF_OPEN_FAILED ((scif_epd_t)-1) #define SCIF_REGISTER_FAILED ((off_t)-1) #define SCIF_MMAP_FAILED ((void *)-1) @@ -990,4 +1002,66 @@ int scif_fence_signal(scif_epd_t epd, off_t loff, u64 lval, off_t roff, */ int scif_get_node_ids(u16 *nodes, int len, u16 *self); +/** + * scif_poll() - Wait for some event on an endpoint + * @epds: Array of endpoint descriptors + * @nepds: Length of epds + * @timeout: Upper limit on time for which scif_poll() will block + * + * scif_poll() waits for one of a set of endpoints to become ready to perform + * an I/O operation. + * + * The epds argument specifies the endpoint descriptors to be examined and the + * events of interest for each endpoint descriptor. epds is a pointer to an + * array with one member for each open endpoint descriptor of interest. + * + * The number of items in the epds array is specified in nepds. The epd field + * of scif_pollepd is an endpoint descriptor of an open endpoint. The field + * events is a bitmask specifying the events which the application is + * interested in. The field revents is an output parameter, filled by the + * kernel with the events that actually occurred. The bits returned in revents + * can include any of those specified in events, or one of the values POLLERR, + * POLLHUP, or POLLNVAL. (These three bits are meaningless in the events + * field, and will be set in the revents field whenever the corresponding + * condition is true.) + * + * If none of the events requested (and no error) has occurred for any of the + * endpoint descriptors, then scif_poll() blocks until one of the events occurs. + * + * The timeout argument specifies an upper limit on the time for which + * scif_poll() will block, in milliseconds. Specifying a negative value in + * timeout means an infinite timeout. + * + * The following bits may be set in events and returned in revents. + * POLLIN - Data may be received without blocking. For a connected + * endpoint, this means that scif_recv() may be called without blocking. For a + * listening endpoint, this means that scif_accept() may be called without + * blocking. + * POLLOUT - Data may be sent without blocking. For a connected endpoint, this + * means that scif_send() may be called without blocking. POLLOUT may also be + * used to block waiting for a non-blocking connect to complete. This bit value + * has no meaning for a listening endpoint and is ignored if specified. + * + * The following bits are only returned in revents, and are ignored if set in + * events. + * POLLERR - An error occurred on the endpoint + * POLLHUP - The connection to the peer endpoint was disconnected + * POLLNVAL - The specified endpoint descriptor is invalid. + * + * Return: + * Upon successful completion, scif_poll() returns a non-negative value. A + * positive value indicates the total number of endpoint descriptors that have + * been selected (that is, endpoint descriptors for which the revents member is + * non-zero). A value of 0 indicates that the call timed out and no endpoint + * descriptors have been selected. Otherwise in user mode -1 is returned and + * errno is set to indicate the error; in kernel mode the negative of one of + * the following errors is returned. + * + * Errors: + * EINTR - A signal occurred before any requested event + * EINVAL - The nepds argument is greater than {OPEN_MAX} + * ENOMEM - There was no space to allocate file descriptor tables + */ +int scif_poll(struct scif_pollepd *epds, unsigned int nepds, long timeout); + #endif /* __SCIF_H__ */ -- cgit v1.2.3 From d3d912eb7386b7512f131b34407978cecccb3703 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 29 Sep 2015 18:11:15 -0700 Subject: misc: mic: Add support for kernel mode SCIF clients Add support for registration/de-registration of kernel mode SCIF clients. SCIF clients are probed with new and existing SCIF peer devices. Similarly the client remove method is called when SCIF peer devices are removed. Changes to SCIF peer device framework necessitated by supporting kernel mode SCIF clients are also included in this patch. Reviewed-by: Nikhil Rao Reviewed-by: Sudeep Dutt Signed-off-by: Ashutosh Dixit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_api.c | 43 +++++++++ drivers/misc/mic/scif/scif_main.c | 88 ++++-------------- drivers/misc/mic/scif/scif_main.h | 5 +- drivers/misc/mic/scif/scif_nm.c | 10 +-- drivers/misc/mic/scif/scif_nodeqp.c | 65 +++++--------- drivers/misc/mic/scif/scif_peer_bus.c | 165 +++++++++++++++++++++------------- drivers/misc/mic/scif/scif_peer_bus.h | 42 +-------- include/linux/scif.h | 58 ++++++++++++ 8 files changed, 255 insertions(+), 221 deletions(-) (limited to 'include') diff --git a/drivers/misc/mic/scif/scif_api.c b/drivers/misc/mic/scif/scif_api.c index bf2d70fcd055..b47d56d5d93c 100644 --- a/drivers/misc/mic/scif/scif_api.c +++ b/drivers/misc/mic/scif/scif_api.c @@ -1430,3 +1430,46 @@ int scif_get_node_ids(u16 *nodes, int len, u16 *self) return online; } EXPORT_SYMBOL_GPL(scif_get_node_ids); + +static int scif_add_client_dev(struct device *dev, struct subsys_interface *si) +{ + struct scif_client *client = + container_of(si, struct scif_client, si); + struct scif_peer_dev *spdev = + container_of(dev, struct scif_peer_dev, dev); + + if (client->probe) + client->probe(spdev); + return 0; +} + +static void scif_remove_client_dev(struct device *dev, + struct subsys_interface *si) +{ + struct scif_client *client = + container_of(si, struct scif_client, si); + struct scif_peer_dev *spdev = + container_of(dev, struct scif_peer_dev, dev); + + if (client->remove) + client->remove(spdev); +} + +void scif_client_unregister(struct scif_client *client) +{ + subsys_interface_unregister(&client->si); +} +EXPORT_SYMBOL_GPL(scif_client_unregister); + +int scif_client_register(struct scif_client *client) +{ + struct subsys_interface *si = &client->si; + + si->name = client->name; + si->subsys = &scif_peer_bus; + si->add_dev = scif_add_client_dev; + si->remove_dev = scif_remove_client_dev; + + return subsys_interface_register(&client->si); +} +EXPORT_SYMBOL_GPL(scif_client_register); diff --git a/drivers/misc/mic/scif/scif_main.c b/drivers/misc/mic/scif/scif_main.c index 6ce851f5c7e6..f90bd06900cb 100644 --- a/drivers/misc/mic/scif/scif_main.c +++ b/drivers/misc/mic/scif/scif_main.c @@ -80,35 +80,6 @@ irqreturn_t scif_intr_handler(int irq, void *data) return IRQ_HANDLED; } -static int scif_peer_probe(struct scif_peer_dev *spdev) -{ - struct scif_dev *scifdev = &scif_dev[spdev->dnode]; - - mutex_lock(&scif_info.conflock); - scif_info.total++; - scif_info.maxid = max_t(u32, spdev->dnode, scif_info.maxid); - mutex_unlock(&scif_info.conflock); - rcu_assign_pointer(scifdev->spdev, spdev); - - /* In the future SCIF kernel client devices will be added here */ - return 0; -} - -static void scif_peer_remove(struct scif_peer_dev *spdev) -{ - struct scif_dev *scifdev = &scif_dev[spdev->dnode]; - - /* In the future SCIF kernel client devices will be removed here */ - spdev = rcu_dereference(scifdev->spdev); - if (spdev) - RCU_INIT_POINTER(scifdev->spdev, NULL); - synchronize_rcu(); - - mutex_lock(&scif_info.conflock); - scif_info.total--; - mutex_unlock(&scif_info.conflock); -} - static void scif_qp_setup_handler(struct work_struct *work) { struct scif_dev *scifdev = container_of(work, struct scif_dev, @@ -139,20 +110,13 @@ static void scif_qp_setup_handler(struct work_struct *work) } } -static int scif_setup_scifdev(struct scif_hw_dev *sdev) +static int scif_setup_scifdev(void) { + /* We support a maximum of 129 SCIF nodes including the mgmt node */ +#define MAX_SCIF_NODES 129 int i; - u8 num_nodes; + u8 num_nodes = MAX_SCIF_NODES; - if (sdev->snode) { - struct mic_bootparam __iomem *bp = sdev->rdp; - - num_nodes = ioread8(&bp->tot_nodes); - } else { - struct mic_bootparam *bp = sdev->dp; - - num_nodes = bp->tot_nodes; - } scif_dev = kcalloc(num_nodes, sizeof(*scif_dev), GFP_KERNEL); if (!scif_dev) return -ENOMEM; @@ -163,7 +127,7 @@ static int scif_setup_scifdev(struct scif_hw_dev *sdev) scifdev->exit = OP_IDLE; init_waitqueue_head(&scifdev->disconn_wq); mutex_init(&scifdev->lock); - INIT_WORK(&scifdev->init_msg_work, scif_qp_response_ack); + INIT_WORK(&scifdev->peer_add_work, scif_add_peer_device); INIT_DELAYED_WORK(&scifdev->p2p_dwork, scif_poll_qp_state); INIT_DELAYED_WORK(&scifdev->qp_dwork, @@ -181,27 +145,21 @@ static void scif_destroy_scifdev(void) static int scif_probe(struct scif_hw_dev *sdev) { - struct scif_dev *scifdev; + struct scif_dev *scifdev = &scif_dev[sdev->dnode]; int rc; dev_set_drvdata(&sdev->dev, sdev); + scifdev->sdev = sdev; + if (1 == atomic_add_return(1, &g_loopb_cnt)) { - struct scif_dev *loopb_dev; + struct scif_dev *loopb_dev = &scif_dev[sdev->snode]; - rc = scif_setup_scifdev(sdev); - if (rc) - goto exit; - scifdev = &scif_dev[sdev->dnode]; - scifdev->sdev = sdev; - loopb_dev = &scif_dev[sdev->snode]; loopb_dev->sdev = sdev; rc = scif_setup_loopback_qp(loopb_dev); if (rc) - goto free_sdev; - } else { - scifdev = &scif_dev[sdev->dnode]; - scifdev->sdev = sdev; + goto exit; } + rc = scif_setup_intr_wq(scifdev); if (rc) goto destroy_loopb; @@ -237,8 +195,6 @@ destroy_intr: destroy_loopb: if (atomic_dec_and_test(&g_loopb_cnt)) scif_destroy_loopback_qp(&scif_dev[sdev->snode]); -free_sdev: - scif_destroy_scifdev(); exit: return rc; } @@ -290,13 +246,6 @@ static void scif_remove(struct scif_hw_dev *sdev) scifdev->sdev = NULL; } -static struct scif_peer_driver scif_peer_driver = { - .driver.name = KBUILD_MODNAME, - .driver.owner = THIS_MODULE, - .probe = scif_peer_probe, - .remove = scif_peer_remove, -}; - static struct scif_hw_dev_id id_table[] = { { MIC_SCIF_DEV, SCIF_DEV_ANY_ID }, { 0 }, @@ -312,6 +261,8 @@ static struct scif_driver scif_driver = { static int _scif_init(void) { + int rc; + spin_lock_init(&scif_info.eplock); spin_lock_init(&scif_info.nb_connect_lock); spin_lock_init(&scif_info.port_lock); @@ -326,10 +277,15 @@ static int _scif_init(void) init_waitqueue_head(&scif_info.exitwq); scif_info.en_msg_log = 0; scif_info.p2p_enable = 1; + rc = scif_setup_scifdev(); + if (rc) + goto error; INIT_WORK(&scif_info.misc_work, scif_misc_handler); INIT_WORK(&scif_info.conn_work, scif_conn_handler); idr_init(&scif_ports); return 0; +error: + return rc; } static void _scif_exit(void) @@ -347,12 +303,9 @@ static int __init scif_init(void) rc = scif_peer_bus_init(); if (rc) goto exit; - rc = scif_peer_register_driver(&scif_peer_driver); - if (rc) - goto peer_bus_exit; rc = scif_register_driver(&scif_driver); if (rc) - goto unreg_scif_peer; + goto peer_bus_exit; rc = misc_register(mdev); if (rc) goto unreg_scif; @@ -360,8 +313,6 @@ static int __init scif_init(void) return 0; unreg_scif: scif_unregister_driver(&scif_driver); -unreg_scif_peer: - scif_peer_unregister_driver(&scif_peer_driver); peer_bus_exit: scif_peer_bus_exit(); exit: @@ -374,7 +325,6 @@ static void __exit scif_exit(void) scif_exit_debugfs(); misc_deregister(&scif_info.mdev); scif_unregister_driver(&scif_driver); - scif_peer_unregister_driver(&scif_peer_driver); scif_peer_bus_exit(); _scif_exit(); } diff --git a/drivers/misc/mic/scif/scif_main.h b/drivers/misc/mic/scif/scif_main.h index 87c13279a8f0..b0795b059803 100644 --- a/drivers/misc/mic/scif/scif_main.h +++ b/drivers/misc/mic/scif/scif_main.h @@ -140,7 +140,7 @@ struct scif_p2p_info { * @db: doorbell the peer will trigger to generate an interrupt on self * @rdb: Doorbell to trigger on the peer to generate an interrupt on the peer * @cookie: Cookie received while registering the interrupt handler - * init_msg_work: work scheduled for SCIF_INIT message processing + * @peer_add_work: Work for handling device_add for peer devices * @p2p_dwork: Delayed work to enable polling for P2P state * @qp_dwork: Delayed work for enabling polling for remote QP information * @p2p_retry: Number of times to retry polling of P2P state @@ -166,7 +166,7 @@ struct scif_dev { int db; int rdb; struct mic_irq *cookie; - struct work_struct init_msg_work; + struct work_struct peer_add_work; struct delayed_work p2p_dwork; struct delayed_work qp_dwork; int p2p_retry; @@ -183,6 +183,7 @@ struct scif_dev { extern struct scif_info scif_info; extern struct idr scif_ports; +extern struct bus_type scif_peer_bus; extern struct scif_dev *scif_dev; extern const struct file_operations scif_fops; extern const struct file_operations scif_anon_fops; diff --git a/drivers/misc/mic/scif/scif_nm.c b/drivers/misc/mic/scif/scif_nm.c index 9b4c5382d6a7..f1b1b97b3636 100644 --- a/drivers/misc/mic/scif/scif_nm.c +++ b/drivers/misc/mic/scif/scif_nm.c @@ -147,14 +147,8 @@ void scif_cleanup_scifdev(struct scif_dev *dev) void scif_handle_remove_node(int node) { struct scif_dev *scifdev = &scif_dev[node]; - struct scif_peer_dev *spdev; - - rcu_read_lock(); - spdev = rcu_dereference(scifdev->spdev); - rcu_read_unlock(); - if (spdev) - scif_peer_unregister_device(spdev); - else + + if (scif_peer_unregister_device(scifdev)) scif_send_acks(scifdev); } diff --git a/drivers/misc/mic/scif/scif_nodeqp.c b/drivers/misc/mic/scif/scif_nodeqp.c index 6dfdae3452d6..5cdb9f088350 100644 --- a/drivers/misc/mic/scif/scif_nodeqp.c +++ b/drivers/misc/mic/scif/scif_nodeqp.c @@ -259,6 +259,11 @@ int scif_setup_qp_connect_response(struct scif_dev *scifdev, &qp->remote_qp->local_write, r_buf, get_count_order(remote_size)); + /* + * Because the node QP may already be processing an INIT message, set + * the read pointer so the cached read offset isn't lost + */ + qp->remote_qp->local_read = qp->inbound_q.current_read_offset; /* * resetup the inbound_q now that we know where the * inbound_read really is. @@ -529,27 +534,6 @@ static void scif_p2p_setup(void) } } -void scif_qp_response_ack(struct work_struct *work) -{ - struct scif_dev *scifdev = container_of(work, struct scif_dev, - init_msg_work); - struct scif_peer_dev *spdev; - - /* Drop the INIT message if it has already been received */ - if (_scifdev_alive(scifdev)) - return; - - spdev = scif_peer_register_device(scifdev); - if (IS_ERR(spdev)) - return; - - if (scif_is_mgmt_node()) { - mutex_lock(&scif_info.conflock); - scif_p2p_setup(); - mutex_unlock(&scif_info.conflock); - } -} - static char *message_types[] = {"BAD", "INIT", "EXIT", @@ -682,13 +666,14 @@ scif_init(struct scif_dev *scifdev, struct scifmsg *msg) * address to complete initializing the inbound_q. */ flush_delayed_work(&scifdev->qp_dwork); - /* - * Delegate the peer device registration to a workqueue, otherwise if - * SCIF client probe (called during peer device registration) calls - * scif_connect(..), it will block the message processing thread causing - * a deadlock. - */ - schedule_work(&scifdev->init_msg_work); + + scif_peer_register_device(scifdev); + + if (scif_is_mgmt_node()) { + mutex_lock(&scif_info.conflock); + scif_p2p_setup(); + mutex_unlock(&scif_info.conflock); + } } /** @@ -838,13 +823,13 @@ void scif_poll_qp_state(struct work_struct *work) msecs_to_jiffies(SCIF_NODE_QP_TIMEOUT)); return; } - scif_peer_register_device(peerdev); return; timeout: dev_err(&peerdev->sdev->dev, "%s %d remote node %d offline, state = 0x%x\n", __func__, __LINE__, peerdev->node, qp->qp_state); qp->remote_qp->qp_state = SCIF_QP_OFFLINE; + scif_peer_unregister_device(peerdev); scif_cleanup_scifdev(peerdev); } @@ -894,6 +879,9 @@ scif_node_add_ack(struct scif_dev *scifdev, struct scifmsg *msg) goto local_error; peerdev->rdb = msg->payload[2]; qp->remote_qp->qp_state = SCIF_QP_ONLINE; + + scif_peer_register_device(peerdev); + schedule_delayed_work(&peerdev->p2p_dwork, 0); return; local_error: @@ -1169,7 +1157,6 @@ int scif_setup_loopback_qp(struct scif_dev *scifdev) int err = 0; void *local_q; struct scif_qp *qp; - struct scif_peer_dev *spdev; err = scif_setup_intr_wq(scifdev); if (err) @@ -1216,15 +1203,11 @@ int scif_setup_loopback_qp(struct scif_dev *scifdev) &qp->local_write, local_q, get_count_order(SCIF_NODE_QP_SIZE)); scif_info.nodeid = scifdev->node; - spdev = scif_peer_register_device(scifdev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - goto free_local_q; - } + + scif_peer_register_device(scifdev); + scif_info.loopb_dev = scifdev; return err; -free_local_q: - kfree(local_q); free_qpairs: kfree(scifdev->qpairs); destroy_loopb_wq: @@ -1243,13 +1226,7 @@ exit: */ int scif_destroy_loopback_qp(struct scif_dev *scifdev) { - struct scif_peer_dev *spdev; - - rcu_read_lock(); - spdev = rcu_dereference(scifdev->spdev); - rcu_read_unlock(); - if (spdev) - scif_peer_unregister_device(spdev); + scif_peer_unregister_device(scifdev); destroy_workqueue(scif_info.loopb_wq); scif_destroy_intr_wq(scifdev); kfree(scifdev->qpairs->outbound_q.rb_base); diff --git a/drivers/misc/mic/scif/scif_peer_bus.c b/drivers/misc/mic/scif/scif_peer_bus.c index 589ae9ad2501..547bf7bdd5c2 100644 --- a/drivers/misc/mic/scif/scif_peer_bus.c +++ b/drivers/misc/mic/scif/scif_peer_bus.c @@ -24,93 +24,138 @@ dev_to_scif_peer(struct device *dev) return container_of(dev, struct scif_peer_dev, dev); } -static inline struct scif_peer_driver * -drv_to_scif_peer(struct device_driver *drv) -{ - return container_of(drv, struct scif_peer_driver, driver); -} +struct bus_type scif_peer_bus = { + .name = "scif_peer_bus", +}; -static int scif_peer_dev_match(struct device *dv, struct device_driver *dr) +static void scif_peer_release_dev(struct device *d) { - return !strncmp(dev_name(dv), dr->name, 4); + struct scif_peer_dev *sdev = dev_to_scif_peer(d); + struct scif_dev *scifdev = &scif_dev[sdev->dnode]; + + scif_cleanup_scifdev(scifdev); + kfree(sdev); } -static int scif_peer_dev_probe(struct device *d) +static int scif_peer_initialize_device(struct scif_dev *scifdev) { - struct scif_peer_dev *dev = dev_to_scif_peer(d); - struct scif_peer_driver *drv = drv_to_scif_peer(dev->dev.driver); + struct scif_peer_dev *spdev; + int ret; - return drv->probe(dev); -} + spdev = kzalloc(sizeof(*spdev), GFP_KERNEL); + if (!spdev) { + ret = -ENOMEM; + goto err; + } -static int scif_peer_dev_remove(struct device *d) -{ - struct scif_peer_dev *dev = dev_to_scif_peer(d); - struct scif_peer_driver *drv = drv_to_scif_peer(dev->dev.driver); + spdev->dev.parent = scifdev->sdev->dev.parent; + spdev->dev.release = scif_peer_release_dev; + spdev->dnode = scifdev->node; + spdev->dev.bus = &scif_peer_bus; + dev_set_name(&spdev->dev, "scif_peer-dev%u", spdev->dnode); + + device_initialize(&spdev->dev); + get_device(&spdev->dev); + rcu_assign_pointer(scifdev->spdev, spdev); - drv->remove(dev); + mutex_lock(&scif_info.conflock); + scif_info.total++; + scif_info.maxid = max_t(u32, spdev->dnode, scif_info.maxid); + mutex_unlock(&scif_info.conflock); return 0; +err: + dev_err(&scifdev->sdev->dev, + "dnode %d: initialize_device rc %d\n", scifdev->node, ret); + return ret; } -static struct bus_type scif_peer_bus = { - .name = "scif_peer_bus", - .match = scif_peer_dev_match, - .probe = scif_peer_dev_probe, - .remove = scif_peer_dev_remove, -}; - -int scif_peer_register_driver(struct scif_peer_driver *driver) +static int scif_peer_add_device(struct scif_dev *scifdev) { - driver->driver.bus = &scif_peer_bus; - return driver_register(&driver->driver); + struct scif_peer_dev *spdev = rcu_dereference(scifdev->spdev); + int ret; + + ret = device_add(&spdev->dev); + put_device(&spdev->dev); + if (ret) { + dev_err(&scifdev->sdev->dev, + "dnode %d: peer device_add failed\n", scifdev->node); + goto put_spdev; + } + dev_dbg(&spdev->dev, "Added peer dnode %d\n", spdev->dnode); + return 0; +put_spdev: + RCU_INIT_POINTER(scifdev->spdev, NULL); + synchronize_rcu(); + put_device(&spdev->dev); + + mutex_lock(&scif_info.conflock); + scif_info.total--; + mutex_unlock(&scif_info.conflock); + return ret; } -void scif_peer_unregister_driver(struct scif_peer_driver *driver) +void scif_add_peer_device(struct work_struct *work) { - driver_unregister(&driver->driver); + struct scif_dev *scifdev = container_of(work, struct scif_dev, + peer_add_work); + + scif_peer_add_device(scifdev); } -static void scif_peer_release_dev(struct device *d) +/* + * Peer device registration is split into a device_initialize and a device_add. + * The reason for doing this is as follows: First, peer device registration + * itself cannot be done in the message processing thread and must be delegated + * to another workqueue, otherwise if SCIF client probe, called during peer + * device registration, calls scif_connect(..), it will block the message + * processing thread causing a deadlock. Next, device_initialize is done in the + * "top-half" message processing thread and device_add in the "bottom-half" + * workqueue. If this is not done, SCIF_CNCT_REQ message processing executing + * concurrently with SCIF_INIT message processing is unable to get a reference + * on the peer device, thereby failing the connect request. + */ +void scif_peer_register_device(struct scif_dev *scifdev) { - struct scif_peer_dev *sdev = dev_to_scif_peer(d); - struct scif_dev *scifdev = &scif_dev[sdev->dnode]; + int ret; - scif_cleanup_scifdev(scifdev); - kfree(sdev); + mutex_lock(&scifdev->lock); + ret = scif_peer_initialize_device(scifdev); + if (ret) + goto exit; + schedule_work(&scifdev->peer_add_work); +exit: + mutex_unlock(&scifdev->lock); } -struct scif_peer_dev * -scif_peer_register_device(struct scif_dev *scifdev) +int scif_peer_unregister_device(struct scif_dev *scifdev) { - int ret; struct scif_peer_dev *spdev; - spdev = kzalloc(sizeof(*spdev), GFP_KERNEL); - if (!spdev) - return ERR_PTR(-ENOMEM); - - spdev->dev.parent = scifdev->sdev->dev.parent; - spdev->dev.release = scif_peer_release_dev; - spdev->dnode = scifdev->node; - spdev->dev.bus = &scif_peer_bus; + mutex_lock(&scifdev->lock); + /* Flush work to ensure device register is complete */ + flush_work(&scifdev->peer_add_work); - dev_set_name(&spdev->dev, "scif_peer-dev%u", spdev->dnode); /* - * device_register() causes the bus infrastructure to look for a - * matching driver. + * Continue holding scifdev->lock since theoretically unregister_device + * can be called simultaneously from multiple threads */ - ret = device_register(&spdev->dev); - if (ret) - goto free_spdev; - return spdev; -free_spdev: - kfree(spdev); - return ERR_PTR(ret); -} - -void scif_peer_unregister_device(struct scif_peer_dev *sdev) -{ - device_unregister(&sdev->dev); + spdev = rcu_dereference(scifdev->spdev); + if (!spdev) { + mutex_unlock(&scifdev->lock); + return -ENODEV; + } + + RCU_INIT_POINTER(scifdev->spdev, NULL); + synchronize_rcu(); + mutex_unlock(&scifdev->lock); + + dev_dbg(&spdev->dev, "Removing peer dnode %d\n", spdev->dnode); + device_unregister(&spdev->dev); + + mutex_lock(&scif_info.conflock); + scif_info.total--; + mutex_unlock(&scif_info.conflock); + return 0; } int scif_peer_bus_init(void) diff --git a/drivers/misc/mic/scif/scif_peer_bus.h b/drivers/misc/mic/scif/scif_peer_bus.h index 33f0dbb30152..a3b8dd2edaa5 100644 --- a/drivers/misc/mic/scif/scif_peer_bus.h +++ b/drivers/misc/mic/scif/scif_peer_bus.h @@ -19,47 +19,13 @@ #include #include - -/* - * Peer devices show up as PCIe devices for the mgmt node but not the cards. - * The mgmt node discovers all the cards on the PCIe bus and informs the other - * cards about their peers. Upon notification of a peer a node adds a peer - * device to the peer bus to maintain symmetry in the way devices are - * discovered across all nodes in the SCIF network. - */ -/** - * scif_peer_dev - representation of a peer SCIF device - * @dev: underlying device - * @dnode - The destination node which this device will communicate with. - */ -struct scif_peer_dev { - struct device dev; - u8 dnode; -}; - -/** - * scif_peer_driver - operations for a scif_peer I/O driver - * @driver: underlying device driver (populate name and owner). - * @id_table: the ids serviced by this driver. - * @probe: the function to call when a device is found. Returns 0 or -errno. - * @remove: the function to call when a device is removed. - */ -struct scif_peer_driver { - struct device_driver driver; - const struct scif_peer_dev_id *id_table; - - int (*probe)(struct scif_peer_dev *dev); - void (*remove)(struct scif_peer_dev *dev); -}; +#include struct scif_dev; -int scif_peer_register_driver(struct scif_peer_driver *driver); -void scif_peer_unregister_driver(struct scif_peer_driver *driver); - -struct scif_peer_dev *scif_peer_register_device(struct scif_dev *sdev); -void scif_peer_unregister_device(struct scif_peer_dev *sdev); - +void scif_add_peer_device(struct work_struct *work); +void scif_peer_register_device(struct scif_dev *sdev); +int scif_peer_unregister_device(struct scif_dev *scifdev); int scif_peer_bus_init(void); void scif_peer_bus_exit(void); #endif /* _SCIF_PEER_BUS_H */ diff --git a/include/linux/scif.h b/include/linux/scif.h index b1923ad2ddd3..fd62c051b166 100644 --- a/include/linux/scif.h +++ b/include/linux/scif.h @@ -55,6 +55,7 @@ #include #include +#include #include #define SCIF_ACCEPT_SYNC 1 @@ -105,6 +106,37 @@ struct scif_pollepd { short revents; }; +/** + * scif_peer_dev - representation of a peer SCIF device + * + * Peer devices show up as PCIe devices for the mgmt node but not the cards. + * The mgmt node discovers all the cards on the PCIe bus and informs the other + * cards about their peers. Upon notification of a peer a node adds a peer + * device to the peer bus to maintain symmetry in the way devices are + * discovered across all nodes in the SCIF network. + * + * @dev: underlying device + * @dnode - The destination node which this device will communicate with. + */ +struct scif_peer_dev { + struct device dev; + u8 dnode; +}; + +/** + * scif_client - representation of a SCIF client + * @name: client name + * @probe - client method called when a peer device is registered + * @remove - client method called when a peer device is unregistered + * @si - subsys_interface used internally for implementing SCIF clients + */ +struct scif_client { + const char *name; + void (*probe)(struct scif_peer_dev *spdev); + void (*remove)(struct scif_peer_dev *spdev); + struct subsys_interface si; +}; + #define SCIF_OPEN_FAILED ((scif_epd_t)-1) #define SCIF_REGISTER_FAILED ((off_t)-1) #define SCIF_MMAP_FAILED ((void *)-1) @@ -1064,4 +1096,30 @@ int scif_get_node_ids(u16 *nodes, int len, u16 *self); */ int scif_poll(struct scif_pollepd *epds, unsigned int nepds, long timeout); +/** + * scif_client_register() - Register a SCIF client + * @client: client to be registered + * + * scif_client_register() registers a SCIF client. The probe() method + * of the client is called when SCIF peer devices come online and the + * remove() method is called when the peer devices disappear. + * + * Return: + * Upon successful completion, scif_client_register() returns a non-negative + * value. Otherwise the return value is the same as subsys_interface_register() + * in the kernel. + */ +int scif_client_register(struct scif_client *client); + +/** + * scif_client_unregister() - Unregister a SCIF client + * @client: client to be unregistered + * + * scif_client_unregister() unregisters a SCIF client. + * + * Return: + * None + */ +void scif_client_unregister(struct scif_client *client); + #endif /* __SCIF_H__ */ -- cgit v1.2.3 From d411e79391092925457d89b77d7cd3038ba6d04b Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 29 Sep 2015 18:13:54 -0700 Subject: misc: mic: Remove COSM functionality from the MIC card driver Since card side COSM functionality, to trigger MIC device shutdowns and communicate shutdown status to the host, is now moved into a separate COSM client driver, this patch removes this functionality from the base MIC card driver. The mic_bus driver is also updated to use the device index provided by COSM rather than maintain its own device index. Reviewed-by: Nikhil Rao Reviewed-by: Sudeep Dutt Signed-off-by: Ashutosh Dixit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/bus/mic_bus.c | 22 ++-------- drivers/misc/mic/card/mic_device.c | 88 ++------------------------------------ drivers/misc/mic/card/mic_x100.c | 2 +- drivers/misc/mic/common/mic_dev.h | 13 ++++++ include/linux/mic_bus.h | 3 +- 5 files changed, 23 insertions(+), 105 deletions(-) (limited to 'include') diff --git a/drivers/misc/mic/bus/mic_bus.c b/drivers/misc/mic/bus/mic_bus.c index 961ae90aae47..c64955d8cbc1 100644 --- a/drivers/misc/mic/bus/mic_bus.c +++ b/drivers/misc/mic/bus/mic_bus.c @@ -25,9 +25,6 @@ #include #include -/* Unique numbering for mbus devices. */ -static DEFINE_IDA(mbus_index_ida); - static ssize_t device_show(struct device *d, struct device_attribute *attr, char *buf) { @@ -147,7 +144,8 @@ static void mbus_release_dev(struct device *d) struct mbus_device * mbus_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, - struct mbus_hw_ops *hw_ops, void __iomem *mmio_va) + struct mbus_hw_ops *hw_ops, int index, + void __iomem *mmio_va) { int ret; struct mbus_device *mbdev; @@ -166,13 +164,7 @@ mbus_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, mbdev->dev.release = mbus_release_dev; mbdev->hw_ops = hw_ops; mbdev->dev.bus = &mic_bus; - - /* Assign a unique device index and hence name. */ - ret = ida_simple_get(&mbus_index_ida, 0, 0, GFP_KERNEL); - if (ret < 0) - goto free_mbdev; - - mbdev->index = ret; + mbdev->index = index; dev_set_name(&mbdev->dev, "mbus-dev%u", mbdev->index); /* * device_register() causes the bus infrastructure to look for a @@ -180,10 +172,8 @@ mbus_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, */ ret = device_register(&mbdev->dev); if (ret) - goto ida_remove; + goto free_mbdev; return mbdev; -ida_remove: - ida_simple_remove(&mbus_index_ida, mbdev->index); free_mbdev: kfree(mbdev); return ERR_PTR(ret); @@ -192,10 +182,7 @@ EXPORT_SYMBOL_GPL(mbus_register_device); void mbus_unregister_device(struct mbus_device *mbdev) { - int index = mbdev->index; /* save for after device release */ - device_unregister(&mbdev->dev); - ida_simple_remove(&mbus_index_ida, index); } EXPORT_SYMBOL_GPL(mbus_unregister_device); @@ -207,7 +194,6 @@ static int __init mbus_init(void) static void __exit mbus_exit(void) { bus_unregister(&mic_bus); - ida_destroy(&mbus_index_ida); } core_initcall(mbus_init); diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c index 6338908b2252..d0edaf7e0cd5 100644 --- a/drivers/misc/mic/card/mic_device.c +++ b/drivers/misc/mic/card/mic_device.c @@ -37,71 +37,6 @@ #include "mic_virtio.h" static struct mic_driver *g_drv; -static struct mic_irq *shutdown_cookie; - -static void mic_notify_host(u8 state) -{ - struct mic_driver *mdrv = g_drv; - struct mic_bootparam __iomem *bootparam = mdrv->dp; - - iowrite8(state, &bootparam->shutdown_status); - dev_dbg(mdrv->dev, "%s %d system_state %d\n", - __func__, __LINE__, state); - mic_send_intr(&mdrv->mdev, ioread8(&bootparam->c2h_shutdown_db)); -} - -static int mic_panic_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - struct mic_driver *mdrv = g_drv; - struct mic_bootparam __iomem *bootparam = mdrv->dp; - - iowrite8(-1, &bootparam->h2c_config_db); - iowrite8(-1, &bootparam->h2c_shutdown_db); - mic_notify_host(MIC_CRASHED); - return NOTIFY_DONE; -} - -static struct notifier_block mic_panic = { - .notifier_call = mic_panic_event, -}; - -static irqreturn_t mic_shutdown_isr(int irq, void *data) -{ - struct mic_driver *mdrv = g_drv; - struct mic_bootparam __iomem *bootparam = mdrv->dp; - - mic_ack_interrupt(&g_drv->mdev); - if (ioread8(&bootparam->shutdown_card)) - orderly_poweroff(true); - return IRQ_HANDLED; -} - -static int mic_shutdown_init(void) -{ - int rc = 0; - struct mic_driver *mdrv = g_drv; - struct mic_bootparam __iomem *bootparam = mdrv->dp; - int shutdown_db; - - shutdown_db = mic_next_card_db(); - shutdown_cookie = mic_request_card_irq(mic_shutdown_isr, NULL, - "Shutdown", mdrv, shutdown_db); - if (IS_ERR(shutdown_cookie)) - rc = PTR_ERR(shutdown_cookie); - else - iowrite8(shutdown_db, &bootparam->h2c_shutdown_db); - return rc; -} - -static void mic_shutdown_uninit(void) -{ - struct mic_driver *mdrv = g_drv; - struct mic_bootparam __iomem *bootparam = mdrv->dp; - - iowrite8(-1, &bootparam->h2c_shutdown_db); - mic_free_card_irq(shutdown_cookie, mdrv); -} static int __init mic_dp_init(void) { @@ -359,11 +294,7 @@ int __init mic_driver_init(struct mic_driver *mdrv) u8 node_id; g_drv = mdrv; - /* - * Unloading the card module is not supported. The MIC card module - * handles fundamental operations like host/card initiated shutdowns - * and informing the host about card crashes and cannot be unloaded. - */ + /* Unloading the card module is not supported. */ if (!try_module_get(mdrv->dev->driver->owner)) { rc = -ENODEV; goto done; @@ -374,12 +305,9 @@ int __init mic_driver_init(struct mic_driver *mdrv) rc = mic_init_irq(); if (rc) goto dp_uninit; - rc = mic_shutdown_init(); - if (rc) - goto irq_uninit; if (!mic_request_dma_chans(mdrv)) { rc = -ENODEV; - goto shutdown_uninit; + goto irq_uninit; } rc = mic_devices_init(mdrv); if (rc) @@ -390,21 +318,18 @@ int __init mic_driver_init(struct mic_driver *mdrv) NULL, &scif_hw_ops, 0, node_id, &mdrv->mdev.mmio, NULL, NULL, mdrv->dp, mdrv->dma_ch, - mdrv->num_dma_ch); + mdrv->num_dma_ch, true); if (IS_ERR(mdrv->scdev)) { rc = PTR_ERR(mdrv->scdev); goto device_uninit; } mic_create_card_debug_dir(mdrv); - atomic_notifier_chain_register(&panic_notifier_list, &mic_panic); done: return rc; device_uninit: mic_devices_uninit(mdrv); dma_free: mic_free_dma_chans(mdrv); -shutdown_uninit: - mic_shutdown_uninit(); irq_uninit: mic_uninit_irq(); dp_uninit: @@ -425,13 +350,6 @@ void mic_driver_uninit(struct mic_driver *mdrv) scif_unregister_device(mdrv->scdev); mic_devices_uninit(mdrv); mic_free_dma_chans(mdrv); - /* - * Inform the host about the shutdown status i.e. poweroff/restart etc. - * The module cannot be unloaded so the only code path to call - * mic_devices_uninit(..) is the shutdown callback. - */ - mic_notify_host(system_state); - mic_shutdown_uninit(); mic_uninit_irq(); mic_dp_uninit(); module_put(mdrv->dev->driver->owner); diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c index 77fd41781c2e..b2958ce2368c 100644 --- a/drivers/misc/mic/card/mic_x100.c +++ b/drivers/misc/mic/card/mic_x100.c @@ -261,7 +261,7 @@ static int __init mic_probe(struct platform_device *pdev) mic_hw_intr_init(mdrv); platform_set_drvdata(pdev, mdrv); mdrv->dma_mbdev = mbus_register_device(mdrv->dev, MBUS_DEV_DMA_MIC, - NULL, &mbus_hw_ops, + NULL, &mbus_hw_ops, 0, mdrv->mdev.mmio.va); if (IS_ERR(mdrv->dma_mbdev)) { rc = PTR_ERR(mdrv->dma_mbdev); diff --git a/drivers/misc/mic/common/mic_dev.h b/drivers/misc/mic/common/mic_dev.h index 0b58c46045dc..50776772ebdf 100644 --- a/drivers/misc/mic/common/mic_dev.h +++ b/drivers/misc/mic/common/mic_dev.h @@ -21,6 +21,19 @@ #ifndef __MIC_DEV_H__ #define __MIC_DEV_H__ +/* The maximum number of MIC devices supported in a single host system. */ +#define MIC_MAX_NUM_DEVS 128 + +/** + * enum mic_hw_family - The hardware family to which a device belongs. + */ +enum mic_hw_family { + MIC_FAMILY_X100 = 0, + MIC_FAMILY_X200, + MIC_FAMILY_UNKNOWN, + MIC_FAMILY_LAST +}; + /** * struct mic_mw - MIC memory window * diff --git a/include/linux/mic_bus.h b/include/linux/mic_bus.h index d5b5f76d57ef..27d7c95fd0da 100644 --- a/include/linux/mic_bus.h +++ b/include/linux/mic_bus.h @@ -91,7 +91,8 @@ struct mbus_hw_ops { struct mbus_device * mbus_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, - struct mbus_hw_ops *hw_ops, void __iomem *mmio_va); + struct mbus_hw_ops *hw_ops, int index, + void __iomem *mmio_va); void mbus_unregister_device(struct mbus_device *mbdev); int mbus_register_driver(struct mbus_driver *drv); -- cgit v1.2.3 From f4a66c20448257fbb8932827e5f766b74d1acbf0 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 29 Sep 2015 18:14:30 -0700 Subject: misc: mic: Update MIC host daemon with COSM changes This patch updates the MIC host daemon to work with corresponding changes in COSM. Other MIC daemon fixes, cleanups and enhancements as are also rolled into this patch. Changes to MIC sysfs ABI which go into effect with this patch are also documented. Reviewed-by: Sudeep Dutt Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Ashutosh Dixit Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-mic.txt | 29 ++- Documentation/mic/mic_overview.txt | 31 ++- Documentation/mic/mpssd/mpss | 4 +- Documentation/mic/mpssd/mpssd.c | 362 ++++++++++++++++---------- Documentation/mic/mpssd/mpssd.h | 1 + drivers/misc/mic/Makefile | 4 + drivers/misc/mic/bus/scif_bus.c | 7 +- drivers/misc/mic/bus/scif_bus.h | 6 +- include/uapi/linux/mic_common.h | 16 +- 9 files changed, 285 insertions(+), 175 deletions(-) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-class-mic.txt b/Documentation/ABI/testing/sysfs-class-mic.txt index 13f48afc534f..d45eed2bf128 100644 --- a/Documentation/ABI/testing/sysfs-class-mic.txt +++ b/Documentation/ABI/testing/sysfs-class-mic.txt @@ -41,18 +41,15 @@ Description: When read, this entry provides the current state of an Intel MIC device in the context of the card OS. Possible values that will be read are: - "offline" - The MIC device is ready to boot the card OS. On + "ready" - The MIC device is ready to boot the card OS. On reading this entry after an OSPM resume, a "boot" has to be written to this entry if the card was previously shutdown during OSPM suspend. - "online" - The MIC device has initiated booting a card OS. + "booting" - The MIC device has initiated booting a card OS. + "online" - The MIC device has completed boot and is online "shutting_down" - The card OS is shutting down. + "resetting" - A reset has been initiated for the MIC device "reset_failed" - The MIC device has failed to reset. - "suspending" - The MIC device is currently being prepared for - suspend. On reading this entry, a "suspend" has to be written - to the state sysfs entry to ensure the card is shutdown during - OSPM suspend. - "suspended" - The MIC device has been suspended. When written, this sysfs entry triggers different state change operations depending upon the current state of the card OS. @@ -62,8 +59,6 @@ Description: sysfs entries. "reset" - Initiates device reset. "shutdown" - Initiates card OS shutdown. - "suspend" - Initiates card OS shutdown and also marks the card - as suspended. What: /sys/class/mic/mic(x)/shutdown_status Date: October 2013 @@ -126,7 +121,7 @@ Description: the card. This sysfs entry can be written with the following valid strings: a) linux - Boot a Linux image. - b) elf - Boot an elf image for flash updates. + b) flash - Boot an image for flash updates. What: /sys/class/mic/mic(x)/log_buf_addr Date: October 2013 @@ -155,3 +150,17 @@ Description: daemon to set the log buffer length address. The correct log buffer length address to be written can be found in the System.map file of the card OS. + +What: /sys/class/mic/mic(x)/heartbeat_enable +Date: March 2015 +KernelVersion: 3.20 +Contact: Ashutosh Dixit +Description: + The MIC drivers detect and inform user space about card crashes + via a heartbeat mechanism (see the description of + shutdown_status above). User space can turn off this + notification by setting heartbeat_enable to 0 and enable it by + setting this entry to 1. If this notification is disabled it is + the responsibility of user space to detect card crashes via + alternative means such as a network ping. This setting is + enabled by default. diff --git a/Documentation/mic/mic_overview.txt b/Documentation/mic/mic_overview.txt index 1a2f2c8ec59e..73f44fc3e715 100644 --- a/Documentation/mic/mic_overview.txt +++ b/Documentation/mic/mic_overview.txt @@ -28,6 +28,10 @@ The Symmetric Communication Interface (SCIF (pronounced as skiff)) is a low level communications API across PCIe currently implemented for MIC. More details are available at scif_overview.txt. +The Coprocessor State Management (COSM) driver on the host allows for +boot, shutdown and reset of Intel MIC devices. It communicates with a COSM +"client" driver on the MIC cards over SCIF to perform these functions. + Here is a block diagram of the various components described above. The virtio backends are situated on the host rather than the card given better single threaded performance for the host compared to MIC, the ability of @@ -51,19 +55,20 @@ the fact that the virtio block storage backend can only be on the host. | | | Virtio over PCIe IOCTLs | | | +--------------------------+ +-----------+ | | | +-----------+ -| MIC DMA | | +----------+ | +-----------+ | | MIC DMA | -| Driver | | | SCIF | | | SCIF | | | Driver | -+-----------+ | +----------+ | +-----------+ | +-----------+ - | | | | | | | -+---------------+ | +-----+-----+ | +-----+-----+ | +---------------+ -|MIC virtual Bus| | |SCIF HW Bus| | |SCIF HW BUS| | |MIC virtual Bus| -+---------------+ | +-----------+ | +-----+-----+ | +---------------+ - | | | | | | | - | +--------------+ | | | +---------------+ | - | |Intel MIC | | | | |Intel MIC | | - +---|Card Driver +----+ | | |Host Driver | | - +--------------+ | +----+---------------+-----+ - | | | +| MIC DMA | | +------+ | +------+ +------+ | | MIC DMA | +| Driver | | | SCIF | | | SCIF | | COSM | | | Driver | ++-----------+ | +------+ | +------+ +--+---+ | +-----------+ + | | | | | | | | ++---------------+ | +------+ | +--+---+ +--+---+ | +----------------+ +|MIC virtual Bus| | |SCIF | | |SCIF | | COSM | | |MIC virtual Bus | ++---------------+ | |HW Bus| | |HW Bus| | Bus | | +----------------+ + | | +------+ | +--+---+ +------+ | | + | | | | | | | | + | +-----------+---+ | | | +---------------+ | + | |Intel MIC | | | | |Intel MIC | | + +---|Card Driver | | | | |Host Driver | | + +------------+--------+ | +----+---------------+-----+ + | | | +-------------------------------------------------------------+ | | | PCIe Bus | diff --git a/Documentation/mic/mpssd/mpss b/Documentation/mic/mpssd/mpss index 582aad4811ae..09ea90931649 100755 --- a/Documentation/mic/mpssd/mpss +++ b/Documentation/mic/mpssd/mpss @@ -119,10 +119,10 @@ stop() # Wait for the cards to go offline for f in $sysfs/* do - while [ "`cat $f/state`" != "offline" ] + while [ "`cat $f/state`" != "ready" ] do sleep 1 - echo -e "Waiting for "`basename $f`" to go offline" + echo -e "Waiting for "`basename $f`" to become ready" done done diff --git a/Documentation/mic/mpssd/mpssd.c b/Documentation/mic/mpssd/mpssd.c index 3c5c379fc29d..aaeafa18d99b 100644 --- a/Documentation/mic/mpssd/mpssd.c +++ b/Documentation/mic/mpssd/mpssd.c @@ -43,7 +43,7 @@ #include #include -static void init_mic(struct mic_info *mic); +static void *init_mic(void *arg); static FILE *logfp; static struct mic_info mic_list; @@ -116,19 +116,18 @@ static struct { .num = htole16(MIC_VRING_ENTRIES), }, #if GSO_ENABLED - .host_features = htole32( + .host_features = htole32( 1 << VIRTIO_NET_F_CSUM | 1 << VIRTIO_NET_F_GSO | 1 << VIRTIO_NET_F_GUEST_TSO4 | 1 << VIRTIO_NET_F_GUEST_TSO6 | - 1 << VIRTIO_NET_F_GUEST_ECN | - 1 << VIRTIO_NET_F_GUEST_UFO), + 1 << VIRTIO_NET_F_GUEST_ECN), #else .host_features = 0, #endif }; -static const char *mic_config_dir = "/etc/sysconfig/mic"; +static const char *mic_config_dir = "/etc/mpss"; static const char *virtblk_backend = "VIRTBLK_BACKEND"; static struct { struct mic_device_desc dd; @@ -192,7 +191,7 @@ tap_configure(struct mic_info *mic, char *dev) return ret; } - snprintf(ipaddr, IFNAMSIZ, "172.31.%d.254/24", mic->id); + snprintf(ipaddr, IFNAMSIZ, "172.31.%d.254/24", mic->id + 1); pid = fork(); if (pid == 0) { @@ -255,8 +254,7 @@ static int tun_alloc(struct mic_info *mic, char *dev) return err; } #if GSO_ENABLED - offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | - TUN_F_TSO_ECN | TUN_F_UFO; + offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | TUN_F_TSO_ECN; err = ioctl(fd, TUNSETOFFLOAD, offload); if (err < 0) { @@ -332,7 +330,6 @@ static struct mic_device_desc *get_device_desc(struct mic_info *mic, int type) return d; } mpsslog("%s %s %d not found\n", mic->name, __func__, type); - assert(0); return NULL; } @@ -415,6 +412,13 @@ mic_virtio_copy(struct mic_info *mic, int fd, return ret; } +static inline unsigned _vring_size(unsigned int num, unsigned long align) +{ + return ((sizeof(struct vring_desc) * num + sizeof(__u16) * (3 + num) + + align - 1) & ~(align - 1)) + + sizeof(__u16) * 3 + sizeof(struct vring_used_elem) * num; +} + /* * This initialization routine requires at least one * vring i.e. vr0. vr1 is optional. @@ -426,8 +430,9 @@ init_vr(struct mic_info *mic, int fd, int type, int vr_size; char *va; - vr_size = PAGE_ALIGN(vring_size(MIC_VRING_ENTRIES, - MIC_VIRTIO_RING_ALIGN) + sizeof(struct _mic_vring_info)); + vr_size = PAGE_ALIGN(_vring_size(MIC_VRING_ENTRIES, + MIC_VIRTIO_RING_ALIGN) + + sizeof(struct _mic_vring_info)); va = mmap(NULL, MIC_DEVICE_PAGE_END + vr_size * num_vq, PROT_READ, MAP_SHARED, fd, 0); if (MAP_FAILED == va) { @@ -439,25 +444,25 @@ init_vr(struct mic_info *mic, int fd, int type, set_dp(mic, type, va); vr0->va = (struct mic_vring *)&va[MIC_DEVICE_PAGE_END]; vr0->info = vr0->va + - vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN); + _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN); vring_init(&vr0->vr, MIC_VRING_ENTRIES, vr0->va, MIC_VIRTIO_RING_ALIGN); mpsslog("%s %s vr0 %p vr0->info %p vr_size 0x%x vring 0x%x ", __func__, mic->name, vr0->va, vr0->info, vr_size, - vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN)); + _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN)); mpsslog("magic 0x%x expected 0x%x\n", le32toh(vr0->info->magic), MIC_MAGIC + type); assert(le32toh(vr0->info->magic) == MIC_MAGIC + type); if (vr1) { vr1->va = (struct mic_vring *) &va[MIC_DEVICE_PAGE_END + vr_size]; - vr1->info = vr1->va + vring_size(MIC_VRING_ENTRIES, + vr1->info = vr1->va + _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN); vring_init(&vr1->vr, MIC_VRING_ENTRIES, vr1->va, MIC_VIRTIO_RING_ALIGN); mpsslog("%s %s vr1 %p vr1->info %p vr_size 0x%x vring 0x%x ", __func__, mic->name, vr1->va, vr1->info, vr_size, - vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN)); + _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN)); mpsslog("magic 0x%x expected 0x%x\n", le32toh(vr1->info->magic), MIC_MAGIC + type + 1); assert(le32toh(vr1->info->magic) == MIC_MAGIC + type + 1); @@ -466,16 +471,21 @@ done: return va; } -static void +static int wait_for_card_driver(struct mic_info *mic, int fd, int type) { struct pollfd pollfd; int err; struct mic_device_desc *desc = get_device_desc(mic, type); + __u8 prev_status; + if (!desc) + return -ENODEV; + prev_status = desc->status; pollfd.fd = fd; mpsslog("%s %s Waiting .... desc-> type %d status 0x%x\n", mic->name, __func__, type, desc->status); + while (1) { pollfd.events = POLLIN; pollfd.revents = 0; @@ -487,8 +497,13 @@ wait_for_card_driver(struct mic_info *mic, int fd, int type) } if (pollfd.revents) { - mpsslog("%s %s Waiting... desc-> type %d status 0x%x\n", - mic->name, __func__, type, desc->status); + if (desc->status != prev_status) { + mpsslog("%s %s Waiting... desc-> type %d " + "status 0x%x\n", + mic->name, __func__, type, + desc->status); + prev_status = desc->status; + } if (desc->status & VIRTIO_CONFIG_S_DRIVER_OK) { mpsslog("%s %s poll.revents %d\n", mic->name, __func__, pollfd.revents); @@ -499,6 +514,7 @@ wait_for_card_driver(struct mic_info *mic, int fd, int type) } } } + return 0; } /* Spin till we have some descriptors */ @@ -575,9 +591,16 @@ virtio_net(void *arg) __func__, strerror(errno)); continue; } - if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) - wait_for_card_driver(mic, mic->mic_net.virtio_net_fd, - VIRTIO_ID_NET); + if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) { + err = wait_for_card_driver(mic, + mic->mic_net.virtio_net_fd, + VIRTIO_ID_NET); + if (err) { + mpsslog("%s %s %d Exiting...\n", + mic->name, __func__, __LINE__); + break; + } + } /* * Check if there is data to be read from TUN and write to * virtio net fd if there is. @@ -786,10 +809,16 @@ virtio_console(void *arg) strerror(errno)); continue; } - if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) - wait_for_card_driver(mic, - mic->mic_console.virtio_console_fd, - VIRTIO_ID_CONSOLE); + if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) { + err = wait_for_card_driver(mic, + mic->mic_console.virtio_console_fd, + VIRTIO_ID_CONSOLE); + if (err) { + mpsslog("%s %s %d Exiting...\n", + mic->name, __func__, __LINE__); + break; + } + } if (console_poll[MONITOR_FD].revents & POLLIN) { copy.iov = iov0; @@ -1048,8 +1077,9 @@ stop_virtblk(struct mic_info *mic) { int vr_size, ret; - vr_size = PAGE_ALIGN(vring_size(MIC_VRING_ENTRIES, - MIC_VIRTIO_RING_ALIGN) + sizeof(struct _mic_vring_info)); + vr_size = PAGE_ALIGN(_vring_size(MIC_VRING_ENTRIES, + MIC_VIRTIO_RING_ALIGN) + + sizeof(struct _mic_vring_info)); ret = munmap(mic->mic_virtblk.block_dp, MIC_DEVICE_PAGE_END + vr_size * virtblk_dev_page.dd.num_vq); if (ret < 0) @@ -1131,6 +1161,10 @@ write_status(int fd, __u8 *status) return ioctl(fd, MIC_VIRTIO_COPY_DESC, ©); } +#ifndef VIRTIO_BLK_T_GET_ID +#define VIRTIO_BLK_T_GET_ID 8 +#endif + static void * virtio_block(void *arg) { @@ -1297,12 +1331,7 @@ reset(struct mic_info *mic) mpsslog("%s: %s %d state %s\n", mic->name, __func__, __LINE__, state); - /* - * If the shutdown was initiated by OSPM, the state stays - * in "suspended" which is also a valid condition for reset. - */ - if ((!strcmp(state, "offline")) || - (!strcmp(state, "suspended"))) { + if (!strcmp(state, "ready")) { free(state); break; } @@ -1331,34 +1360,50 @@ get_mic_shutdown_status(struct mic_info *mic, char *shutdown_status) assert(0); }; -static int get_mic_state(struct mic_info *mic, char *state) +static int get_mic_state(struct mic_info *mic) { - if (!strcmp(state, "offline")) - return MIC_OFFLINE; - if (!strcmp(state, "online")) - return MIC_ONLINE; - if (!strcmp(state, "shutting_down")) - return MIC_SHUTTING_DOWN; - if (!strcmp(state, "reset_failed")) - return MIC_RESET_FAILED; - if (!strcmp(state, "suspending")) - return MIC_SUSPENDING; - if (!strcmp(state, "suspended")) - return MIC_SUSPENDED; - mpsslog("%s: BUG invalid state %s\n", mic->name, state); - /* Invalid state */ - assert(0); + char *state = NULL; + enum mic_states mic_state; + + while (!state) { + state = readsysfs(mic->name, "state"); + sleep(1); + } + mpsslog("%s: %s %d state %s\n", + mic->name, __func__, __LINE__, state); + + if (!strcmp(state, "ready")) { + mic_state = MIC_READY; + } else if (!strcmp(state, "booting")) { + mic_state = MIC_BOOTING; + } else if (!strcmp(state, "online")) { + mic_state = MIC_ONLINE; + } else if (!strcmp(state, "shutting_down")) { + mic_state = MIC_SHUTTING_DOWN; + } else if (!strcmp(state, "reset_failed")) { + mic_state = MIC_RESET_FAILED; + } else if (!strcmp(state, "resetting")) { + mic_state = MIC_RESETTING; + } else { + mpsslog("%s: BUG invalid state %s\n", mic->name, state); + assert(0); + } + + free(state); + return mic_state; }; static void mic_handle_shutdown(struct mic_info *mic) { #define SHUTDOWN_TIMEOUT 60 - int i = SHUTDOWN_TIMEOUT, ret, stat = 0; + int i = SHUTDOWN_TIMEOUT; char *shutdown_status; while (i) { shutdown_status = readsysfs(mic->name, "shutdown_status"); - if (!shutdown_status) + if (!shutdown_status) { + sleep(1); continue; + } mpsslog("%s: %s %d shutdown_status %s\n", mic->name, __func__, __LINE__, shutdown_status); switch (get_mic_shutdown_status(mic, shutdown_status)) { @@ -1377,94 +1422,110 @@ static void mic_handle_shutdown(struct mic_info *mic) i--; } reset: - ret = kill(mic->pid, SIGTERM); - mpsslog("%s: %s %d kill pid %d ret %d\n", - mic->name, __func__, __LINE__, - mic->pid, ret); - if (!ret) { - ret = waitpid(mic->pid, &stat, - WIFSIGNALED(stat)); - mpsslog("%s: %s %d waitpid ret %d pid %d\n", - mic->name, __func__, __LINE__, - ret, mic->pid); - } - if (ret == mic->pid) - reset(mic); + if (!i) + mpsslog("%s: %s %d timing out waiting for shutdown_status %s\n", + mic->name, __func__, __LINE__, shutdown_status); + reset(mic); } -static void * -mic_config(void *arg) +static int open_state_fd(struct mic_info *mic) { - struct mic_info *mic = (struct mic_info *)arg; - char *state = NULL; char pathname[PATH_MAX]; - int fd, ret; - struct pollfd ufds[1]; - char value[4096]; + int fd; snprintf(pathname, PATH_MAX - 1, "%s/%s/%s", MICSYSFSDIR, mic->name, "state"); fd = open(pathname, O_RDONLY); - if (fd < 0) { + if (fd < 0) mpsslog("%s: opening file %s failed %s\n", mic->name, pathname, strerror(errno)); - goto error; + return fd; +} + +static int block_till_state_change(int fd, struct mic_info *mic) +{ + struct pollfd ufds[1]; + char value[PAGE_SIZE]; + int ret; + + ufds[0].fd = fd; + ufds[0].events = POLLERR | POLLPRI; + ret = poll(ufds, 1, -1); + if (ret < 0) { + mpsslog("%s: %s %d poll failed %s\n", + mic->name, __func__, __LINE__, strerror(errno)); + return ret; + } + + ret = lseek(fd, 0, SEEK_SET); + if (ret < 0) { + mpsslog("%s: %s %d Failed to seek to 0: %s\n", + mic->name, __func__, __LINE__, strerror(errno)); + return ret; + } + + ret = read(fd, value, sizeof(value)); + if (ret < 0) { + mpsslog("%s: %s %d Failed to read sysfs entry: %s\n", + mic->name, __func__, __LINE__, strerror(errno)); + return ret; + } + + return 0; +} + +static void * +mic_config(void *arg) +{ + struct mic_info *mic = (struct mic_info *)arg; + int fd, ret, stat = 0; + + fd = open_state_fd(mic); + if (fd < 0) { + mpsslog("%s: %s %d open state fd failed %s\n", + mic->name, __func__, __LINE__, strerror(errno)); + goto exit; } do { - ret = lseek(fd, 0, SEEK_SET); + ret = block_till_state_change(fd, mic); if (ret < 0) { - mpsslog("%s: Failed to seek to file start '%s': %s\n", - mic->name, pathname, strerror(errno)); - goto close_error1; + mpsslog("%s: %s %d block_till_state_change error %s\n", + mic->name, __func__, __LINE__, strerror(errno)); + goto close_exit; } - ret = read(fd, value, sizeof(value)); - if (ret < 0) { - mpsslog("%s: Failed to read sysfs entry '%s': %s\n", - mic->name, pathname, strerror(errno)); - goto close_error1; - } -retry: - state = readsysfs(mic->name, "state"); - if (!state) - goto retry; - mpsslog("%s: %s %d state %s\n", - mic->name, __func__, __LINE__, state); - switch (get_mic_state(mic, state)) { + + switch (get_mic_state(mic)) { case MIC_SHUTTING_DOWN: mic_handle_shutdown(mic); - goto close_error; - case MIC_SUSPENDING: - mic->boot_on_resume = 1; - setsysfs(mic->name, "state", "suspend"); - mic_handle_shutdown(mic); - goto close_error; - case MIC_OFFLINE: + break; + case MIC_READY: + case MIC_RESET_FAILED: + ret = kill(mic->pid, SIGTERM); + mpsslog("%s: %s %d kill pid %d ret %d\n", + mic->name, __func__, __LINE__, + mic->pid, ret); + if (!ret) { + ret = waitpid(mic->pid, &stat, + WIFSIGNALED(stat)); + mpsslog("%s: %s %d waitpid ret %d pid %d\n", + mic->name, __func__, __LINE__, + ret, mic->pid); + } if (mic->boot_on_resume) { setsysfs(mic->name, "state", "boot"); mic->boot_on_resume = 0; } - break; + goto close_exit; default: break; } - free(state); - - ufds[0].fd = fd; - ufds[0].events = POLLERR | POLLPRI; - ret = poll(ufds, 1, -1); - if (ret < 0) { - mpsslog("%s: poll failed %s\n", - mic->name, strerror(errno)); - goto close_error1; - } } while (1); -close_error: - free(state); -close_error1: + +close_exit: close(fd); -error: +exit: init_mic(mic); pthread_exit(NULL); } @@ -1477,15 +1538,15 @@ set_cmdline(struct mic_info *mic) len = snprintf(buffer, PATH_MAX, "clocksource=tsc highres=off nohz=off "); - len += snprintf(buffer + len, PATH_MAX - len, + len += snprintf(buffer + len, PATH_MAX, "cpufreq_on;corec6_off;pc3_off;pc6_off "); - len += snprintf(buffer + len, PATH_MAX - len, + len += snprintf(buffer + len, PATH_MAX, "ifcfg=static;address,172.31.%d.1;netmask,255.255.255.0", - mic->id); + mic->id + 1); setsysfs(mic->name, "cmdline", buffer); mpsslog("%s: Command line: \"%s\"\n", mic->name, buffer); - snprintf(buffer, PATH_MAX, "172.31.%d.1", mic->id); + snprintf(buffer, PATH_MAX, "172.31.%d.1", mic->id + 1); mpsslog("%s: IPADDR: \"%s\"\n", mic->name, buffer); } @@ -1541,8 +1602,6 @@ set_log_buf_info(struct mic_info *mic) close(fd); } -static void init_mic(struct mic_info *mic); - static void change_virtblk_backend(int x, siginfo_t *siginfo, void *p) { @@ -1553,8 +1612,16 @@ change_virtblk_backend(int x, siginfo_t *siginfo, void *p) } static void -init_mic(struct mic_info *mic) +set_mic_boot_params(struct mic_info *mic) +{ + set_log_buf_info(mic); + set_cmdline(mic); +} + +static void * +init_mic(void *arg) { + struct mic_info *mic = (struct mic_info *)arg; struct sigaction ignore = { .sa_flags = 0, .sa_handler = SIG_IGN @@ -1564,7 +1631,7 @@ init_mic(struct mic_info *mic) .sa_sigaction = change_virtblk_backend, }; char buffer[PATH_MAX]; - int err; + int err, fd; /* * Currently, one virtio block device is supported for each MIC card @@ -1577,12 +1644,38 @@ init_mic(struct mic_info *mic) * the MIC daemon. */ sigaction(SIGUSR1, &ignore, NULL); +retry: + fd = open_state_fd(mic); + if (fd < 0) { + mpsslog("%s: %s %d open state fd failed %s\n", + mic->name, __func__, __LINE__, strerror(errno)); + sleep(2); + goto retry; + } + + if (mic->restart) { + snprintf(buffer, PATH_MAX, "boot"); + setsysfs(mic->name, "state", buffer); + mpsslog("%s restarting mic %d\n", + mic->name, mic->restart); + mic->restart = 0; + } + + while (1) { + while (block_till_state_change(fd, mic)) { + mpsslog("%s: %s %d block_till_state_change error %s\n", + mic->name, __func__, __LINE__, strerror(errno)); + sleep(2); + continue; + } + + if (get_mic_state(mic) == MIC_BOOTING) + break; + } mic->pid = fork(); switch (mic->pid) { case 0: - set_log_buf_info(mic); - set_cmdline(mic); add_virtio_device(mic, &virtcons_dev_page.dd); add_virtio_device(mic, &virtnet_dev_page.dd); err = pthread_create(&mic->mic_console.console_thread, NULL, @@ -1612,24 +1705,29 @@ init_mic(struct mic_info *mic) mic->name, mic->id, errno); break; default: - if (mic->restart) { - snprintf(buffer, PATH_MAX, "boot"); - setsysfs(mic->name, "state", buffer); - mpsslog("%s restarting mic %d\n", - mic->name, mic->restart); - mic->restart = 0; - } - pthread_create(&mic->config_thread, NULL, mic_config, mic); + err = pthread_create(&mic->config_thread, NULL, + mic_config, mic); + if (err) + mpsslog("%s mic_config pthread_create failed %s\n", + mic->name, strerror(err)); } + + return NULL; } static void start_daemon(void) { struct mic_info *mic; + int err; - for (mic = mic_list.next; mic != NULL; mic = mic->next) - init_mic(mic); + for (mic = mic_list.next; mic; mic = mic->next) { + set_mic_boot_params(mic); + err = pthread_create(&mic->init_thread, NULL, init_mic, mic); + if (err) + mpsslog("%s init_mic pthread_create failed %s\n", + mic->name, strerror(err)); + } while (1) sleep(60); diff --git a/Documentation/mic/mpssd/mpssd.h b/Documentation/mic/mpssd/mpssd.h index f5f18b15d9a0..8bd64944aacc 100644 --- a/Documentation/mic/mpssd/mpssd.h +++ b/Documentation/mic/mpssd/mpssd.h @@ -86,6 +86,7 @@ struct mic_info { int id; char *name; pthread_t config_thread; + pthread_t init_thread; pid_t pid; struct mic_console_info mic_console; struct mic_net_info mic_net; diff --git a/drivers/misc/mic/Makefile b/drivers/misc/mic/Makefile index a2e75f46fe4e..e288a1106738 100644 --- a/drivers/misc/mic/Makefile +++ b/drivers/misc/mic/Makefile @@ -2,5 +2,9 @@ # Makefile - Intel MIC Linux driver. # Copyright(c) 2013, Intel Corporation. # +obj-$(CONFIG_INTEL_MIC_HOST) += host/ +obj-$(CONFIG_INTEL_MIC_CARD) += card/ obj-y += bus/ obj-$(CONFIG_SCIF) += scif/ +obj-$(CONFIG_MIC_COSM) += cosm/ +obj-$(CONFIG_MIC_COSM) += cosm_client/ diff --git a/drivers/misc/mic/bus/scif_bus.c b/drivers/misc/mic/bus/scif_bus.c index 2da7ceed015d..fd2702143022 100644 --- a/drivers/misc/mic/bus/scif_bus.c +++ b/drivers/misc/mic/bus/scif_bus.c @@ -28,7 +28,6 @@ static ssize_t device_show(struct device *d, return sprintf(buf, "0x%04x\n", dev->id.device); } - static DEVICE_ATTR_RO(device); static ssize_t vendor_show(struct device *d, @@ -38,7 +37,6 @@ static ssize_t vendor_show(struct device *d, return sprintf(buf, "0x%04x\n", dev->id.vendor); } - static DEVICE_ATTR_RO(vendor); static ssize_t modalias_show(struct device *d, @@ -49,7 +47,6 @@ static ssize_t modalias_show(struct device *d, return sprintf(buf, "scif:d%08Xv%08X\n", dev->id.device, dev->id.vendor); } - static DEVICE_ATTR_RO(modalias); static struct attribute *scif_dev_attrs[] = { @@ -144,7 +141,8 @@ struct scif_hw_dev * scif_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, struct scif_hw_ops *hw_ops, u8 dnode, u8 snode, struct mic_mw *mmio, struct mic_mw *aper, void *dp, - void __iomem *rdp, struct dma_chan **chan, int num_chan) + void __iomem *rdp, struct dma_chan **chan, int num_chan, + bool card_rel_da) { int ret; struct scif_hw_dev *sdev; @@ -171,6 +169,7 @@ scif_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, dma_set_mask(&sdev->dev, DMA_BIT_MASK(64)); sdev->dma_ch = chan; sdev->num_dma_ch = num_chan; + sdev->card_rel_da = card_rel_da; dev_set_name(&sdev->dev, "scif-dev%u", sdev->dnode); /* * device_register() causes the bus infrastructure to look for a diff --git a/drivers/misc/mic/bus/scif_bus.h b/drivers/misc/mic/bus/scif_bus.h index 335a228a8236..94f29ac608b6 100644 --- a/drivers/misc/mic/bus/scif_bus.h +++ b/drivers/misc/mic/bus/scif_bus.h @@ -46,6 +46,8 @@ struct scif_hw_dev_id { * @rdp - Remote device page * @dma_ch - Array of DMA channels * @num_dma_ch - Number of DMA channels available + * @card_rel_da - Set to true if DMA addresses programmed in the DMA engine + * are relative to the card point of view */ struct scif_hw_dev { struct scif_hw_ops *hw_ops; @@ -59,6 +61,7 @@ struct scif_hw_dev { void __iomem *rdp; struct dma_chan **dma_ch; int num_dma_ch; + bool card_rel_da; }; /** @@ -114,7 +117,8 @@ scif_register_device(struct device *pdev, int id, struct scif_hw_ops *hw_ops, u8 dnode, u8 snode, struct mic_mw *mmio, struct mic_mw *aper, void *dp, void __iomem *rdp, - struct dma_chan **chan, int num_chan); + struct dma_chan **chan, int num_chan, + bool card_rel_da); void scif_unregister_device(struct scif_hw_dev *sdev); static inline struct scif_hw_dev *dev_to_scif(struct device *dev) diff --git a/include/uapi/linux/mic_common.h b/include/uapi/linux/mic_common.h index 302a2ced373c..e9686372029d 100644 --- a/include/uapi/linux/mic_common.h +++ b/include/uapi/linux/mic_common.h @@ -75,12 +75,7 @@ struct mic_device_ctrl { * struct mic_bootparam: Virtio device independent information in device page * * @magic: A magic value used by the card to ensure it can see the host - * @c2h_shutdown_db: Card to Host shutdown doorbell set by host - * @h2c_shutdown_db: Host to Card shutdown doorbell set by card * @h2c_config_db: Host to Card Virtio config doorbell set by card - * @shutdown_status: Card shutdown status set by card - * @shutdown_card: Set to 1 by the host when a card shutdown is initiated - * @tot_nodes: Total number of nodes in the SCIF network * @node_id: Unique id of the node * @h2c_scif_db - Host to card SCIF doorbell set by card * @c2h_scif_db - Card to host SCIF doorbell set by host @@ -89,12 +84,7 @@ struct mic_device_ctrl { */ struct mic_bootparam { __le32 magic; - __s8 c2h_shutdown_db; - __s8 h2c_shutdown_db; __s8 h2c_config_db; - __u8 shutdown_status; - __u8 shutdown_card; - __u8 tot_nodes; __u8 node_id; __u8 h2c_scif_db; __u8 c2h_scif_db; @@ -219,12 +209,12 @@ static inline unsigned mic_total_desc_size(struct mic_device_desc *desc) * enum mic_states - MIC states. */ enum mic_states { - MIC_OFFLINE = 0, + MIC_READY = 0, + MIC_BOOTING, MIC_ONLINE, MIC_SHUTTING_DOWN, + MIC_RESETTING, MIC_RESET_FAILED, - MIC_SUSPENDING, - MIC_SUSPENDED, MIC_LAST }; -- cgit v1.2.3 From a44f2630d78bfafc8eb5ab7d8b7b8cd4642ba749 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Tue, 29 Sep 2015 18:15:03 -0700 Subject: misc: mic: SCIF RMA header file and IOCTL changes This patch updates the SCIF header file and IOCTL interface with the changes required to support RMAs. APIs added include the ability to pin pages and register those pages with SCIF. SCIF kernel clients can also add references to remote registered pages and access them via the CPU. The user space IOCTL interface has been updated to enable SCIF registration, RDMA/CPU copies and fence APIs for RDMA synchronization. Reviewed-by: Nikhil Rao Reviewed-by: Ashutosh Dixit Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- include/linux/scif.h | 234 ++++++++++++++++++++++++++++++++++++++-- include/uapi/linux/scif_ioctl.h | 85 +++++++++++++++ 2 files changed, 309 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/scif.h b/include/linux/scif.h index fd62c051b166..49a35d6edc94 100644 --- a/include/linux/scif.h +++ b/include/linux/scif.h @@ -93,6 +93,27 @@ enum { #define SCIF_PORT_RSVD 1088 typedef struct scif_endpt *scif_epd_t; +typedef struct scif_pinned_pages *scif_pinned_pages_t; + +/** + * struct scif_range - SCIF registered range used in kernel mode + * @cookie: cookie used internally by SCIF + * @nr_pages: number of pages of PAGE_SIZE + * @prot_flags: R/W protection + * @phys_addr: Array of bus addresses + * @va: Array of kernel virtual addresses backed by the pages in the phys_addr + * array. The va is populated only when called on the host for a remote + * SCIF connection on MIC. This is required to support the use case of DMA + * between MIC and another device which is not a SCIF node e.g., an IB or + * ethernet NIC. + */ +struct scif_range { + void *cookie; + int nr_pages; + int prot_flags; + dma_addr_t *phys_addr; + void __iomem **va; +}; /** * struct scif_pollepd - SCIF endpoint to be monitored via scif_poll @@ -389,7 +410,6 @@ int scif_close(scif_epd_t epd); * Errors: * EBADF, ENOTTY - epd is not a valid endpoint descriptor * ECONNRESET - Connection reset by peer - * EFAULT - An invalid address was specified for a parameter * EINVAL - flags is invalid, or len is negative * ENODEV - The remote node is lost or existed, but is not currently in the * network since it may have crashed @@ -442,7 +462,6 @@ int scif_send(scif_epd_t epd, void *msg, int len, int flags); * EAGAIN - The destination node is returning from a low power state * EBADF, ENOTTY - epd is not a valid endpoint descriptor * ECONNRESET - Connection reset by peer - * EFAULT - An invalid address was specified for a parameter * EINVAL - flags is invalid, or len is negative * ENODEV - The remote node is lost or existed, but is not currently in the * network since it may have crashed @@ -505,9 +524,6 @@ int scif_recv(scif_epd_t epd, void *msg, int len, int flags); * SCIF_PROT_READ - allow read operations from the window * SCIF_PROT_WRITE - allow write operations to the window * - * The map_flags argument can be set to SCIF_MAP_FIXED which interprets a - * fixed offset. - * * Return: * Upon successful completion, scif_register() returns the offset at which the * mapping was placed (po); otherwise in user mode SCIF_REGISTER_FAILED (that @@ -520,7 +536,6 @@ int scif_recv(scif_epd_t epd, void *msg, int len, int flags); * EAGAIN - The mapping could not be performed due to lack of resources * EBADF, ENOTTY - epd is not a valid endpoint descriptor * ECONNRESET - Connection reset by peer - * EFAULT - Addresses in the range [addr, addr + len - 1] are invalid * EINVAL - map_flags is invalid, or prot_flags is invalid, or SCIF_MAP_FIXED is * set in flags, and offset is not a multiple of the page size, or addr is not a * multiple of the page size, or len is not a multiple of the page size, or is @@ -803,7 +818,6 @@ int scif_writeto(scif_epd_t epd, off_t loffset, size_t len, off_t * EACCESS - Attempt to write to a read-only range * EBADF, ENOTTY - epd is not a valid endpoint descriptor * ECONNRESET - Connection reset by peer - * EFAULT - Addresses in the range [addr, addr + len - 1] are invalid * EINVAL - rma_flags is invalid * ENODEV - The remote node is lost or existed, but is not currently in the * network since it may have crashed @@ -884,7 +898,6 @@ int scif_vreadfrom(scif_epd_t epd, void *addr, size_t len, off_t roffset, * EACCESS - Attempt to write to a read-only range * EBADF, ENOTTY - epd is not a valid endpoint descriptor * ECONNRESET - Connection reset by peer - * EFAULT - Addresses in the range [addr, addr + len - 1] are invalid * EINVAL - rma_flags is invalid * ENODEV - The remote node is lost or existed, but is not currently in the * network since it may have crashed @@ -1028,11 +1041,212 @@ int scif_fence_signal(scif_epd_t epd, off_t loff, u64 lval, off_t roff, * online nodes in the SCIF network including 'self'; otherwise in user mode * -1 is returned and errno is set to indicate the error; in kernel mode no * errors are returned. + */ +int scif_get_node_ids(u16 *nodes, int len, u16 *self); + +/** + * scif_pin_pages() - Pin a set of pages + * @addr: Virtual address of range to pin + * @len: Length of range to pin + * @prot_flags: Page protection flags + * @map_flags: Page classification flags + * @pinned_pages: Handle to pinned pages + * + * scif_pin_pages() pins (locks in physical memory) the physical pages which + * back the range of virtual address pages starting at addr and continuing for + * len bytes. addr and len are constrained to be multiples of the page size. A + * successful scif_pin_pages() call returns a handle to pinned_pages which may + * be used in subsequent calls to scif_register_pinned_pages(). + * + * The pages will remain pinned as long as there is a reference against the + * scif_pinned_pages_t value returned by scif_pin_pages() and until + * scif_unpin_pages() is called, passing the scif_pinned_pages_t value. A + * reference is added to a scif_pinned_pages_t value each time a window is + * created by calling scif_register_pinned_pages() and passing the + * scif_pinned_pages_t value. A reference is removed from a + * scif_pinned_pages_t value each time such a window is deleted. + * + * Subsequent operations which change the memory pages to which virtual + * addresses are mapped (such as mmap(), munmap()) have no effect on the + * scif_pinned_pages_t value or windows created against it. + * + * If the process will fork(), it is recommended that the registered + * virtual address range be marked with MADV_DONTFORK. Doing so will prevent + * problems due to copy-on-write semantics. + * + * The prot_flags argument is formed by OR'ing together one or more of the + * following values. + * SCIF_PROT_READ - allow read operations against the pages + * SCIF_PROT_WRITE - allow write operations against the pages + * The map_flags argument can be set as SCIF_MAP_KERNEL to interpret addr as a + * kernel space address. By default, addr is interpreted as a user space + * address. + * + * Return: + * Upon successful completion, scif_pin_pages() returns 0; otherwise the + * negative of one of the following errors is returned. * * Errors: - * EFAULT - Bad address + * EINVAL - prot_flags is invalid, map_flags is invalid, or offset is negative + * ENOMEM - Not enough space */ -int scif_get_node_ids(u16 *nodes, int len, u16 *self); +int scif_pin_pages(void *addr, size_t len, int prot_flags, int map_flags, + scif_pinned_pages_t *pinned_pages); + +/** + * scif_unpin_pages() - Unpin a set of pages + * @pinned_pages: Handle to pinned pages to be unpinned + * + * scif_unpin_pages() prevents scif_register_pinned_pages() from registering new + * windows against pinned_pages. The physical pages represented by pinned_pages + * will remain pinned until all windows previously registered against + * pinned_pages are deleted (the window is scif_unregister()'d and all + * references to the window are removed (see scif_unregister()). + * + * pinned_pages must have been obtain from a previous call to scif_pin_pages(). + * After calling scif_unpin_pages(), it is an error to pass pinned_pages to + * scif_register_pinned_pages(). + * + * Return: + * Upon successful completion, scif_unpin_pages() returns 0; otherwise the + * negative of one of the following errors is returned. + * + * Errors: + * EINVAL - pinned_pages is not valid + */ +int scif_unpin_pages(scif_pinned_pages_t pinned_pages); + +/** + * scif_register_pinned_pages() - Mark a memory region for remote access. + * @epd: endpoint descriptor + * @pinned_pages: Handle to pinned pages + * @offset: Registered address space offset + * @map_flags: Flags which control where pages are mapped + * + * The scif_register_pinned_pages() function opens a window, a range of whole + * pages of the registered address space of the endpoint epd, starting at + * offset po. The value of po, further described below, is a function of the + * parameters offset and pinned_pages, and the value of map_flags. Each page of + * the window represents a corresponding physical memory page of the range + * represented by pinned_pages; the length of the window is the same as the + * length of range represented by pinned_pages. A successful + * scif_register_pinned_pages() call returns po as the return value. + * + * When SCIF_MAP_FIXED is set in the map_flags argument, po will be offset + * exactly, and offset is constrained to be a multiple of the page size. The + * mapping established by scif_register_pinned_pages() will not replace any + * existing registration; an error is returned if any page of the new window + * would intersect an existing window. + * + * When SCIF_MAP_FIXED is not set, the implementation uses offset in an + * implementation-defined manner to arrive at po. The po so chosen will be an + * area of the registered address space that the implementation deems suitable + * for a mapping of the required size. An offset value of 0 is interpreted as + * granting the implementation complete freedom in selecting po, subject to + * constraints described below. A non-zero value of offset is taken to be a + * suggestion of an offset near which the mapping should be placed. When the + * implementation selects a value for po, it does not replace any extant + * window. In all cases, po will be a multiple of the page size. + * + * The physical pages which are so represented by a window are available for + * access in calls to scif_get_pages(), scif_readfrom(), scif_writeto(), + * scif_vreadfrom(), and scif_vwriteto(). While a window is registered, the + * physical pages represented by the window will not be reused by the memory + * subsystem for any other purpose. Note that the same physical page may be + * represented by multiple windows. + * + * Windows created by scif_register_pinned_pages() are unregistered by + * scif_unregister(). + * + * The map_flags argument can be set to SCIF_MAP_FIXED which interprets a + * fixed offset. + * + * Return: + * Upon successful completion, scif_register_pinned_pages() returns the offset + * at which the mapping was placed (po); otherwise the negative of one of the + * following errors is returned. + * + * Errors: + * EADDRINUSE - SCIF_MAP_FIXED is set in map_flags and pages in the new window + * would intersect an existing window + * EAGAIN - The mapping could not be performed due to lack of resources + * ECONNRESET - Connection reset by peer + * EINVAL - map_flags is invalid, or SCIF_MAP_FIXED is set in map_flags, and + * offset is not a multiple of the page size, or offset is negative + * ENODEV - The remote node is lost or existed, but is not currently in the + * network since it may have crashed + * ENOMEM - Not enough space + * ENOTCONN - The endpoint is not connected + */ +off_t scif_register_pinned_pages(scif_epd_t epd, + scif_pinned_pages_t pinned_pages, + off_t offset, int map_flags); + +/** + * scif_get_pages() - Add references to remote registered pages + * @epd: endpoint descriptor + * @offset: remote registered offset + * @len: length of range of pages + * @pages: returned scif_range structure + * + * scif_get_pages() returns the addresses of the physical pages represented by + * those pages of the registered address space of the peer of epd, starting at + * offset and continuing for len bytes. offset and len are constrained to be + * multiples of the page size. + * + * All of the pages in the specified range [offset, offset + len - 1] must be + * within a single window of the registered address space of the peer of epd. + * + * The addresses are returned as a virtually contiguous array pointed to by the + * phys_addr component of the scif_range structure whose address is returned in + * pages. The nr_pages component of scif_range is the length of the array. The + * prot_flags component of scif_range holds the protection flag value passed + * when the pages were registered. + * + * Each physical page whose address is returned by scif_get_pages() remains + * available and will not be released for reuse until the scif_range structure + * is returned in a call to scif_put_pages(). The scif_range structure returned + * by scif_get_pages() must be unmodified. + * + * It is an error to call scif_close() on an endpoint on which a scif_range + * structure of that endpoint has not been returned to scif_put_pages(). + * + * Return: + * Upon successful completion, scif_get_pages() returns 0; otherwise the + * negative of one of the following errors is returned. + * Errors: + * ECONNRESET - Connection reset by peer. + * EINVAL - offset is not a multiple of the page size, or offset is negative, or + * len is not a multiple of the page size + * ENODEV - The remote node is lost or existed, but is not currently in the + * network since it may have crashed + * ENOTCONN - The endpoint is not connected + * ENXIO - Offsets in the range [offset, offset + len - 1] are invalid + * for the registered address space of the peer epd + */ +int scif_get_pages(scif_epd_t epd, off_t offset, size_t len, + struct scif_range **pages); + +/** + * scif_put_pages() - Remove references from remote registered pages + * @pages: pages to be returned + * + * scif_put_pages() releases a scif_range structure previously obtained by + * calling scif_get_pages(). The physical pages represented by pages may + * be reused when the window which represented those pages is unregistered. + * Therefore, those pages must not be accessed after calling scif_put_pages(). + * + * Return: + * Upon successful completion, scif_put_pages() returns 0; otherwise the + * negative of one of the following errors is returned. + * Errors: + * EINVAL - pages does not point to a valid scif_range structure, or + * the scif_range structure pointed to by pages was already returned + * ENODEV - The remote node is lost or existed, but is not currently in the + * network since it may have crashed + * ENOTCONN - The endpoint is not connected + */ +int scif_put_pages(struct scif_range *pages); /** * scif_poll() - Wait for some event on an endpoint diff --git a/include/uapi/linux/scif_ioctl.h b/include/uapi/linux/scif_ioctl.h index 4a94d917cf99..d9048918be52 100644 --- a/include/uapi/linux/scif_ioctl.h +++ b/include/uapi/linux/scif_ioctl.h @@ -106,6 +106,82 @@ struct scifioctl_msg { __s32 out_len; }; +/** + * struct scifioctl_reg - used for SCIF_REG IOCTL + * @addr: starting virtual address + * @len: length of range + * @offset: offset of window + * @prot: read/write protection + * @flags: flags + * @out_offset: offset returned + */ +struct scifioctl_reg { + __u64 addr; + __u64 len; + __s64 offset; + __s32 prot; + __s32 flags; + __s64 out_offset; +}; + +/** + * struct scifioctl_unreg - used for SCIF_UNREG IOCTL + * @offset: start of range to unregister + * @len: length of range to unregister + */ +struct scifioctl_unreg { + __s64 offset; + __u64 len; +}; + +/** + * struct scifioctl_copy - used for SCIF DMA copy IOCTLs + * + * @loffset: offset in local registered address space to/from + * which to copy + * @len: length of range to copy + * @roffset: offset in remote registered address space to/from + * which to copy + * @addr: user virtual address to/from which to copy + * @flags: flags + * + * This structure is used for SCIF_READFROM, SCIF_WRITETO, SCIF_VREADFROM + * and SCIF_VREADFROM IOCTL's. + */ +struct scifioctl_copy { + __s64 loffset; + __u64 len; + __s64 roffset; + __u64 addr; + __s32 flags; +}; + +/** + * struct scifioctl_fence_mark - used for SCIF_FENCE_MARK IOCTL + * @flags: flags + * @mark: fence handle which is a pointer to a __s32 + */ +struct scifioctl_fence_mark { + __s32 flags; + __u64 mark; +}; + +/** + * struct scifioctl_fence_signal - used for SCIF_FENCE_SIGNAL IOCTL + * @loff: local offset + * @lval: value to write to loffset + * @roff: remote offset + * @rval: value to write to roffset + * @flags: flags + */ +struct scifioctl_fence_signal { + __s64 loff; + __u64 lval; + __s64 roff; + __u64 rval; + __s32 flags; +}; + /** * struct scifioctl_node_ids - used for SCIF_GET_NODEIDS IOCTL * @nodes: pointer to an array of node_ids @@ -125,6 +201,15 @@ struct scifioctl_node_ids { #define SCIF_ACCEPTREG _IOWR('s', 5, __u64) #define SCIF_SEND _IOWR('s', 6, struct scifioctl_msg) #define SCIF_RECV _IOWR('s', 7, struct scifioctl_msg) +#define SCIF_REG _IOWR('s', 8, struct scifioctl_reg) +#define SCIF_UNREG _IOWR('s', 9, struct scifioctl_unreg) +#define SCIF_READFROM _IOWR('s', 10, struct scifioctl_copy) +#define SCIF_WRITETO _IOWR('s', 11, struct scifioctl_copy) +#define SCIF_VREADFROM _IOWR('s', 12, struct scifioctl_copy) +#define SCIF_VWRITETO _IOWR('s', 13, struct scifioctl_copy) #define SCIF_GET_NODEIDS _IOWR('s', 14, struct scifioctl_node_ids) +#define SCIF_FENCE_MARK _IOWR('s', 15, struct scifioctl_fence_mark) +#define SCIF_FENCE_WAIT _IOWR('s', 16, __s32) +#define SCIF_FENCE_SIGNAL _IOWR('s', 17, struct scifioctl_fence_signal) #endif /* SCIF_IOCTL_H */ -- cgit v1.2.3 From 7bd1d4093c2fa37d1ecab05da3c9d48ea2af2264 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 22 Sep 2015 15:47:10 +0300 Subject: stm class: Introduce an abstraction for System Trace Module devices A System Trace Module (STM) is a device exporting data in System Trace Protocol (STP) format as defined by MIPI STP standards. Examples of such devices are Intel(R) Trace Hub and Coresight STM. This abstraction provides a unified interface for software trace sources to send their data over an STM device to a debug host. In order to do that, such a trace source needs to be assigned a pair of master/channel identifiers that all the data from this source will be tagged with. The STP decoder on the debug host side will use these master/channel tags to distinguish different trace streams from one another inside one STP stream. This abstraction provides a configfs-based policy management mechanism for dynamic allocation of these master/channel pairs based on trace source-supplied string identifier. It has the flexibility of being defined at runtime and at the same time (provided that the policy definition is aligned with the decoding end) consistency. For userspace trace sources, this abstraction provides write()-based and mmap()-based (if the underlying stm device allows this) output mechanism. For kernel-side trace sources, we provide "stm_source" device class that can be connected to an stm device at run time. Cc: linux-api@vger.kernel.org Reviewed-by: Mathieu Poirier Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/configfs-stp-policy | 48 + Documentation/ABI/testing/sysfs-class-stm | 14 + Documentation/ABI/testing/sysfs-class-stm_source | 11 + Documentation/ioctl/ioctl-number.txt | 3 + Documentation/trace/stm.txt | 80 ++ drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/hwtracing/stm/Kconfig | 8 + drivers/hwtracing/stm/Makefile | 3 + drivers/hwtracing/stm/core.c | 1029 ++++++++++++++++++++++ drivers/hwtracing/stm/policy.c | 529 +++++++++++ drivers/hwtracing/stm/stm.h | 87 ++ include/linux/stm.h | 126 +++ include/uapi/linux/stm.h | 50 ++ 14 files changed, 1991 insertions(+) create mode 100644 Documentation/ABI/testing/configfs-stp-policy create mode 100644 Documentation/ABI/testing/sysfs-class-stm create mode 100644 Documentation/ABI/testing/sysfs-class-stm_source create mode 100644 Documentation/trace/stm.txt create mode 100644 drivers/hwtracing/stm/Kconfig create mode 100644 drivers/hwtracing/stm/Makefile create mode 100644 drivers/hwtracing/stm/core.c create mode 100644 drivers/hwtracing/stm/policy.c create mode 100644 drivers/hwtracing/stm/stm.h create mode 100644 include/linux/stm.h create mode 100644 include/uapi/linux/stm.h (limited to 'include') diff --git a/Documentation/ABI/testing/configfs-stp-policy b/Documentation/ABI/testing/configfs-stp-policy new file mode 100644 index 000000000000..421ce6825c66 --- /dev/null +++ b/Documentation/ABI/testing/configfs-stp-policy @@ -0,0 +1,48 @@ +What: /config/stp-policy +Date: June 2015 +KernelVersion: 4.3 +Description: + This group contains policies mandating Master/Channel allocation + for software sources wishing to send trace data over an STM + device. + +What: /config/stp-policy/. +Date: June 2015 +KernelVersion: 4.3 +Description: + This group is the root of a policy; its name is a concatenation + of an stm device name to which this policy applies and an + arbitrary string. If part doesn't match an existing + stm device, mkdir will fail with ENODEV; if that device already + has a policy assigned to it, mkdir will fail with EBUSY. + +What: /config/stp-policy/./device +Date: June 2015 +KernelVersion: 4.3 +Description: + STM device to which this policy applies, read only. Same as the + component of its parent directory. + +What: /config/stp-policy/./ +Date: June 2015 +KernelVersion: 4.3 +Description: + Policy node is a string identifier that software clients will + use to request a master/channel to be allocated and assigned to + them. + +What: /config/stp-policy/.//masters +Date: June 2015 +KernelVersion: 4.3 +Description: + Range of masters from which to allocate for users of this node. + Write two numbers: the first master and the last master number. + +What: /config/stp-policy/.//channels +Date: June 2015 +KernelVersion: 4.3 +Description: + Range of channels from which to allocate for users of this node. + Write two numbers: the first channel and the last channel + number. + diff --git a/Documentation/ABI/testing/sysfs-class-stm b/Documentation/ABI/testing/sysfs-class-stm new file mode 100644 index 000000000000..c9aa4f3fc9a7 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-stm @@ -0,0 +1,14 @@ +What: /sys/class/stm//masters +Date: June 2015 +KernelVersion: 4.3 +Contact: Alexander Shishkin +Description: + Shows first and last available to software master numbers on + this STM device. + +What: /sys/class/stm//channels +Date: June 2015 +KernelVersion: 4.3 +Contact: Alexander Shishkin +Description: + Shows the number of channels per master on this STM device. diff --git a/Documentation/ABI/testing/sysfs-class-stm_source b/Documentation/ABI/testing/sysfs-class-stm_source new file mode 100644 index 000000000000..57b8dd39bbf7 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-stm_source @@ -0,0 +1,11 @@ +What: /sys/class/stm_source//stm_source_link +Date: June 2015 +KernelVersion: 4.3 +Contact: Alexander Shishkin +Description: + stm_source device linkage to stm device, where its tracing data + is directed. Reads return an existing connection or "" if + this stm_source is not connected to any stm device yet. + Write an existing (registered) stm device's name here to + connect that device. If a device is already connected to this + stm_source, it will first be disconnected. diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index df1b25eb8382..3785b7e131f8 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -81,6 +81,9 @@ Code Seq#(hex) Include File Comments 0x22 all scsi/sg.h '#' 00-3F IEEE 1394 Subsystem Block for the entire subsystem '$' 00-0F linux/perf_counter.h, linux/perf_event.h +'%' 00-0F include/uapi/linux/stm.h + System Trace Module subsystem + '&' 00-07 drivers/firewire/nosy-user.h '1' 00-1F PPS kit from Ulrich Windl diff --git a/Documentation/trace/stm.txt b/Documentation/trace/stm.txt new file mode 100644 index 000000000000..ea035f9dbfd7 --- /dev/null +++ b/Documentation/trace/stm.txt @@ -0,0 +1,80 @@ +System Trace Module +=================== + +System Trace Module (STM) is a device described in MIPI STP specs as +STP trace stream generator. STP (System Trace Protocol) is a trace +protocol multiplexing data from multiple trace sources, each one of +which is assigned a unique pair of master and channel. While some of +these masters and channels are statically allocated to certain +hardware trace sources, others are available to software. Software +trace sources are usually free to pick for themselves any +master/channel combination from this pool. + +On the receiving end of this STP stream (the decoder side), trace +sources can only be identified by master/channel combination, so in +order for the decoder to be able to make sense of the trace that +involves multiple trace sources, it needs to be able to map those +master/channel pairs to the trace sources that it understands. + +For instance, it is helpful to know that syslog messages come on +master 7 channel 15, while arbitrary user applications can use masters +48 to 63 and channels 0 to 127. + +To solve this mapping problem, stm class provides a policy management +mechanism via configfs, that allows defining rules that map string +identifiers to ranges of masters and channels. If these rules (policy) +are consistent with what decoder expects, it will be able to properly +process the trace data. + +This policy is a tree structure containing rules (policy_node) that +have a name (string identifier) and a range of masters and channels +associated with it, located in "stp-policy" subsystem directory in +configfs. The topmost directory's name (the policy) is formatted as +the STM device name to which this policy applies and and arbitrary +string identifier separated by a stop. From the examle above, a rule +may look like this: + +$ ls /config/stp-policy/dummy_stm.my-policy/user +channels masters +$ cat /config/stp-policy/dummy_stm.my-policy/user/masters +48 63 +$ cat /config/stp-policy/dummy_stm.my-policy/user/channels +0 127 + +which means that the master allocation pool for this rule consists of +masters 48 through 63 and channel allocation pool has channels 0 +through 127 in it. Now, any producer (trace source) identifying itself +with "user" identification string will be allocated a master and +channel from within these ranges. + +These rules can be nested, for example, one can define a rule "dummy" +under "user" directory from the example above and this new rule will +be used for trace sources with the id string of "user/dummy". + +Trace sources have to open the stm class device's node and write their +trace data into its file descriptor. In order to identify themselves +to the policy, they need to do a STP_POLICY_ID_SET ioctl on this file +descriptor providing their id string. Otherwise, they will be +automatically allocated a master/channel pair upon first write to this +file descriptor according to the "default" rule of the policy, if such +exists. + +Some STM devices may allow direct mapping of the channel mmio regions +to userspace for zero-copy writing. One mappable page (in terms of +mmu) will usually contain multiple channels' mmios, so the user will +need to allocate that many channels to themselves (via the +aforementioned ioctl() call) to be able to do this. That is, if your +stm device's channel mmio region is 64 bytes and hardware page size is +4096 bytes, after a successful STP_POLICY_ID_SET ioctl() call with +width==64, you should be able to mmap() one page on this file +descriptor and obtain direct access to an mmio region for 64 channels. + +For kernel-based trace sources, there is "stm_source" device +class. Devices of this class can be connected and disconnected to/from +stm devices at runtime via a sysfs attribute. + +Examples of STM devices are Intel(R) Trace Hub [1] and Coresight STM +[2]. + +[1] https://software.intel.com/sites/default/files/managed/d3/3c/intel-th-developer-manual.pdf +[2] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0444b/index.html diff --git a/drivers/Kconfig b/drivers/Kconfig index 46b4a8e0f859..b6e1cea63f81 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -188,4 +188,6 @@ source "drivers/nvdimm/Kconfig" source "drivers/nvmem/Kconfig" +source "drivers/hwtracing/stm/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index b250b36b54f2..e71b5e2a2c71 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -165,5 +165,6 @@ obj-$(CONFIG_PERF_EVENTS) += perf/ obj-$(CONFIG_RAS) += ras/ obj-$(CONFIG_THUNDERBOLT) += thunderbolt/ obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/ +obj-$(CONFIG_STM) += hwtracing/stm/ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_NVMEM) += nvmem/ diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig new file mode 100644 index 000000000000..e101cb4f8e43 --- /dev/null +++ b/drivers/hwtracing/stm/Kconfig @@ -0,0 +1,8 @@ +config STM + tristate "System Trace Module devices" + help + A System Trace Module (STM) is a device exporting data in System + Trace Protocol (STP) format as defined by MIPI STP standards. + Examples of such devices are Intel(R) Trace Hub and Coresight STM. + + Say Y here to enable System Trace Module device support. diff --git a/drivers/hwtracing/stm/Makefile b/drivers/hwtracing/stm/Makefile new file mode 100644 index 000000000000..adec7016499f --- /dev/null +++ b/drivers/hwtracing/stm/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_STM) += stm_core.o + +stm_core-y := core.o policy.o diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c new file mode 100644 index 000000000000..b79c42c62557 --- /dev/null +++ b/drivers/hwtracing/stm/core.c @@ -0,0 +1,1029 @@ +/* + * System Trace Module (STM) infrastructure + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * STM class implements generic infrastructure for System Trace Module devices + * as defined in MIPI STPv2 specification. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "stm.h" + +#include + +static unsigned int stm_core_up; + +/* + * The SRCU here makes sure that STM device doesn't disappear from under a + * stm_source_write() caller, which may want to have as little overhead as + * possible. + */ +static struct srcu_struct stm_source_srcu; + +static ssize_t masters_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct stm_device *stm = to_stm_device(dev); + int ret; + + ret = sprintf(buf, "%u %u\n", stm->data->sw_start, stm->data->sw_end); + + return ret; +} + +static DEVICE_ATTR_RO(masters); + +static ssize_t channels_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct stm_device *stm = to_stm_device(dev); + int ret; + + ret = sprintf(buf, "%u\n", stm->data->sw_nchannels); + + return ret; +} + +static DEVICE_ATTR_RO(channels); + +static struct attribute *stm_attrs[] = { + &dev_attr_masters.attr, + &dev_attr_channels.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(stm); + +static struct class stm_class = { + .name = "stm", + .dev_groups = stm_groups, +}; + +static int stm_dev_match(struct device *dev, const void *data) +{ + const char *name = data; + + return sysfs_streq(name, dev_name(dev)); +} + +/** + * stm_find_device() - find stm device by name + * @buf: character buffer containing the name + * + * This is called when either policy gets assigned to an stm device or an + * stm_source device gets linked to an stm device. + * + * This grabs device's reference (get_device()) and module reference, both + * of which the calling path needs to make sure to drop with stm_put_device(). + * + * Return: stm device pointer or null if lookup failed. + */ +struct stm_device *stm_find_device(const char *buf) +{ + struct stm_device *stm; + struct device *dev; + + if (!stm_core_up) + return NULL; + + dev = class_find_device(&stm_class, NULL, buf, stm_dev_match); + if (!dev) + return NULL; + + stm = to_stm_device(dev); + if (!try_module_get(stm->owner)) { + put_device(dev); + return NULL; + } + + return stm; +} + +/** + * stm_put_device() - drop references on the stm device + * @stm: stm device, previously acquired by stm_find_device() + * + * This drops the module reference and device reference taken by + * stm_find_device(). + */ +void stm_put_device(struct stm_device *stm) +{ + module_put(stm->owner); + put_device(&stm->dev); +} + +/* + * Internally we only care about software-writable masters here, that is the + * ones in the range [stm_data->sw_start..stm_data..sw_end], however we need + * original master numbers to be visible externally, since they are the ones + * that will appear in the STP stream. Thus, the internal bookkeeping uses + * $master - stm_data->sw_start to reference master descriptors and such. + */ + +#define __stm_master(_s, _m) \ + ((_s)->masters[(_m) - (_s)->data->sw_start]) + +static inline struct stp_master * +stm_master(struct stm_device *stm, unsigned int idx) +{ + if (idx < stm->data->sw_start || idx > stm->data->sw_end) + return NULL; + + return __stm_master(stm, idx); +} + +static int stp_master_alloc(struct stm_device *stm, unsigned int idx) +{ + struct stp_master *master; + size_t size; + + size = ALIGN(stm->data->sw_nchannels, 8) / 8; + size += sizeof(struct stp_master); + master = kzalloc(size, GFP_ATOMIC); + if (!master) + return -ENOMEM; + + master->nr_free = stm->data->sw_nchannels; + __stm_master(stm, idx) = master; + + return 0; +} + +static void stp_master_free(struct stm_device *stm, unsigned int idx) +{ + struct stp_master *master = stm_master(stm, idx); + + if (!master) + return; + + __stm_master(stm, idx) = NULL; + kfree(master); +} + +static void stm_output_claim(struct stm_device *stm, struct stm_output *output) +{ + struct stp_master *master = stm_master(stm, output->master); + + if (WARN_ON_ONCE(master->nr_free < output->nr_chans)) + return; + + bitmap_allocate_region(&master->chan_map[0], output->channel, + ilog2(output->nr_chans)); + + master->nr_free -= output->nr_chans; +} + +static void +stm_output_disclaim(struct stm_device *stm, struct stm_output *output) +{ + struct stp_master *master = stm_master(stm, output->master); + + bitmap_release_region(&master->chan_map[0], output->channel, + ilog2(output->nr_chans)); + + output->nr_chans = 0; + master->nr_free += output->nr_chans; +} + +/* + * This is like bitmap_find_free_region(), except it can ignore @start bits + * at the beginning. + */ +static int find_free_channels(unsigned long *bitmap, unsigned int start, + unsigned int end, unsigned int width) +{ + unsigned int pos; + int i; + + for (pos = start; pos < end + 1; pos = ALIGN(pos, width)) { + pos = find_next_zero_bit(bitmap, end + 1, pos); + if (pos + width > end + 1) + break; + + if (pos & (width - 1)) + continue; + + for (i = 1; i < width && !test_bit(pos + i, bitmap); i++) + ; + if (i == width) + return pos; + } + + return -1; +} + +static unsigned int +stm_find_master_chan(struct stm_device *stm, unsigned int width, + unsigned int *mstart, unsigned int mend, + unsigned int *cstart, unsigned int cend) +{ + struct stp_master *master; + unsigned int midx; + int pos, err; + + for (midx = *mstart; midx <= mend; midx++) { + if (!stm_master(stm, midx)) { + err = stp_master_alloc(stm, midx); + if (err) + return err; + } + + master = stm_master(stm, midx); + + if (!master->nr_free) + continue; + + pos = find_free_channels(master->chan_map, *cstart, cend, + width); + if (pos < 0) + continue; + + *mstart = midx; + *cstart = pos; + return 0; + } + + return -ENOSPC; +} + +static int stm_output_assign(struct stm_device *stm, unsigned int width, + struct stp_policy_node *policy_node, + struct stm_output *output) +{ + unsigned int midx, cidx, mend, cend; + int ret = -EINVAL; + + if (width > stm->data->sw_nchannels) + return -EINVAL; + + if (policy_node) { + stp_policy_node_get_ranges(policy_node, + &midx, &mend, &cidx, &cend); + } else { + midx = stm->data->sw_start; + cidx = 0; + mend = stm->data->sw_end; + cend = stm->data->sw_nchannels - 1; + } + + spin_lock(&stm->mc_lock); + /* output is already assigned -- shouldn't happen */ + if (WARN_ON_ONCE(output->nr_chans)) + goto unlock; + + ret = stm_find_master_chan(stm, width, &midx, mend, &cidx, cend); + if (ret) + goto unlock; + + output->master = midx; + output->channel = cidx; + output->nr_chans = width; + stm_output_claim(stm, output); + dev_dbg(&stm->dev, "assigned %u:%u (+%u)\n", midx, cidx, width); + + ret = 0; +unlock: + spin_unlock(&stm->mc_lock); + + return ret; +} + +static void stm_output_free(struct stm_device *stm, struct stm_output *output) +{ + spin_lock(&stm->mc_lock); + if (output->nr_chans) + stm_output_disclaim(stm, output); + spin_unlock(&stm->mc_lock); +} + +static int major_match(struct device *dev, const void *data) +{ + unsigned int major = *(unsigned int *)data; + + return MAJOR(dev->devt) == major; +} + +static int stm_char_open(struct inode *inode, struct file *file) +{ + struct stm_file *stmf; + struct device *dev; + unsigned int major = imajor(inode); + int err = -ENODEV; + + dev = class_find_device(&stm_class, NULL, &major, major_match); + if (!dev) + return -ENODEV; + + stmf = kzalloc(sizeof(*stmf), GFP_KERNEL); + if (!stmf) + return -ENOMEM; + + stmf->stm = to_stm_device(dev); + + if (!try_module_get(stmf->stm->owner)) + goto err_free; + + file->private_data = stmf; + + return nonseekable_open(inode, file); + +err_free: + kfree(stmf); + + return err; +} + +static int stm_char_release(struct inode *inode, struct file *file) +{ + struct stm_file *stmf = file->private_data; + + stm_output_free(stmf->stm, &stmf->output); + stm_put_device(stmf->stm); + kfree(stmf); + + return 0; +} + +static int stm_file_assign(struct stm_file *stmf, char *id, unsigned int width) +{ + struct stm_device *stm = stmf->stm; + int ret; + + stmf->policy_node = stp_policy_node_lookup(stm, id); + + ret = stm_output_assign(stm, width, stmf->policy_node, &stmf->output); + + if (stmf->policy_node) + stp_policy_node_put(stmf->policy_node); + + return ret; +} + +static void stm_write(struct stm_data *data, unsigned int master, + unsigned int channel, const char *buf, size_t count) +{ + unsigned int flags = STP_PACKET_TIMESTAMPED; + const unsigned char *p = buf, nil = 0; + size_t pos; + ssize_t sz; + + for (pos = 0, p = buf; count > pos; pos += sz, p += sz) { + sz = min_t(unsigned int, count - pos, 8); + sz = data->packet(data, master, channel, STP_PACKET_DATA, flags, + sz, p); + flags = 0; + } + + data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, &nil); +} + +static ssize_t stm_char_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct stm_file *stmf = file->private_data; + struct stm_device *stm = stmf->stm; + char *kbuf; + int err; + + /* + * if no m/c have been assigned to this writer up to this + * point, use "default" policy entry + */ + if (!stmf->output.nr_chans) { + err = stm_file_assign(stmf, "default", 1); + /* + * EBUSY means that somebody else just assigned this + * output, which is just fine for write() + */ + if (err && err != -EBUSY) + return err; + } + + kbuf = kmalloc(count + 1, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + err = copy_from_user(kbuf, buf, count); + if (err) { + kfree(kbuf); + return -EFAULT; + } + + stm_write(stm->data, stmf->output.master, stmf->output.channel, kbuf, + count); + + kfree(kbuf); + + return count; +} + +static int stm_char_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct stm_file *stmf = file->private_data; + struct stm_device *stm = stmf->stm; + unsigned long size, phys; + + if (!stm->data->mmio_addr) + return -EOPNOTSUPP; + + if (vma->vm_pgoff) + return -EINVAL; + + size = vma->vm_end - vma->vm_start; + + if (stmf->output.nr_chans * stm->data->sw_mmiosz != size) + return -EINVAL; + + phys = stm->data->mmio_addr(stm->data, stmf->output.master, + stmf->output.channel, + stmf->output.nr_chans); + + if (!phys) + return -EINVAL; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; + vm_iomap_memory(vma, phys, size); + + return 0; +} + +static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg) +{ + struct stm_device *stm = stmf->stm; + struct stp_policy_id *id; + int ret = -EINVAL; + u32 size; + + if (stmf->output.nr_chans) + return -EBUSY; + + if (copy_from_user(&size, arg, sizeof(size))) + return -EFAULT; + + if (size >= PATH_MAX + sizeof(*id)) + return -EINVAL; + + /* + * size + 1 to make sure the .id string at the bottom is terminated, + * which is also why memdup_user() is not useful here + */ + id = kzalloc(size + 1, GFP_KERNEL); + if (!id) + return -ENOMEM; + + if (copy_from_user(id, arg, size)) { + ret = -EFAULT; + goto err_free; + } + + if (id->__reserved_0 || id->__reserved_1) + goto err_free; + + if (id->width < 1 || + id->width > PAGE_SIZE / stm->data->sw_mmiosz) + goto err_free; + + ret = stm_file_assign(stmf, id->id, id->width); + if (ret) + goto err_free; + + ret = 0; + + if (stm->data->link) + ret = stm->data->link(stm->data, stmf->output.master, + stmf->output.channel); + + if (ret) { + stm_output_free(stmf->stm, &stmf->output); + stm_put_device(stmf->stm); + } + +err_free: + kfree(id); + + return ret; +} + +static int stm_char_policy_get_ioctl(struct stm_file *stmf, void __user *arg) +{ + struct stp_policy_id id = { + .size = sizeof(id), + .master = stmf->output.master, + .channel = stmf->output.channel, + .width = stmf->output.nr_chans, + .__reserved_0 = 0, + .__reserved_1 = 0, + }; + + return copy_to_user(arg, &id, id.size) ? -EFAULT : 0; +} + +static long +stm_char_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct stm_file *stmf = file->private_data; + struct stm_data *stm_data = stmf->stm->data; + int err = -ENOTTY; + u64 options; + + switch (cmd) { + case STP_POLICY_ID_SET: + err = stm_char_policy_set_ioctl(stmf, (void __user *)arg); + if (err) + return err; + + return stm_char_policy_get_ioctl(stmf, (void __user *)arg); + + case STP_POLICY_ID_GET: + return stm_char_policy_get_ioctl(stmf, (void __user *)arg); + + case STP_SET_OPTIONS: + if (copy_from_user(&options, (u64 __user *)arg, sizeof(u64))) + return -EFAULT; + + if (stm_data->set_options) + err = stm_data->set_options(stm_data, + stmf->output.master, + stmf->output.channel, + stmf->output.nr_chans, + options); + + break; + default: + break; + } + + return err; +} + +#ifdef CONFIG_COMPAT +static long +stm_char_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + return stm_char_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); +} +#else +#define stm_char_compat_ioctl NULL +#endif + +static const struct file_operations stm_fops = { + .open = stm_char_open, + .release = stm_char_release, + .write = stm_char_write, + .mmap = stm_char_mmap, + .unlocked_ioctl = stm_char_ioctl, + .compat_ioctl = stm_char_compat_ioctl, + .llseek = no_llseek, +}; + +static void stm_device_release(struct device *dev) +{ + struct stm_device *stm = to_stm_device(dev); + + kfree(stm); +} + +int stm_register_device(struct device *parent, struct stm_data *stm_data, + struct module *owner) +{ + struct stm_device *stm; + unsigned int nmasters; + int err = -ENOMEM; + + if (!stm_core_up) + return -EPROBE_DEFER; + + if (!stm_data->packet || !stm_data->sw_nchannels) + return -EINVAL; + + nmasters = stm_data->sw_end - stm_data->sw_start; + stm = kzalloc(sizeof(*stm) + nmasters * sizeof(void *), GFP_KERNEL); + if (!stm) + return -ENOMEM; + + stm->major = register_chrdev(0, stm_data->name, &stm_fops); + if (stm->major < 0) + goto err_free; + + device_initialize(&stm->dev); + stm->dev.devt = MKDEV(stm->major, 0); + stm->dev.class = &stm_class; + stm->dev.parent = parent; + stm->dev.release = stm_device_release; + + err = kobject_set_name(&stm->dev.kobj, "%s", stm_data->name); + if (err) + goto err_device; + + err = device_add(&stm->dev); + if (err) + goto err_device; + + spin_lock_init(&stm->link_lock); + INIT_LIST_HEAD(&stm->link_list); + + spin_lock_init(&stm->mc_lock); + mutex_init(&stm->policy_mutex); + stm->sw_nmasters = nmasters; + stm->owner = owner; + stm->data = stm_data; + stm_data->stm = stm; + + return 0; + +err_device: + put_device(&stm->dev); +err_free: + kfree(stm); + + return err; +} +EXPORT_SYMBOL_GPL(stm_register_device); + +static void __stm_source_link_drop(struct stm_source_device *src, + struct stm_device *stm); + +void stm_unregister_device(struct stm_data *stm_data) +{ + struct stm_device *stm = stm_data->stm; + struct stm_source_device *src, *iter; + int i; + + spin_lock(&stm->link_lock); + list_for_each_entry_safe(src, iter, &stm->link_list, link_entry) { + __stm_source_link_drop(src, stm); + } + spin_unlock(&stm->link_lock); + + synchronize_srcu(&stm_source_srcu); + + unregister_chrdev(stm->major, stm_data->name); + + mutex_lock(&stm->policy_mutex); + if (stm->policy) + stp_policy_unbind(stm->policy); + mutex_unlock(&stm->policy_mutex); + + for (i = 0; i < stm->sw_nmasters; i++) + stp_master_free(stm, i); + + device_unregister(&stm->dev); + stm_data->stm = NULL; +} +EXPORT_SYMBOL_GPL(stm_unregister_device); + +/** + * stm_source_link_add() - connect an stm_source device to an stm device + * @src: stm_source device + * @stm: stm device + * + * This function establishes a link from stm_source to an stm device so that + * the former can send out trace data to the latter. + * + * Return: 0 on success, -errno otherwise. + */ +static int stm_source_link_add(struct stm_source_device *src, + struct stm_device *stm) +{ + char *id; + int err; + + spin_lock(&stm->link_lock); + spin_lock(&src->link_lock); + + /* src->link is dereferenced under stm_source_srcu but not the list */ + rcu_assign_pointer(src->link, stm); + list_add_tail(&src->link_entry, &stm->link_list); + + spin_unlock(&src->link_lock); + spin_unlock(&stm->link_lock); + + id = kstrdup(src->data->name, GFP_KERNEL); + if (id) { + src->policy_node = + stp_policy_node_lookup(stm, id); + + kfree(id); + } + + err = stm_output_assign(stm, src->data->nr_chans, + src->policy_node, &src->output); + + if (src->policy_node) + stp_policy_node_put(src->policy_node); + + if (err) + goto fail_detach; + + /* this is to notify the STM device that a new link has been made */ + if (stm->data->link) + err = stm->data->link(stm->data, src->output.master, + src->output.channel); + + if (err) + goto fail_free_output; + + /* this is to let the source carry out all necessary preparations */ + if (src->data->link) + src->data->link(src->data); + + return 0; + +fail_free_output: + stm_output_free(stm, &src->output); + stm_put_device(stm); + +fail_detach: + spin_lock(&stm->link_lock); + spin_lock(&src->link_lock); + + rcu_assign_pointer(src->link, NULL); + list_del_init(&src->link_entry); + + spin_unlock(&src->link_lock); + spin_unlock(&stm->link_lock); + + return err; +} + +/** + * __stm_source_link_drop() - detach stm_source from an stm device + * @src: stm_source device + * @stm: stm device + * + * If @stm is @src::link, disconnect them from one another and put the + * reference on the @stm device. + * + * Caller must hold stm::link_lock. + */ +static void __stm_source_link_drop(struct stm_source_device *src, + struct stm_device *stm) +{ + spin_lock(&src->link_lock); + if (WARN_ON_ONCE(src->link != stm)) { + spin_unlock(&src->link_lock); + return; + } + + stm_output_free(src->link, &src->output); + /* caller must hold stm::link_lock */ + list_del_init(&src->link_entry); + /* matches stm_find_device() from stm_source_link_store() */ + stm_put_device(src->link); + rcu_assign_pointer(src->link, NULL); + + spin_unlock(&src->link_lock); +} + +/** + * stm_source_link_drop() - detach stm_source from its stm device + * @src: stm_source device + * + * Unlinking means disconnecting from source's STM device; after this + * writes will be unsuccessful until it is linked to a new STM device. + * + * This will happen on "stm_source_link" sysfs attribute write to undo + * the existing link (if any), or on linked STM device's de-registration. + */ +static void stm_source_link_drop(struct stm_source_device *src) +{ + struct stm_device *stm; + int idx; + + idx = srcu_read_lock(&stm_source_srcu); + stm = srcu_dereference(src->link, &stm_source_srcu); + + if (stm) { + if (src->data->unlink) + src->data->unlink(src->data); + + spin_lock(&stm->link_lock); + __stm_source_link_drop(src, stm); + spin_unlock(&stm->link_lock); + } + + srcu_read_unlock(&stm_source_srcu, idx); +} + +static ssize_t stm_source_link_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct stm_source_device *src = to_stm_source_device(dev); + struct stm_device *stm; + int idx, ret; + + idx = srcu_read_lock(&stm_source_srcu); + stm = srcu_dereference(src->link, &stm_source_srcu); + ret = sprintf(buf, "%s\n", + stm ? dev_name(&stm->dev) : ""); + srcu_read_unlock(&stm_source_srcu, idx); + + return ret; +} + +static ssize_t stm_source_link_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stm_source_device *src = to_stm_source_device(dev); + struct stm_device *link; + int err; + + stm_source_link_drop(src); + + link = stm_find_device(buf); + if (!link) + return -EINVAL; + + err = stm_source_link_add(src, link); + if (err) + stm_put_device(link); + + return err ? : count; +} + +static DEVICE_ATTR_RW(stm_source_link); + +static struct attribute *stm_source_attrs[] = { + &dev_attr_stm_source_link.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(stm_source); + +static struct class stm_source_class = { + .name = "stm_source", + .dev_groups = stm_source_groups, +}; + +static void stm_source_device_release(struct device *dev) +{ + struct stm_source_device *src = to_stm_source_device(dev); + + kfree(src); +} + +/** + * stm_source_register_device() - register an stm_source device + * @parent: parent device + * @data: device description structure + * + * This will create a device of stm_source class that can write + * data to an stm device once linked. + * + * Return: 0 on success, -errno otherwise. + */ +int stm_source_register_device(struct device *parent, + struct stm_source_data *data) +{ + struct stm_source_device *src; + int err; + + if (!stm_core_up) + return -EPROBE_DEFER; + + src = kzalloc(sizeof(*src), GFP_KERNEL); + if (!src) + return -ENOMEM; + + device_initialize(&src->dev); + src->dev.class = &stm_source_class; + src->dev.parent = parent; + src->dev.release = stm_source_device_release; + + err = kobject_set_name(&src->dev.kobj, "%s", data->name); + if (err) + goto err; + + err = device_add(&src->dev); + if (err) + goto err; + + spin_lock_init(&src->link_lock); + INIT_LIST_HEAD(&src->link_entry); + src->data = data; + data->src = src; + + return 0; + +err: + put_device(&src->dev); + kfree(src); + + return err; +} +EXPORT_SYMBOL_GPL(stm_source_register_device); + +/** + * stm_source_unregister_device() - unregister an stm_source device + * @data: device description that was used to register the device + * + * This will remove a previously created stm_source device from the system. + */ +void stm_source_unregister_device(struct stm_source_data *data) +{ + struct stm_source_device *src = data->src; + + stm_source_link_drop(src); + + device_destroy(&stm_source_class, src->dev.devt); +} +EXPORT_SYMBOL_GPL(stm_source_unregister_device); + +int stm_source_write(struct stm_source_data *data, unsigned int chan, + const char *buf, size_t count) +{ + struct stm_source_device *src = data->src; + struct stm_device *stm; + int idx; + + if (!src->output.nr_chans) + return -ENODEV; + + if (chan >= src->output.nr_chans) + return -EINVAL; + + idx = srcu_read_lock(&stm_source_srcu); + + stm = srcu_dereference(src->link, &stm_source_srcu); + if (stm) + stm_write(stm->data, src->output.master, + src->output.channel + chan, + buf, count); + else + count = -ENODEV; + + srcu_read_unlock(&stm_source_srcu, idx); + + return count; +} +EXPORT_SYMBOL_GPL(stm_source_write); + +static int __init stm_core_init(void) +{ + int err; + + err = class_register(&stm_class); + if (err) + return err; + + err = class_register(&stm_source_class); + if (err) + goto err_stm; + + err = stp_configfs_init(); + if (err) + goto err_src; + + init_srcu_struct(&stm_source_srcu); + + stm_core_up++; + + return 0; + +err_src: + class_unregister(&stm_source_class); +err_stm: + class_unregister(&stm_class); + + return err; +} + +module_init(stm_core_init); + +static void __exit stm_core_exit(void) +{ + cleanup_srcu_struct(&stm_source_srcu); + class_unregister(&stm_source_class); + class_unregister(&stm_class); + stp_configfs_exit(); +} + +module_exit(stm_core_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("System Trace Module device class"); +MODULE_AUTHOR("Alexander Shishkin "); diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c new file mode 100644 index 000000000000..6498a9dbb7bd --- /dev/null +++ b/drivers/hwtracing/stm/policy.c @@ -0,0 +1,529 @@ +/* + * System Trace Module (STM) master/channel allocation policy management + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * A master/channel allocation policy allows mapping string identifiers to + * master and channel ranges, where allocation can be done. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include "stm.h" + +/* + * STP Master/Channel allocation policy configfs layout. + */ + +struct stp_policy { + struct config_group group; + struct stm_device *stm; +}; + +struct stp_policy_node { + struct config_group group; + struct stp_policy *policy; + unsigned int first_master; + unsigned int last_master; + unsigned int first_channel; + unsigned int last_channel; +}; + +static struct configfs_subsystem stp_policy_subsys; + +void stp_policy_node_get_ranges(struct stp_policy_node *policy_node, + unsigned int *mstart, unsigned int *mend, + unsigned int *cstart, unsigned int *cend) +{ + *mstart = policy_node->first_master; + *mend = policy_node->last_master; + *cstart = policy_node->first_channel; + *cend = policy_node->last_channel; +} + +static inline char *stp_policy_node_name(struct stp_policy_node *policy_node) +{ + return policy_node->group.cg_item.ci_name ? : ""; +} + +static inline struct stp_policy *to_stp_policy(struct config_item *item) +{ + return item ? + container_of(to_config_group(item), struct stp_policy, group) : + NULL; +} + +static inline struct stp_policy_node * +to_stp_policy_node(struct config_item *item) +{ + return item ? + container_of(to_config_group(item), struct stp_policy_node, + group) : + NULL; +} + +static ssize_t stp_policy_node_masters_show(struct stp_policy_node *policy_node, + char *page) +{ + ssize_t count; + + count = sprintf(page, "%u %u\n", policy_node->first_master, + policy_node->last_master); + + return count; +} + +static ssize_t +stp_policy_node_masters_store(struct stp_policy_node *policy_node, + const char *page, size_t count) +{ + unsigned int first, last; + struct stm_device *stm; + char *p = (char *)page; + ssize_t ret = -ENODEV; + + if (sscanf(p, "%u %u", &first, &last) != 2) + return -EINVAL; + + mutex_lock(&stp_policy_subsys.su_mutex); + stm = policy_node->policy->stm; + if (!stm) + goto unlock; + + /* must be within [sw_start..sw_end], which is an inclusive range */ + if (first > INT_MAX || last > INT_MAX || first > last || + first < stm->data->sw_start || + last > stm->data->sw_end) { + ret = -ERANGE; + goto unlock; + } + + ret = count; + policy_node->first_master = first; + policy_node->last_master = last; + +unlock: + mutex_unlock(&stp_policy_subsys.su_mutex); + + return ret; +} + +static ssize_t +stp_policy_node_channels_show(struct stp_policy_node *policy_node, char *page) +{ + ssize_t count; + + count = sprintf(page, "%u %u\n", policy_node->first_channel, + policy_node->last_channel); + + return count; +} + +static ssize_t +stp_policy_node_channels_store(struct stp_policy_node *policy_node, + const char *page, size_t count) +{ + unsigned int first, last; + struct stm_device *stm; + char *p = (char *)page; + ssize_t ret = -ENODEV; + + if (sscanf(p, "%u %u", &first, &last) != 2) + return -EINVAL; + + mutex_lock(&stp_policy_subsys.su_mutex); + stm = policy_node->policy->stm; + if (!stm) + goto unlock; + + if (first > INT_MAX || last > INT_MAX || first > last || + last >= stm->data->sw_nchannels) { + ret = -ERANGE; + goto unlock; + } + + ret = count; + policy_node->first_channel = first; + policy_node->last_channel = last; + +unlock: + mutex_unlock(&stp_policy_subsys.su_mutex); + + return ret; +} + +static void stp_policy_node_release(struct config_item *item) +{ + kfree(to_stp_policy_node(item)); +} + +struct stp_policy_node_attribute { + struct configfs_attribute attr; + ssize_t (*show)(struct stp_policy_node *, char *); + ssize_t (*store)(struct stp_policy_node *, const char *, size_t); +}; + +static ssize_t stp_policy_node_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + struct stp_policy_node *policy_node = to_stp_policy_node(item); + struct stp_policy_node_attribute *pn_attr = + container_of(attr, struct stp_policy_node_attribute, attr); + ssize_t count = 0; + + if (pn_attr->show) + count = pn_attr->show(policy_node, page); + + return count; +} + +static ssize_t stp_policy_node_attr_store(struct config_item *item, + struct configfs_attribute *attr, + const char *page, size_t len) +{ + struct stp_policy_node *policy_node = to_stp_policy_node(item); + struct stp_policy_node_attribute *pn_attr = + container_of(attr, struct stp_policy_node_attribute, attr); + ssize_t count = -EINVAL; + + if (pn_attr->store) + count = pn_attr->store(policy_node, page, len); + + return count; +} + +static struct configfs_item_operations stp_policy_node_item_ops = { + .release = stp_policy_node_release, + .show_attribute = stp_policy_node_attr_show, + .store_attribute = stp_policy_node_attr_store, +}; + +static struct stp_policy_node_attribute stp_policy_node_attr_range = { + .attr = { + .ca_owner = THIS_MODULE, + .ca_name = "masters", + .ca_mode = S_IRUGO | S_IWUSR, + }, + .show = stp_policy_node_masters_show, + .store = stp_policy_node_masters_store, +}; + +static struct stp_policy_node_attribute stp_policy_node_attr_channels = { + .attr = { + .ca_owner = THIS_MODULE, + .ca_name = "channels", + .ca_mode = S_IRUGO | S_IWUSR, + }, + .show = stp_policy_node_channels_show, + .store = stp_policy_node_channels_store, +}; + +static struct configfs_attribute *stp_policy_node_attrs[] = { + &stp_policy_node_attr_range.attr, + &stp_policy_node_attr_channels.attr, + NULL, +}; + +static struct config_item_type stp_policy_type; +static struct config_item_type stp_policy_node_type; + +static struct config_group * +stp_policy_node_make(struct config_group *group, const char *name) +{ + struct stp_policy_node *policy_node, *parent_node; + struct stp_policy *policy; + + if (group->cg_item.ci_type == &stp_policy_type) { + policy = container_of(group, struct stp_policy, group); + } else { + parent_node = container_of(group, struct stp_policy_node, + group); + policy = parent_node->policy; + } + + if (!policy->stm) + return ERR_PTR(-ENODEV); + + policy_node = kzalloc(sizeof(struct stp_policy_node), GFP_KERNEL); + if (!policy_node) + return ERR_PTR(-ENOMEM); + + config_group_init_type_name(&policy_node->group, name, + &stp_policy_node_type); + + policy_node->policy = policy; + + /* default values for the attributes */ + policy_node->first_master = policy->stm->data->sw_start; + policy_node->last_master = policy->stm->data->sw_end; + policy_node->first_channel = 0; + policy_node->last_channel = policy->stm->data->sw_nchannels - 1; + + return &policy_node->group; +} + +static void +stp_policy_node_drop(struct config_group *group, struct config_item *item) +{ + config_item_put(item); +} + +static struct configfs_group_operations stp_policy_node_group_ops = { + .make_group = stp_policy_node_make, + .drop_item = stp_policy_node_drop, +}; + +static struct config_item_type stp_policy_node_type = { + .ct_item_ops = &stp_policy_node_item_ops, + .ct_group_ops = &stp_policy_node_group_ops, + .ct_attrs = stp_policy_node_attrs, + .ct_owner = THIS_MODULE, +}; + +/* + * Root group: policies. + */ +static struct configfs_attribute stp_policy_attr_device = { + .ca_owner = THIS_MODULE, + .ca_name = "device", + .ca_mode = S_IRUGO, +}; + +static struct configfs_attribute *stp_policy_attrs[] = { + &stp_policy_attr_device, + NULL, +}; + +static ssize_t stp_policy_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + struct stp_policy *policy = to_stp_policy(item); + ssize_t count; + + count = sprintf(page, "%s\n", + (policy && policy->stm) ? + policy->stm->data->name : + ""); + + return count; +} + +void stp_policy_unbind(struct stp_policy *policy) +{ + struct stm_device *stm = policy->stm; + + if (WARN_ON_ONCE(!policy->stm)) + return; + + mutex_lock(&stm->policy_mutex); + stm->policy = NULL; + mutex_unlock(&stm->policy_mutex); + + policy->stm = NULL; + + stm_put_device(stm); +} + +static void stp_policy_release(struct config_item *item) +{ + struct stp_policy *policy = to_stp_policy(item); + + stp_policy_unbind(policy); + kfree(policy); +} + +static struct configfs_item_operations stp_policy_item_ops = { + .release = stp_policy_release, + .show_attribute = stp_policy_attr_show, +}; + +static struct configfs_group_operations stp_policy_group_ops = { + .make_group = stp_policy_node_make, +}; + +static struct config_item_type stp_policy_type = { + .ct_item_ops = &stp_policy_item_ops, + .ct_group_ops = &stp_policy_group_ops, + .ct_attrs = stp_policy_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group * +stp_policies_make(struct config_group *group, const char *name) +{ + struct config_group *ret; + struct stm_device *stm; + char *devname, *p; + + devname = kasprintf(GFP_KERNEL, "%s", name); + if (!devname) + return ERR_PTR(-ENOMEM); + + /* + * node must look like ., where + * is the name of an existing stm device and + * is an arbitrary string + */ + p = strchr(devname, '.'); + if (!p) { + kfree(devname); + return ERR_PTR(-EINVAL); + } + + *p++ = '\0'; + + stm = stm_find_device(devname); + kfree(devname); + + if (!stm) + return ERR_PTR(-ENODEV); + + mutex_lock(&stm->policy_mutex); + if (stm->policy) { + ret = ERR_PTR(-EBUSY); + goto unlock_policy; + } + + stm->policy = kzalloc(sizeof(*stm->policy), GFP_KERNEL); + if (!stm->policy) { + ret = ERR_PTR(-ENOMEM); + goto unlock_policy; + } + + config_group_init_type_name(&stm->policy->group, name, + &stp_policy_type); + stm->policy->stm = stm; + + ret = &stm->policy->group; + +unlock_policy: + mutex_unlock(&stm->policy_mutex); + + if (IS_ERR(ret)) + stm_put_device(stm); + + return ret; +} + +static struct configfs_group_operations stp_policies_group_ops = { + .make_group = stp_policies_make, +}; + +static struct config_item_type stp_policies_type = { + .ct_group_ops = &stp_policies_group_ops, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem stp_policy_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "stp-policy", + .ci_type = &stp_policies_type, + }, + }, +}; + +/* + * Lock the policy mutex from the outside + */ +static struct stp_policy_node * +__stp_policy_node_lookup(struct stp_policy *policy, char *s) +{ + struct stp_policy_node *policy_node, *ret; + struct list_head *head = &policy->group.cg_children; + struct config_item *item; + char *start, *end = s; + + if (list_empty(head)) + return NULL; + + /* return the first entry if everything else fails */ + item = list_entry(head->next, struct config_item, ci_entry); + ret = to_stp_policy_node(item); + +next: + for (;;) { + start = strsep(&end, "/"); + if (!start) + break; + + if (!*start) + continue; + + list_for_each_entry(item, head, ci_entry) { + policy_node = to_stp_policy_node(item); + + if (!strcmp(start, + policy_node->group.cg_item.ci_name)) { + ret = policy_node; + + if (!end) + goto out; + + head = &policy_node->group.cg_children; + goto next; + } + } + break; + } + +out: + return ret; +} + + +struct stp_policy_node * +stp_policy_node_lookup(struct stm_device *stm, char *s) +{ + struct stp_policy_node *policy_node = NULL; + + mutex_lock(&stp_policy_subsys.su_mutex); + + mutex_lock(&stm->policy_mutex); + if (stm->policy) + policy_node = __stp_policy_node_lookup(stm->policy, s); + mutex_unlock(&stm->policy_mutex); + + if (policy_node) + config_item_get(&policy_node->group.cg_item); + mutex_unlock(&stp_policy_subsys.su_mutex); + + return policy_node; +} + +void stp_policy_node_put(struct stp_policy_node *policy_node) +{ + config_item_put(&policy_node->group.cg_item); +} + +int __init stp_configfs_init(void) +{ + int err; + + config_group_init(&stp_policy_subsys.su_group); + mutex_init(&stp_policy_subsys.su_mutex); + err = configfs_register_subsystem(&stp_policy_subsys); + + return err; +} + +void __exit stp_configfs_exit(void) +{ + configfs_unregister_subsystem(&stp_policy_subsys); +} diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h new file mode 100644 index 000000000000..cf33bf976abe --- /dev/null +++ b/drivers/hwtracing/stm/stm.h @@ -0,0 +1,87 @@ +/* + * System Trace Module (STM) infrastructure + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * STM class implements generic infrastructure for System Trace Module devices + * as defined in MIPI STPv2 specification. + */ + +#ifndef _STM_STM_H_ +#define _STM_STM_H_ + +struct stp_policy; +struct stp_policy_node; + +struct stp_policy_node * +stp_policy_node_lookup(struct stm_device *stm, char *s); +void stp_policy_node_put(struct stp_policy_node *policy_node); +void stp_policy_unbind(struct stp_policy *policy); + +void stp_policy_node_get_ranges(struct stp_policy_node *policy_node, + unsigned int *mstart, unsigned int *mend, + unsigned int *cstart, unsigned int *cend); +int stp_configfs_init(void); +void stp_configfs_exit(void); + +struct stp_master { + unsigned int nr_free; + unsigned long chan_map[0]; +}; + +struct stm_device { + struct device dev; + struct module *owner; + struct stp_policy *policy; + struct mutex policy_mutex; + int major; + unsigned int sw_nmasters; + struct stm_data *data; + spinlock_t link_lock; + struct list_head link_list; + /* master allocation */ + spinlock_t mc_lock; + struct stp_master *masters[0]; +}; + +#define to_stm_device(_d) \ + container_of((_d), struct stm_device, dev) + +struct stm_output { + unsigned int master; + unsigned int channel; + unsigned int nr_chans; +}; + +struct stm_file { + struct stm_device *stm; + struct stp_policy_node *policy_node; + struct stm_output output; +}; + +struct stm_device *stm_find_device(const char *name); +void stm_put_device(struct stm_device *stm); + +struct stm_source_device { + struct device dev; + struct stm_source_data *data; + spinlock_t link_lock; + struct stm_device *link; + struct list_head link_entry; + /* one output per stm_source device */ + struct stp_policy_node *policy_node; + struct stm_output output; +}; + +#define to_stm_source_device(_d) \ + container_of((_d), struct stm_source_device, dev) + +#endif /* _STM_STM_H_ */ diff --git a/include/linux/stm.h b/include/linux/stm.h new file mode 100644 index 000000000000..9d0083d364e6 --- /dev/null +++ b/include/linux/stm.h @@ -0,0 +1,126 @@ +/* + * System Trace Module (STM) infrastructure apis + * Copyright (C) 2014 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#ifndef _STM_H_ +#define _STM_H_ + +#include + +/** + * enum stp_packet_type - STP packets that an STM driver sends + */ +enum stp_packet_type { + STP_PACKET_DATA = 0, + STP_PACKET_FLAG, + STP_PACKET_USER, + STP_PACKET_MERR, + STP_PACKET_GERR, + STP_PACKET_TRIG, + STP_PACKET_XSYNC, +}; + +/** + * enum stp_packet_flags - STP packet modifiers + */ +enum stp_packet_flags { + STP_PACKET_MARKED = 0x1, + STP_PACKET_TIMESTAMPED = 0x2, +}; + +struct stp_policy; + +struct stm_device; + +/** + * struct stm_data - STM device description and callbacks + * @name: device name + * @stm: internal structure, only used by stm class code + * @sw_start: first STP master available to software + * @sw_end: last STP master available to software + * @sw_nchannels: number of STP channels per master + * @sw_mmiosz: size of one channel's IO space, for mmap, optional + * @packet: callback that sends an STP packet + * @mmio_addr: mmap callback, optional + * @link: called when a new stm_source gets linked to us, optional + * @unlink: likewise for unlinking, again optional + * @set_options: set device-specific options on a channel + * + * Fill out this structure before calling stm_register_device() to create + * an STM device and stm_unregister_device() to destroy it. It will also be + * passed back to @packet(), @mmio_addr(), @link(), @unlink() and @set_options() + * callbacks. + * + * Normally, an STM device will have a range of masters available to software + * and the rest being statically assigned to various hardware trace sources. + * The former is defined by the the range [@sw_start..@sw_end] of the device + * description. That is, the lowest master that can be allocated to software + * writers is @sw_start and data from this writer will appear is @sw_start + * master in the STP stream. + */ +struct stm_data { + const char *name; + struct stm_device *stm; + unsigned int sw_start; + unsigned int sw_end; + unsigned int sw_nchannels; + unsigned int sw_mmiosz; + ssize_t (*packet)(struct stm_data *, unsigned int, + unsigned int, unsigned int, + unsigned int, unsigned int, + const unsigned char *); + phys_addr_t (*mmio_addr)(struct stm_data *, unsigned int, + unsigned int, unsigned int); + int (*link)(struct stm_data *, unsigned int, + unsigned int); + void (*unlink)(struct stm_data *, unsigned int, + unsigned int); + long (*set_options)(struct stm_data *, unsigned int, + unsigned int, unsigned int, + unsigned long); +}; + +int stm_register_device(struct device *parent, struct stm_data *stm_data, + struct module *owner); +void stm_unregister_device(struct stm_data *stm_data); + +struct stm_source_device; + +/** + * struct stm_source_data - STM source device description and callbacks + * @name: device name, will be used for policy lookup + * @src: internal structure, only used by stm class code + * @nr_chans: number of channels to allocate + * @link: called when this source gets linked to an STM device + * @unlink: called when this source is about to get unlinked from its STM + * + * Fill in this structure before calling stm_source_register_device() to + * register a source device. Also pass it to unregister and write calls. + */ +struct stm_source_data { + const char *name; + struct stm_source_device *src; + unsigned int percpu; + unsigned int nr_chans; + int (*link)(struct stm_source_data *data); + void (*unlink)(struct stm_source_data *data); +}; + +int stm_source_register_device(struct device *parent, + struct stm_source_data *data); +void stm_source_unregister_device(struct stm_source_data *data); + +int stm_source_write(struct stm_source_data *data, unsigned int chan, + const char *buf, size_t count); + +#endif /* _STM_H_ */ diff --git a/include/uapi/linux/stm.h b/include/uapi/linux/stm.h new file mode 100644 index 000000000000..626a8d3f63b5 --- /dev/null +++ b/include/uapi/linux/stm.h @@ -0,0 +1,50 @@ +/* + * System Trace Module (STM) userspace interfaces + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * STM class implements generic infrastructure for System Trace Module devices + * as defined in MIPI STPv2 specification. + */ + +#ifndef _UAPI_LINUX_STM_H +#define _UAPI_LINUX_STM_H + +#include + +/** + * struct stp_policy_id - identification for the STP policy + * @size: size of the structure including real id[] length + * @master: assigned master + * @channel: first assigned channel + * @width: number of requested channels + * @id: identification string + * + * User must calculate the total size of the structure and put it into + * @size field, fill out the @id and desired @width. In return, kernel + * fills out @master, @channel and @width. + */ +struct stp_policy_id { + __u32 size; + __u16 master; + __u16 channel; + __u16 width; + /* padding */ + __u16 __reserved_0; + __u32 __reserved_1; + char id[0]; +}; + +#define STP_POLICY_ID_SET _IOWR('%', 0, struct stp_policy_id) +#define STP_POLICY_ID_GET _IOR('%', 1, struct stp_policy_id) +#define STP_SET_OPTIONS _IOW('%', 2, __u64) + +#endif /* _UAPI_LINUX_STM_H */ -- cgit v1.2.3 From 1d27ff5aba2d36b152542e9d62158eb6584bcbd3 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 7 Oct 2015 09:26:39 -0600 Subject: coresight: fixing typographical error Tracing gets enabled _for_ a source rather than _from_ a source. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- include/linux/coresight.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/coresight.h b/include/linux/coresight.h index c69e1b932809..a7cabfa23b55 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -207,7 +207,7 @@ struct coresight_ops_link { * Operations available for sources. * @trace_id: returns the value of the component's trace ID as known to the HW. - * @enable: enables tracing from a source. + * @enable: enables tracing for a source. * @disable: disables tracing for a source. */ struct coresight_ops_source { -- cgit v1.2.3 From 6a8c3be7ec8eb3c1197766f9245e0d65a4e5aff8 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Wed, 7 Oct 2015 16:36:28 +0100 Subject: add FPGA manager core API to support programming FPGA's. The following functions are exported as GPL: * fpga_mgr_buf_load Load fpga from image in buffer * fpga_mgr_firmware_load Request firmware and load it to the FPGA. * fpga_mgr_register * fpga_mgr_unregister FPGA device drivers can be added by calling fpga_mgr_register() to register a set of fpga_manager_ops to do device specific stuff. * of_fpga_mgr_get * fpga_mgr_put Get/put a reference to a fpga manager. The following sysfs files are created: * /sys/class/fpga_manager//name Name of low level driver. * /sys/class/fpga_manager//state State of fpga manager Signed-off-by: Alan Tull Acked-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/fpga/Kconfig | 14 ++ drivers/fpga/Makefile | 8 + drivers/fpga/fpga-mgr.c | 382 ++++++++++++++++++++++++++++++++++++++++++ include/linux/fpga/fpga-mgr.h | 127 ++++++++++++++ 6 files changed, 534 insertions(+) create mode 100644 drivers/fpga/Kconfig create mode 100644 drivers/fpga/Makefile create mode 100644 drivers/fpga/fpga-mgr.c create mode 100644 include/linux/fpga/fpga-mgr.h (limited to 'include') diff --git a/drivers/Kconfig b/drivers/Kconfig index 709488ae882e..5a89e409ad18 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -192,4 +192,6 @@ source "drivers/hwtracing/stm/Kconfig" source "drivers/hwtracing/intel_th/Kconfig" +source "drivers/fpga/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index e63542dd7010..7064bf476c2a 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -169,3 +169,4 @@ obj-y += hwtracing/intel_th/ obj-$(CONFIG_STM) += hwtracing/stm/ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_NVMEM) += nvmem/ +obj-$(CONFIG_FPGA) += fpga/ diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig new file mode 100644 index 000000000000..f1f1f6df54d3 --- /dev/null +++ b/drivers/fpga/Kconfig @@ -0,0 +1,14 @@ +# +# FPGA framework configuration +# + +menu "FPGA Configuration Support" + +config FPGA + tristate "FPGA Configuration Framework" + help + Say Y here if you want support for configuring FPGAs from the + kernel. The FPGA framework adds a FPGA manager class and FPGA + manager drivers. + +endmenu diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile new file mode 100644 index 000000000000..3313c5266795 --- /dev/null +++ b/drivers/fpga/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the fpga framework and fpga manager drivers. +# + +# Core FPGA Manager Framework +obj-$(CONFIG_FPGA) += fpga-mgr.o + +# FPGA Manager Drivers diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c new file mode 100644 index 000000000000..25261636687c --- /dev/null +++ b/drivers/fpga/fpga-mgr.c @@ -0,0 +1,382 @@ +/* + * FPGA Manager Core + * + * Copyright (C) 2013-2015 Altera Corporation + * + * With code from the mailing list: + * Copyright (C) 2013 Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_IDA(fpga_mgr_ida); +static struct class *fpga_mgr_class; + +/** + * fpga_mgr_buf_load - load fpga from image in buffer + * @mgr: fpga manager + * @flags: flags setting fpga confuration modes + * @buf: buffer contain fpga image + * @count: byte count of buf + * + * Step the low level fpga manager through the device-specific steps of getting + * an FPGA ready to be configured, writing the image to it, then doing whatever + * post-configuration steps necessary. + * + * Return: 0 on success, negative error code otherwise. + */ +int fpga_mgr_buf_load(struct fpga_manager *mgr, u32 flags, const char *buf, + size_t count) +{ + struct device *dev = &mgr->dev; + int ret; + + if (!mgr) + return -ENODEV; + + /* + * Call the low level driver's write_init function. This will do the + * device-specific things to get the FPGA into the state where it is + * ready to receive an FPGA image. + */ + mgr->state = FPGA_MGR_STATE_WRITE_INIT; + ret = mgr->mops->write_init(mgr, flags, buf, count); + if (ret) { + dev_err(dev, "Error preparing FPGA for writing\n"); + mgr->state = FPGA_MGR_STATE_WRITE_INIT_ERR; + return ret; + } + + /* + * Write the FPGA image to the FPGA. + */ + mgr->state = FPGA_MGR_STATE_WRITE; + ret = mgr->mops->write(mgr, buf, count); + if (ret) { + dev_err(dev, "Error while writing image data to FPGA\n"); + mgr->state = FPGA_MGR_STATE_WRITE_ERR; + return ret; + } + + /* + * After all the FPGA image has been written, do the device specific + * steps to finish and set the FPGA into operating mode. + */ + mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE; + ret = mgr->mops->write_complete(mgr, flags); + if (ret) { + dev_err(dev, "Error after writing image data to FPGA\n"); + mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR; + return ret; + } + mgr->state = FPGA_MGR_STATE_OPERATING; + + return 0; +} +EXPORT_SYMBOL_GPL(fpga_mgr_buf_load); + +/** + * fpga_mgr_firmware_load - request firmware and load to fpga + * @mgr: fpga manager + * @flags: flags setting fpga confuration modes + * @image_name: name of image file on the firmware search path + * + * Request an FPGA image using the firmware class, then write out to the FPGA. + * Update the state before each step to provide info on what step failed if + * there is a failure. + * + * Return: 0 on success, negative error code otherwise. + */ +int fpga_mgr_firmware_load(struct fpga_manager *mgr, u32 flags, + const char *image_name) +{ + struct device *dev = &mgr->dev; + const struct firmware *fw; + int ret; + + if (!mgr) + return -ENODEV; + + dev_info(dev, "writing %s to %s\n", image_name, mgr->name); + + mgr->state = FPGA_MGR_STATE_FIRMWARE_REQ; + + ret = request_firmware(&fw, image_name, dev); + if (ret) { + mgr->state = FPGA_MGR_STATE_FIRMWARE_REQ_ERR; + dev_err(dev, "Error requesting firmware %s\n", image_name); + return ret; + } + + ret = fpga_mgr_buf_load(mgr, flags, fw->data, fw->size); + if (ret) + return ret; + + release_firmware(fw); + + return 0; +} +EXPORT_SYMBOL_GPL(fpga_mgr_firmware_load); + +static const char * const state_str[] = { + [FPGA_MGR_STATE_UNKNOWN] = "unknown", + [FPGA_MGR_STATE_POWER_OFF] = "power off", + [FPGA_MGR_STATE_POWER_UP] = "power up", + [FPGA_MGR_STATE_RESET] = "reset", + + /* requesting FPGA image from firmware */ + [FPGA_MGR_STATE_FIRMWARE_REQ] = "firmware request", + [FPGA_MGR_STATE_FIRMWARE_REQ_ERR] = "firmware request error", + + /* Preparing FPGA to receive image */ + [FPGA_MGR_STATE_WRITE_INIT] = "write init", + [FPGA_MGR_STATE_WRITE_INIT_ERR] = "write init error", + + /* Writing image to FPGA */ + [FPGA_MGR_STATE_WRITE] = "write", + [FPGA_MGR_STATE_WRITE_ERR] = "write error", + + /* Finishing configuration after image has been written */ + [FPGA_MGR_STATE_WRITE_COMPLETE] = "write complete", + [FPGA_MGR_STATE_WRITE_COMPLETE_ERR] = "write complete error", + + /* FPGA reports to be in normal operating mode */ + [FPGA_MGR_STATE_OPERATING] = "operating", +}; + +static ssize_t name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fpga_manager *mgr = to_fpga_manager(dev); + + return sprintf(buf, "%s\n", mgr->name); +} + +static ssize_t state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fpga_manager *mgr = to_fpga_manager(dev); + + return sprintf(buf, "%s\n", state_str[mgr->state]); +} + +static DEVICE_ATTR_RO(name); +static DEVICE_ATTR_RO(state); + +static struct attribute *fpga_mgr_attrs[] = { + &dev_attr_name.attr, + &dev_attr_state.attr, + NULL, +}; +ATTRIBUTE_GROUPS(fpga_mgr); + +static int fpga_mgr_of_node_match(struct device *dev, const void *data) +{ + return dev->of_node == data; +} + +/** + * of_fpga_mgr_get - get an exclusive reference to a fpga mgr + * @node: device node + * + * Given a device node, get an exclusive reference to a fpga mgr. + * + * Return: fpga manager struct or IS_ERR() condition containing error code. + */ +struct fpga_manager *of_fpga_mgr_get(struct device_node *node) +{ + struct fpga_manager *mgr; + struct device *dev; + + if (!node) + return ERR_PTR(-EINVAL); + + dev = class_find_device(fpga_mgr_class, NULL, node, + fpga_mgr_of_node_match); + if (!dev) + return ERR_PTR(-ENODEV); + + mgr = to_fpga_manager(dev); + put_device(dev); + if (!mgr) + return ERR_PTR(-ENODEV); + + /* Get exclusive use of fpga manager */ + if (!mutex_trylock(&mgr->ref_mutex)) + return ERR_PTR(-EBUSY); + + if (!try_module_get(THIS_MODULE)) { + mutex_unlock(&mgr->ref_mutex); + return ERR_PTR(-ENODEV); + } + + return mgr; +} +EXPORT_SYMBOL_GPL(of_fpga_mgr_get); + +/** + * fpga_mgr_put - release a reference to a fpga manager + * @mgr: fpga manager structure + */ +void fpga_mgr_put(struct fpga_manager *mgr) +{ + if (mgr) { + module_put(THIS_MODULE); + mutex_unlock(&mgr->ref_mutex); + } +} +EXPORT_SYMBOL_GPL(fpga_mgr_put); + +/** + * fpga_mgr_register - register a low level fpga manager driver + * @dev: fpga manager device from pdev + * @name: fpga manager name + * @mops: pointer to structure of fpga manager ops + * @priv: fpga manager private data + * + * Return: 0 on success, negative error code otherwise. + */ +int fpga_mgr_register(struct device *dev, const char *name, + const struct fpga_manager_ops *mops, + void *priv) +{ + struct fpga_manager *mgr; + const char *dt_label; + int id, ret; + + if (!mops || !mops->write_init || !mops->write || + !mops->write_complete || !mops->state) { + dev_err(dev, "Attempt to register without fpga_manager_ops\n"); + return -EINVAL; + } + + if (!name || !strlen(name)) { + dev_err(dev, "Attempt to register with no name!\n"); + return -EINVAL; + } + + mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); + if (!mgr) + return -ENOMEM; + + id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL); + if (id < 0) { + ret = id; + goto error_kfree; + } + + mutex_init(&mgr->ref_mutex); + + mgr->name = name; + mgr->mops = mops; + mgr->priv = priv; + + /* + * Initialize framework state by requesting low level driver read state + * from device. FPGA may be in reset mode or may have been programmed + * by bootloader or EEPROM. + */ + mgr->state = mgr->mops->state(mgr); + + device_initialize(&mgr->dev); + mgr->dev.class = fpga_mgr_class; + mgr->dev.parent = dev; + mgr->dev.of_node = dev->of_node; + mgr->dev.id = id; + dev_set_drvdata(dev, mgr); + + dt_label = of_get_property(mgr->dev.of_node, "label", NULL); + if (dt_label) + ret = dev_set_name(&mgr->dev, "%s", dt_label); + else + ret = dev_set_name(&mgr->dev, "fpga%d", id); + + ret = device_add(&mgr->dev); + if (ret) + goto error_device; + + dev_info(&mgr->dev, "%s registered\n", mgr->name); + + return 0; + +error_device: + ida_simple_remove(&fpga_mgr_ida, id); +error_kfree: + kfree(mgr); + + return ret; +} +EXPORT_SYMBOL_GPL(fpga_mgr_register); + +/** + * fpga_mgr_unregister - unregister a low level fpga manager driver + * @dev: fpga manager device from pdev + */ +void fpga_mgr_unregister(struct device *dev) +{ + struct fpga_manager *mgr = dev_get_drvdata(dev); + + dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name); + + /* + * If the low level driver provides a method for putting fpga into + * a desired state upon unregister, do it. + */ + if (mgr->mops->fpga_remove) + mgr->mops->fpga_remove(mgr); + + device_unregister(&mgr->dev); +} +EXPORT_SYMBOL_GPL(fpga_mgr_unregister); + +static void fpga_mgr_dev_release(struct device *dev) +{ + struct fpga_manager *mgr = to_fpga_manager(dev); + + ida_simple_remove(&fpga_mgr_ida, mgr->dev.id); + kfree(mgr); +} + +static int __init fpga_mgr_class_init(void) +{ + pr_info("FPGA manager framework\n"); + + fpga_mgr_class = class_create(THIS_MODULE, "fpga_manager"); + if (IS_ERR(fpga_mgr_class)) + return PTR_ERR(fpga_mgr_class); + + fpga_mgr_class->dev_groups = fpga_mgr_groups; + fpga_mgr_class->dev_release = fpga_mgr_dev_release; + + return 0; +} + +static void __exit fpga_mgr_class_exit(void) +{ + class_destroy(fpga_mgr_class); + ida_destroy(&fpga_mgr_ida); +} + +MODULE_AUTHOR("Alan Tull "); +MODULE_DESCRIPTION("FPGA manager framework"); +MODULE_LICENSE("GPL v2"); + +subsys_initcall(fpga_mgr_class_init); +module_exit(fpga_mgr_class_exit); diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h new file mode 100644 index 000000000000..0940bf45e2f2 --- /dev/null +++ b/include/linux/fpga/fpga-mgr.h @@ -0,0 +1,127 @@ +/* + * FPGA Framework + * + * Copyright (C) 2013-2015 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +#include +#include + +#ifndef _LINUX_FPGA_MGR_H +#define _LINUX_FPGA_MGR_H + +struct fpga_manager; + +/** + * enum fpga_mgr_states - fpga framework states + * @FPGA_MGR_STATE_UNKNOWN: can't determine state + * @FPGA_MGR_STATE_POWER_OFF: FPGA power is off + * @FPGA_MGR_STATE_POWER_UP: FPGA reports power is up + * @FPGA_MGR_STATE_RESET: FPGA in reset state + * @FPGA_MGR_STATE_FIRMWARE_REQ: firmware request in progress + * @FPGA_MGR_STATE_FIRMWARE_REQ_ERR: firmware request failed + * @FPGA_MGR_STATE_WRITE_INIT: preparing FPGA for programming + * @FPGA_MGR_STATE_WRITE_INIT_ERR: Error during WRITE_INIT stage + * @FPGA_MGR_STATE_WRITE: writing image to FPGA + * @FPGA_MGR_STATE_WRITE_ERR: Error while writing FPGA + * @FPGA_MGR_STATE_WRITE_COMPLETE: Doing post programming steps + * @FPGA_MGR_STATE_WRITE_COMPLETE_ERR: Error during WRITE_COMPLETE + * @FPGA_MGR_STATE_OPERATING: FPGA is programmed and operating + */ +enum fpga_mgr_states { + /* default FPGA states */ + FPGA_MGR_STATE_UNKNOWN, + FPGA_MGR_STATE_POWER_OFF, + FPGA_MGR_STATE_POWER_UP, + FPGA_MGR_STATE_RESET, + + /* getting an image for loading */ + FPGA_MGR_STATE_FIRMWARE_REQ, + FPGA_MGR_STATE_FIRMWARE_REQ_ERR, + + /* write sequence: init, write, complete */ + FPGA_MGR_STATE_WRITE_INIT, + FPGA_MGR_STATE_WRITE_INIT_ERR, + FPGA_MGR_STATE_WRITE, + FPGA_MGR_STATE_WRITE_ERR, + FPGA_MGR_STATE_WRITE_COMPLETE, + FPGA_MGR_STATE_WRITE_COMPLETE_ERR, + + /* fpga is programmed and operating */ + FPGA_MGR_STATE_OPERATING, +}; + +/* + * FPGA Manager flags + * FPGA_MGR_PARTIAL_RECONFIG: do partial reconfiguration if supported + */ +#define FPGA_MGR_PARTIAL_RECONFIG BIT(0) + +/** + * struct fpga_manager_ops - ops for low level fpga manager drivers + * @state: returns an enum value of the FPGA's state + * @write_init: prepare the FPGA to receive confuration data + * @write: write count bytes of configuration data to the FPGA + * @write_complete: set FPGA to operating state after writing is done + * @fpga_remove: optional: Set FPGA into a specific state during driver remove + * + * fpga_manager_ops are the low level functions implemented by a specific + * fpga manager driver. The optional ones are tested for NULL before being + * called, so leaving them out is fine. + */ +struct fpga_manager_ops { + enum fpga_mgr_states (*state)(struct fpga_manager *mgr); + int (*write_init)(struct fpga_manager *mgr, u32 flags, + const char *buf, size_t count); + int (*write)(struct fpga_manager *mgr, const char *buf, size_t count); + int (*write_complete)(struct fpga_manager *mgr, u32 flags); + void (*fpga_remove)(struct fpga_manager *mgr); +}; + +/** + * struct fpga_manager - fpga manager structure + * @name: name of low level fpga manager + * @dev: fpga manager device + * @ref_mutex: only allows one reference to fpga manager + * @state: state of fpga manager + * @mops: pointer to struct of fpga manager ops + * @priv: low level driver private date + */ +struct fpga_manager { + const char *name; + struct device dev; + struct mutex ref_mutex; + enum fpga_mgr_states state; + const struct fpga_manager_ops *mops; + void *priv; +}; + +#define to_fpga_manager(d) container_of(d, struct fpga_manager, dev) + +int fpga_mgr_buf_load(struct fpga_manager *mgr, u32 flags, + const char *buf, size_t count); + +int fpga_mgr_firmware_load(struct fpga_manager *mgr, u32 flags, + const char *image_name); + +struct fpga_manager *of_fpga_mgr_get(struct device_node *node); + +void fpga_mgr_put(struct fpga_manager *mgr); + +int fpga_mgr_register(struct device *dev, const char *name, + const struct fpga_manager_ops *mops, void *priv); + +void fpga_mgr_unregister(struct device *dev); + +#endif /*_LINUX_FPGA_MGR_H */ -- cgit v1.2.3 From 11eecf910bd81d36425020743b2df73651c5b466 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Sat, 3 Oct 2015 14:15:13 +0900 Subject: extcon: Modify the id and name of external connector This patch modifies the id and name of external connector with the additional prefix to clarify both attribute and meaning of external connector as following: - EXTCON_CHG_* mean the charger connector. - EXTCON_JACK_* mean the jack connector. - EXTCON_DISP_* mean the display port connector. Following table show the new name of external connector with old name: -------------------------------------------------- Old extcon name | New extcon name | -------------------------------------------------- EXTCON_TA | EXTCON_CHG_USB_DCP | EXTCON_CHARGE_DOWNSTREAM| EXTCON_CHG_USB_CDP | EXTCON_FAST_CHARGER | EXTCON_CHG_USB_FAST | EXTCON_SLOW_CHARGER | EXTCON_CHG_USB_SLOW | -------------------------------------------------- EXTCON_MICROPHONE | EXTCON_JACK_MICROPHONE | EXTCON_HEADPHONE | EXTCON_JACK_HEADPHONE | EXTCON_LINE_IN | EXTCON_JACK_LINE_IN | EXTCON_LINE_OUT | EXTCON_JACK_LINE_OUT | EXTCON_VIDEO_IN | EXTCON_JACK_VIDEO_IN | EXTCON_VIDEO_OUT | EXTCON_JACK_VIDEO_OUT | EXTCON_SPDIF_IN | EXTCON_JACK_SPDIF_IN | EXTCON_SPDIF_OUT | EXTCON_JACK_SPDIF_OUT | -------------------------------------------------- EXTCON_HMDI | EXTCON_DISP_HDMI | EXTCON_MHL | EXTCON_DISP_MHL | EXTCON_DVI | EXTCON_DISP_DVI | EXTCON_VGA | EXTCON_DISP_VGA | -------------------------------------------------- And, when altering the name of USB charger connector, EXTCON refers to the "Battery Charging v1.2 Spec and Adopters Agreement"[1] to use the standard name of USB charging port as following. Following name of USB charging port are already used in power_supply subsystem. We chan check it on patch[2]. - EXTCON_CHG_USB_SDP /* Standard Downstream Port */ - EXTCON_CHG_USB_DCP /* Dedicated Charging Port */ - EXTCON_CHG_USB_CDP /* Charging Downstream Port */ - EXTCON_CHG_USB_ACA /* Accessory Charger Adapter */ [1] www.usb.org/developers/docs/devclass_docs/BCv1.2_070312.zip [2] commit 85efc8a18ced ("power_supply: Add types for USB chargers") Signed-off-by: Chanwoo Choi [ckeepax: For the Arizona changes] Acked-by: Charles Keepax Reviewed-by: Roger Quadros --- drivers/extcon/extcon-arizona.c | 18 ++++++------ drivers/extcon/extcon-axp288.c | 12 ++++---- drivers/extcon/extcon-max14577.c | 17 +++++------ drivers/extcon/extcon-max77693.c | 32 +++++++++++---------- drivers/extcon/extcon-max77843.c | 27 +++++++++-------- drivers/extcon/extcon-max8997.c | 21 +++++++------- drivers/extcon/extcon-rt8973a.c | 4 +-- drivers/extcon/extcon-sm5502.c | 4 +-- drivers/extcon/extcon.c | 61 ++++++++++++++++++++------------------- include/linux/extcon.h | 62 +++++++++++++++++++++++----------------- 10 files changed, 139 insertions(+), 119 deletions(-) (limited to 'include') diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index a1ab0a56b798..e4890dd4fefd 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -137,9 +137,9 @@ static const int arizona_micd_levels[] = { static const unsigned int arizona_cable[] = { EXTCON_MECHANICAL, - EXTCON_MICROPHONE, - EXTCON_HEADPHONE, - EXTCON_LINE_OUT, + EXTCON_JACK_MICROPHONE, + EXTCON_JACK_HEADPHONE, + EXTCON_JACK_LINE_OUT, EXTCON_NONE, }; @@ -600,7 +600,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) struct arizona_extcon_info *info = data; struct arizona *arizona = info->arizona; int id_gpio = arizona->pdata.hpdet_id_gpio; - unsigned int report = EXTCON_HEADPHONE; + unsigned int report = EXTCON_JACK_HEADPHONE; int ret, reading; bool mic = false; @@ -645,9 +645,9 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) /* Report high impedence cables as line outputs */ if (reading >= 5000) - report = EXTCON_LINE_OUT; + report = EXTCON_JACK_LINE_OUT; else - report = EXTCON_HEADPHONE; + report = EXTCON_JACK_HEADPHONE; ret = extcon_set_cable_state_(info->edev, report, true); if (ret != 0) @@ -732,7 +732,7 @@ err: ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); /* Just report headphone */ - ret = extcon_set_cable_state_(info->edev, EXTCON_HEADPHONE, true); + ret = extcon_set_cable_state_(info->edev, EXTCON_JACK_HEADPHONE, true); if (ret != 0) dev_err(arizona->dev, "Failed to report headphone: %d\n", ret); @@ -789,7 +789,7 @@ err: ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); /* Just report headphone */ - ret = extcon_set_cable_state_(info->edev, EXTCON_HEADPHONE, true); + ret = extcon_set_cable_state_(info->edev, EXTCON_JACK_HEADPHONE, true); if (ret != 0) dev_err(arizona->dev, "Failed to report headphone: %d\n", ret); @@ -915,7 +915,7 @@ static void arizona_micd_detect(struct work_struct *work) arizona_identify_headphone(info); ret = extcon_set_cable_state_(info->edev, - EXTCON_MICROPHONE, true); + EXTCON_JACK_MICROPHONE, true); if (ret != 0) dev_err(arizona->dev, "Headset report failed: %d\n", ret); diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index 9668d6a94e38..fd55c2f2080a 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -102,9 +102,9 @@ enum axp288_extcon_irq { }; static const unsigned int axp288_extcon_cables[] = { - EXTCON_SLOW_CHARGER, - EXTCON_CHARGE_DOWNSTREAM, - EXTCON_FAST_CHARGER, + EXTCON_CHG_USB_SDP, + EXTCON_CHG_USB_CDP, + EXTCON_CHG_USB_DCP, EXTCON_NONE, }; @@ -192,18 +192,18 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) dev_dbg(info->dev, "sdp cable is connecetd\n"); notify_otg = true; notify_charger = true; - cable = EXTCON_SLOW_CHARGER; + cable = EXTCON_CHG_USB_SDP; break; case DET_STAT_CDP: dev_dbg(info->dev, "cdp cable is connecetd\n"); notify_otg = true; notify_charger = true; - cable = EXTCON_CHARGE_DOWNSTREAM; + cable = EXTCON_CHG_USB_CDP; break; case DET_STAT_DCP: dev_dbg(info->dev, "dcp cable is connecetd\n"); notify_charger = true; - cable = EXTCON_FAST_CHARGER; + cable = EXTCON_CHG_USB_DCP; break; default: dev_warn(info->dev, diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c index df0659d98e5a..601dbd996487 100644 --- a/drivers/extcon/extcon-max14577.c +++ b/drivers/extcon/extcon-max14577.c @@ -150,10 +150,10 @@ enum max14577_muic_acc_type { static const unsigned int max14577_extcon_cable[] = { EXTCON_USB, - EXTCON_TA, - EXTCON_FAST_CHARGER, - EXTCON_SLOW_CHARGER, - EXTCON_CHARGE_DOWNSTREAM, + EXTCON_CHG_USB_DCP, + EXTCON_CHG_USB_FAST, + EXTCON_CHG_USB_SLOW, + EXTCON_CHG_USB_CDP, EXTCON_JIG, EXTCON_NONE, }; @@ -456,18 +456,19 @@ static int max14577_muic_chg_handler(struct max14577_muic_info *info) extcon_set_cable_state_(info->edev, EXTCON_USB, attached); break; case MAX14577_CHARGER_TYPE_DEDICATED_CHG: - extcon_set_cable_state_(info->edev, EXTCON_TA, attached); + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + attached); break; case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT: - extcon_set_cable_state_(info->edev, EXTCON_CHARGE_DOWNSTREAM, + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP, attached); break; case MAX14577_CHARGER_TYPE_SPECIAL_500MA: - extcon_set_cable_state_(info->edev, EXTCON_SLOW_CHARGER, + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW, attached); break; case MAX14577_CHARGER_TYPE_SPECIAL_1A: - extcon_set_cable_state_(info->edev, EXTCON_FAST_CHARGER, + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST, attached); break; case MAX14577_CHARGER_TYPE_NONE: diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index 35b9e118b2fb..44c499e1beee 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -204,11 +204,11 @@ enum max77693_muic_acc_type { static const unsigned int max77693_extcon_cable[] = { EXTCON_USB, EXTCON_USB_HOST, - EXTCON_TA, - EXTCON_FAST_CHARGER, - EXTCON_SLOW_CHARGER, - EXTCON_CHARGE_DOWNSTREAM, - EXTCON_MHL, + EXTCON_CHG_USB_DCP, + EXTCON_CHG_USB_FAST, + EXTCON_CHG_USB_SLOW, + EXTCON_CHG_USB_CDP, + EXTCON_DISP_MHL, EXTCON_JIG, EXTCON_DOCK, EXTCON_NONE, @@ -505,7 +505,7 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info, return ret; extcon_set_cable_state_(info->edev, EXTCON_DOCK, attached); - extcon_set_cable_state_(info->edev, EXTCON_MHL, attached); + extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached); goto out; case MAX77693_MUIC_ADC_AUDIO_MODE_REMOTE: /* Dock-Desk */ dock_id = EXTCON_DOCK; @@ -605,7 +605,7 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info) case MAX77693_MUIC_GND_MHL: case MAX77693_MUIC_GND_MHL_VB: /* MHL or MHL with USB/TA cable */ - extcon_set_cable_state_(info->edev, EXTCON_MHL, attached); + extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached); break; default: dev_err(info->dev, "failed to detect %s cable of gnd type\n", @@ -801,10 +801,11 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) * - Support charging through micro-usb port without * data connection */ - extcon_set_cable_state_(info->edev, EXTCON_TA, attached); + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + attached); if (!cable_attached) - extcon_set_cable_state_(info->edev, EXTCON_MHL, - cable_attached); + extcon_set_cable_state_(info->edev, + EXTCON_DISP_MHL, cable_attached); break; } @@ -862,7 +863,7 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) extcon_set_cable_state_(info->edev, EXTCON_DOCK, attached); - extcon_set_cable_state_(info->edev, EXTCON_MHL, + extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached); break; } @@ -901,20 +902,21 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) break; case MAX77693_CHARGER_TYPE_DEDICATED_CHG: /* Only TA cable */ - extcon_set_cable_state_(info->edev, EXTCON_TA, attached); + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + attached); break; } break; case MAX77693_CHARGER_TYPE_DOWNSTREAM_PORT: - extcon_set_cable_state_(info->edev, EXTCON_CHARGE_DOWNSTREAM, + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP, attached); break; case MAX77693_CHARGER_TYPE_APPLE_500MA: - extcon_set_cable_state_(info->edev, EXTCON_SLOW_CHARGER, + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW, attached); break; case MAX77693_CHARGER_TYPE_APPLE_1A_2A: - extcon_set_cable_state_(info->edev, EXTCON_FAST_CHARGER, + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST, attached); break; case MAX77693_CHARGER_TYPE_DEAD_BATTERY: diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c index fdd928542c19..9f9ea334399c 100644 --- a/drivers/extcon/extcon-max77843.c +++ b/drivers/extcon/extcon-max77843.c @@ -122,11 +122,11 @@ enum max77843_muic_charger_type { static const unsigned int max77843_extcon_cable[] = { EXTCON_USB, EXTCON_USB_HOST, - EXTCON_TA, - EXTCON_CHARGE_DOWNSTREAM, - EXTCON_FAST_CHARGER, - EXTCON_SLOW_CHARGER, - EXTCON_MHL, + EXTCON_CHG_USB_DCP, + EXTCON_CHG_USB_CDP, + EXTCON_CHG_USB_FAST, + EXTCON_CHG_USB_SLOW, + EXTCON_DISP_MHL, EXTCON_JIG, EXTCON_NONE, }; @@ -355,7 +355,7 @@ static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_MHL, attached); + extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached); break; default: dev_err(info->dev, "failed to detect %s accessory(gnd:0x%x)\n", @@ -494,7 +494,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_CHARGE_DOWNSTREAM, + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP, attached); break; case MAX77843_MUIC_CHG_DEDICATED: @@ -504,7 +504,8 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_TA, attached); + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + attached); break; case MAX77843_MUIC_CHG_SPECIAL_500MA: ret = max77843_muic_set_path(info, @@ -513,7 +514,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_SLOW_CHARGER, + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW, attached); break; case MAX77843_MUIC_CHG_SPECIAL_1A: @@ -523,7 +524,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) if (ret < 0) return ret; - extcon_set_cable_state_(info->edev, EXTCON_FAST_CHARGER, + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST, attached); break; case MAX77843_MUIC_CHG_GND: @@ -532,9 +533,11 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) /* Charger cable on MHL accessory is attach or detach */ if (gnd_type == MAX77843_MUIC_GND_MHL_VB) - extcon_set_cable_state_(info->edev, EXTCON_TA, true); + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + true); else if (gnd_type == MAX77843_MUIC_GND_MHL) - extcon_set_cable_state_(info->edev, EXTCON_TA, false); + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + false); break; case MAX77843_MUIC_CHG_NONE: break; diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index 7b1ef200b121..b2b13b3dce14 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -148,11 +148,11 @@ struct max8997_muic_info { static const unsigned int max8997_extcon_cable[] = { EXTCON_USB, EXTCON_USB_HOST, - EXTCON_TA, - EXTCON_FAST_CHARGER, - EXTCON_SLOW_CHARGER, - EXTCON_CHARGE_DOWNSTREAM, - EXTCON_MHL, + EXTCON_CHG_USB_DCP, + EXTCON_CHG_USB_FAST, + EXTCON_CHG_USB_SLOW, + EXTCON_CHG_USB_CDP, + EXTCON_DISP_MHL, EXTCON_DOCK, EXTCON_JIG, EXTCON_NONE, @@ -403,7 +403,7 @@ static int max8997_muic_adc_handler(struct max8997_muic_info *info) return ret; break; case MAX8997_MUIC_ADC_MHL: - extcon_set_cable_state_(info->edev, EXTCON_MHL, attached); + extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached); break; case MAX8997_MUIC_ADC_FACTORY_MODE_USB_OFF: case MAX8997_MUIC_ADC_FACTORY_MODE_USB_ON: @@ -486,18 +486,19 @@ static int max8997_muic_chg_handler(struct max8997_muic_info *info) } break; case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT: - extcon_set_cable_state_(info->edev, EXTCON_CHARGE_DOWNSTREAM, + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP, attached); break; case MAX8997_CHARGER_TYPE_DEDICATED_CHG: - extcon_set_cable_state_(info->edev, EXTCON_TA, attached); + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP, + attached); break; case MAX8997_CHARGER_TYPE_500MA: - extcon_set_cable_state_(info->edev, EXTCON_SLOW_CHARGER, + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW, attached); break; case MAX8997_CHARGER_TYPE_1A: - extcon_set_cable_state_(info->edev, EXTCON_FAST_CHARGER, + extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST, attached); break; default: diff --git a/drivers/extcon/extcon-rt8973a.c b/drivers/extcon/extcon-rt8973a.c index 1bc3737ea01c..36bf1d63791c 100644 --- a/drivers/extcon/extcon-rt8973a.c +++ b/drivers/extcon/extcon-rt8973a.c @@ -93,7 +93,7 @@ static struct reg_data rt8973a_reg_data[] = { static const unsigned int rt8973a_extcon_cable[] = { EXTCON_USB, EXTCON_USB_HOST, - EXTCON_TA, + EXTCON_CHG_USB_DCP, EXTCON_JIG, EXTCON_NONE, }; @@ -333,7 +333,7 @@ static int rt8973a_muic_cable_handler(struct rt8973a_muic_info *info, con_sw = DM_DP_SWITCH_USB; break; case RT8973A_MUIC_ADC_TA: - id = EXTCON_TA; + id = EXTCON_CHG_USB_DCP; con_sw = DM_DP_SWITCH_OPEN; break; case RT8973A_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB: diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c index 2945091bfd0e..7aac3cc7efd7 100644 --- a/drivers/extcon/extcon-sm5502.c +++ b/drivers/extcon/extcon-sm5502.c @@ -95,7 +95,7 @@ static struct reg_data sm5502_reg_data[] = { static const unsigned int sm5502_extcon_cable[] = { EXTCON_USB, EXTCON_USB_HOST, - EXTCON_TA, + EXTCON_CHG_USB_DCP, EXTCON_NONE, }; @@ -389,7 +389,7 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info, vbus_sw = VBUSIN_SWITCH_VBUSOUT_WITH_USB; break; case SM5502_MUIC_ADC_OPEN_TA: - id = EXTCON_TA; + id = EXTCON_CHG_USB_DCP; con_sw = DM_DP_SWITCH_OPEN; vbus_sw = VBUSIN_SWITCH_VBUSOUT; break; diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 8dd0af1d50bc..21a123cadf78 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -39,37 +39,40 @@ #define CABLE_NAME_MAX 30 static const char *extcon_name[] = { - [EXTCON_NONE] = "NONE", + [EXTCON_NONE] = "NONE", /* USB external connector */ - [EXTCON_USB] = "USB", - [EXTCON_USB_HOST] = "USB-HOST", - - /* Charger external connector */ - [EXTCON_TA] = "TA", - [EXTCON_FAST_CHARGER] = "FAST-CHARGER", - [EXTCON_SLOW_CHARGER] = "SLOW-CHARGER", - [EXTCON_CHARGE_DOWNSTREAM] = "CHARGE-DOWNSTREAM", - - /* Audio/Video external connector */ - [EXTCON_LINE_IN] = "LINE-IN", - [EXTCON_LINE_OUT] = "LINE-OUT", - [EXTCON_MICROPHONE] = "MICROPHONE", - [EXTCON_HEADPHONE] = "HEADPHONE", - - [EXTCON_HDMI] = "HDMI", - [EXTCON_MHL] = "MHL", - [EXTCON_DVI] = "DVI", - [EXTCON_VGA] = "VGA", - [EXTCON_SPDIF_IN] = "SPDIF-IN", - [EXTCON_SPDIF_OUT] = "SPDIF-OUT", - [EXTCON_VIDEO_IN] = "VIDEO-IN", - [EXTCON_VIDEO_OUT] = "VIDEO-OUT", - - /* Etc external connector */ - [EXTCON_DOCK] = "DOCK", - [EXTCON_JIG] = "JIG", - [EXTCON_MECHANICAL] = "MECHANICAL", + [EXTCON_USB] = "USB", + [EXTCON_USB_HOST] = "USB-HOST", + + /* Charging external connector */ + [EXTCON_CHG_USB_SDP] = "SDP", + [EXTCON_CHG_USB_DCP] = "DCP", + [EXTCON_CHG_USB_CDP] = "CDP", + [EXTCON_CHG_USB_ACA] = "ACA", + [EXTCON_CHG_USB_FAST] = "FAST-CHARGER", + [EXTCON_CHG_USB_SLOW] = "SLOW-CHARGER", + + /* Jack external connector */ + [EXTCON_JACK_MICROPHONE] = "MICROPHONE", + [EXTCON_JACK_HEADPHONE] = "HEADPHONE", + [EXTCON_JACK_LINE_IN] = "LINE-IN", + [EXTCON_JACK_LINE_OUT] = "LINE-OUT", + [EXTCON_JACK_VIDEO_IN] = "VIDEO-IN", + [EXTCON_JACK_VIDEO_OUT] = "VIDEO-OUT", + [EXTCON_JACK_SPDIF_IN] = "SPDIF-IN", + [EXTCON_JACK_SPDIF_OUT] = "SPDIF-OUT", + + /* Display external connector */ + [EXTCON_DISP_HDMI] = "HDMI", + [EXTCON_DISP_MHL] = "MHL", + [EXTCON_DISP_DVI] = "DVI", + [EXTCON_DISP_VGA] = "VGA", + + /* Miscellaneous external connector */ + [EXTCON_DOCK] = "DOCK", + [EXTCON_JIG] = "JIG", + [EXTCON_MECHANICAL] = "MECHANICAL", NULL, }; diff --git a/include/linux/extcon.h b/include/linux/extcon.h index c0f8c4fc5d45..7abf674c388c 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -31,32 +31,42 @@ /* * Define the unique id of supported external connectors */ -#define EXTCON_NONE 0 - -#define EXTCON_USB 1 /* USB connector */ -#define EXTCON_USB_HOST 2 - -#define EXTCON_TA 3 /* Charger connector */ -#define EXTCON_FAST_CHARGER 4 -#define EXTCON_SLOW_CHARGER 5 -#define EXTCON_CHARGE_DOWNSTREAM 6 - -#define EXTCON_LINE_IN 7 /* Audio/Video connector */ -#define EXTCON_LINE_OUT 8 -#define EXTCON_MICROPHONE 9 -#define EXTCON_HEADPHONE 10 -#define EXTCON_HDMI 11 -#define EXTCON_MHL 12 -#define EXTCON_DVI 13 -#define EXTCON_VGA 14 -#define EXTCON_SPDIF_IN 15 -#define EXTCON_SPDIF_OUT 16 -#define EXTCON_VIDEO_IN 17 -#define EXTCON_VIDEO_OUT 18 - -#define EXTCON_DOCK 19 /* Misc connector */ -#define EXTCON_JIG 20 -#define EXTCON_MECHANICAL 21 +#define EXTCON_NONE 0 + +/* USB external connector */ +#define EXTCON_USB 1 +#define EXTCON_USB_HOST 2 + +/* Charging external connector */ +#define EXTCON_CHG_USB_SDP 5 /* Standard Downstream Port */ +#define EXTCON_CHG_USB_DCP 6 /* Dedicated Charging Port */ +#define EXTCON_CHG_USB_CDP 7 /* Charging Downstream Port */ +#define EXTCON_CHG_USB_ACA 8 /* Accessory Charger Adapter */ +#define EXTCON_CHG_USB_FAST 9 +#define EXTCON_CHG_USB_SLOW 10 + +/* Jack external connector */ +#define EXTCON_JACK_MICROPHONE 20 +#define EXTCON_JACK_HEADPHONE 21 +#define EXTCON_JACK_LINE_IN 22 +#define EXTCON_JACK_LINE_OUT 23 +#define EXTCON_JACK_VIDEO_IN 24 +#define EXTCON_JACK_VIDEO_OUT 25 +#define EXTCON_JACK_SPDIF_IN 26 /* Sony Philips Digital InterFace */ +#define EXTCON_JACK_SPDIF_OUT 27 + +/* Display external connector */ +#define EXTCON_DISP_HDMI 40 /* High-Definition Multimedia Interface */ +#define EXTCON_DISP_MHL 41 /* Mobile High-Definition Link */ +#define EXTCON_DISP_DVI 42 /* Digital Visual Interface */ +#define EXTCON_DISP_VGA 43 /* Video Graphics Array */ + +/* Miscellaneous external connector */ +#define EXTCON_DOCK 60 +#define EXTCON_JIG 61 +#define EXTCON_MECHANICAL 62 + +#define EXTCON_NUM 63 struct extcon_cable; -- cgit v1.2.3 From 52d63671c15bd3dfcd72f4ea324b338d1309db97 Mon Sep 17 00:00:00 2001 From: Gabriel Laskar Date: Tue, 6 Oct 2015 16:27:36 +0200 Subject: msm: remove unused header Signed-off-by: Gabriel Laskar Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- include/linux/msm_mdp.h | 79 ------------------------------------------------- 1 file changed, 79 deletions(-) delete mode 100644 include/linux/msm_mdp.h (limited to 'include') diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h deleted file mode 100644 index fe722c1fb61d..000000000000 --- a/include/linux/msm_mdp.h +++ /dev/null @@ -1,79 +0,0 @@ -/* include/linux/msm_mdp.h - * - * Copyright (C) 2007 Google Incorporated - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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. - */ -#ifndef _MSM_MDP_H_ -#define _MSM_MDP_H_ - -#include - -#define MSMFB_IOCTL_MAGIC 'm' -#define MSMFB_GRP_DISP _IOW(MSMFB_IOCTL_MAGIC, 1, unsigned int) -#define MSMFB_BLIT _IOW(MSMFB_IOCTL_MAGIC, 2, unsigned int) - -enum { - MDP_RGB_565, /* RGB 565 planar */ - MDP_XRGB_8888, /* RGB 888 padded */ - MDP_Y_CBCR_H2V2, /* Y and CbCr, pseudo planar w/ Cb is in MSB */ - MDP_ARGB_8888, /* ARGB 888 */ - MDP_RGB_888, /* RGB 888 planar */ - MDP_Y_CRCB_H2V2, /* Y and CrCb, pseudo planar w/ Cr is in MSB */ - MDP_YCRYCB_H2V1, /* YCrYCb interleave */ - MDP_Y_CRCB_H2V1, /* Y and CrCb, pseduo planar w/ Cr is in MSB */ - MDP_Y_CBCR_H2V1, /* Y and CrCb, pseduo planar w/ Cr is in MSB */ - MDP_RGBA_8888, /* ARGB 888 */ - MDP_BGRA_8888, /* ABGR 888 */ - MDP_RGBX_8888, /* RGBX 888 */ - MDP_IMGTYPE_LIMIT /* Non valid image type after this enum */ -}; - -enum { - PMEM_IMG, - FB_IMG, -}; - -/* flag values */ -#define MDP_ROT_NOP 0 -#define MDP_FLIP_LR 0x1 -#define MDP_FLIP_UD 0x2 -#define MDP_ROT_90 0x4 -#define MDP_ROT_180 (MDP_FLIP_UD|MDP_FLIP_LR) -#define MDP_ROT_270 (MDP_ROT_90|MDP_FLIP_UD|MDP_FLIP_LR) -#define MDP_DITHER 0x8 -#define MDP_BLUR 0x10 - -#define MDP_TRANSP_NOP 0xffffffff -#define MDP_ALPHA_NOP 0xff - -struct mdp_rect { - u32 x, y, w, h; -}; - -struct mdp_img { - u32 width, height, format, offset; - int memory_id; /* the file descriptor */ -}; - -struct mdp_blit_req { - struct mdp_img src; - struct mdp_img dst; - struct mdp_rect src_rect; - struct mdp_rect dst_rect; - u32 alpha, transp_mask, flags; -}; - -struct mdp_blit_req_list { - u32 count; - struct mdp_blit_req req[]; -}; - -#endif /* _MSM_MDP_H_ */ -- cgit v1.2.3 From ad1bfe410e91189522514ea784668dc75a4e64c4 Mon Sep 17 00:00:00 2001 From: Dmitry Kalinkin Date: Sun, 11 Oct 2015 01:00:58 +0300 Subject: vme: 8-bit status/id takes 256 values, not 255 Fixes an off by one array size. Signed-off-by: Dmitry Kalinkin Signed-off-by: Greg Kroah-Hartman --- drivers/vme/vme_bridge.h | 4 +++- include/linux/vme.h | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/vme/vme_bridge.h b/drivers/vme/vme_bridge.h index 934949abd745..a3ef63b336e4 100644 --- a/drivers/vme/vme_bridge.h +++ b/drivers/vme/vme_bridge.h @@ -1,6 +1,8 @@ #ifndef _VME_BRIDGE_H_ #define _VME_BRIDGE_H_ +#include + #define VME_CRCSR_BUF_SIZE (508*1024) /* * Resource structures @@ -88,7 +90,7 @@ struct vme_callback { struct vme_irq { int count; - struct vme_callback callback[255]; + struct vme_callback callback[VME_NUM_STATUSID]; }; /* Allow 16 characters for name (including null character) */ diff --git a/include/linux/vme.h b/include/linux/vme.h index c0131358f351..71e4a6dec5ac 100644 --- a/include/linux/vme.h +++ b/include/linux/vme.h @@ -81,6 +81,9 @@ struct vme_resource { extern struct bus_type vme_bus_type; +/* Number of VME interrupt vectors */ +#define VME_NUM_STATUSID 256 + /* VME_MAX_BRIDGES comes from the type of vme_bus_numbers */ #define VME_MAX_BRIDGES (sizeof(unsigned int)*8) #define VME_MAX_SLOTS 32 -- cgit v1.2.3