diff options
Diffstat (limited to 'drivers')
195 files changed, 7965 insertions, 3112 deletions
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index e764f9ac9cf8..7b3ad8ed2f4e 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -55,14 +55,19 @@ static const guid_t ads_guid = GUID_INIT(0xdbb8e3e6, 0x5886, 0x4ba6, 0x87, 0x95, 0x13, 0x19, 0xf5, 0x2a, 0x96, 0x6b); +static const guid_t buffer_prop_guid = + GUID_INIT(0xedb12dd0, 0x363d, 0x4085, + 0xa3, 0xd2, 0x49, 0x52, 0x2c, 0xa1, 0x60, 0xc4); + static bool acpi_enumerate_nondev_subnodes(acpi_handle scope, - const union acpi_object *desc, + union acpi_object *desc, struct acpi_device_data *data, struct fwnode_handle *parent); -static bool acpi_extract_properties(const union acpi_object *desc, +static bool acpi_extract_properties(acpi_handle handle, + union acpi_object *desc, struct acpi_device_data *data); -static bool acpi_nondev_subnode_extract(const union acpi_object *desc, +static bool acpi_nondev_subnode_extract(union acpi_object *desc, acpi_handle handle, const union acpi_object *link, struct list_head *list, @@ -81,7 +86,7 @@ static bool acpi_nondev_subnode_extract(const union acpi_object *desc, INIT_LIST_HEAD(&dn->data.properties); INIT_LIST_HEAD(&dn->data.subnodes); - result = acpi_extract_properties(desc, &dn->data); + result = acpi_extract_properties(handle, desc, &dn->data); if (handle) { acpi_handle scope; @@ -155,16 +160,16 @@ static bool acpi_nondev_subnode_ok(acpi_handle scope, return acpi_nondev_subnode_data_ok(handle, link, list, parent); } -static int acpi_add_nondev_subnodes(acpi_handle scope, - const union acpi_object *links, - struct list_head *list, - struct fwnode_handle *parent) +static bool acpi_add_nondev_subnodes(acpi_handle scope, + union acpi_object *links, + struct list_head *list, + struct fwnode_handle *parent) { bool ret = false; int i; for (i = 0; i < links->package.count; i++) { - const union acpi_object *link, *desc; + union acpi_object *link, *desc; acpi_handle handle; bool result; @@ -204,7 +209,7 @@ static int acpi_add_nondev_subnodes(acpi_handle scope, } static bool acpi_enumerate_nondev_subnodes(acpi_handle scope, - const union acpi_object *desc, + union acpi_object *desc, struct acpi_device_data *data, struct fwnode_handle *parent) { @@ -212,7 +217,8 @@ static bool acpi_enumerate_nondev_subnodes(acpi_handle scope, /* Look for the ACPI data subnodes GUID. */ for (i = 0; i < desc->package.count; i += 2) { - const union acpi_object *guid, *links; + const union acpi_object *guid; + union acpi_object *links; guid = &desc->package.elements[i]; links = &desc->package.elements[i + 1]; @@ -325,7 +331,7 @@ static bool acpi_is_property_guid(const guid_t *guid) struct acpi_device_properties * acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid, - const union acpi_object *properties) + union acpi_object *properties) { struct acpi_device_properties *props; @@ -340,7 +346,141 @@ acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid, return props; } -static bool acpi_extract_properties(const union acpi_object *desc, +static void acpi_nondev_subnode_tag(acpi_handle handle, void *context) +{ +} + +static void acpi_untie_nondev_subnodes(struct acpi_device_data *data) +{ + struct acpi_data_node *dn; + + list_for_each_entry(dn, &data->subnodes, sibling) { + acpi_detach_data(dn->handle, acpi_nondev_subnode_tag); + + acpi_untie_nondev_subnodes(&dn->data); + } +} + +static bool acpi_tie_nondev_subnodes(struct acpi_device_data *data) +{ + struct acpi_data_node *dn; + + list_for_each_entry(dn, &data->subnodes, sibling) { + acpi_status status; + bool ret; + + status = acpi_attach_data(dn->handle, acpi_nondev_subnode_tag, dn); + if (ACPI_FAILURE(status)) { + acpi_handle_err(dn->handle, "Can't tag data node\n"); + return false; + } + + ret = acpi_tie_nondev_subnodes(&dn->data); + if (!ret) + return ret; + } + + return true; +} + +static void acpi_data_add_buffer_props(acpi_handle handle, + struct acpi_device_data *data, + union acpi_object *properties) +{ + struct acpi_device_properties *props; + union acpi_object *package; + size_t alloc_size; + unsigned int i; + u32 *count; + + if (check_mul_overflow((size_t)properties->package.count, + sizeof(*package) + sizeof(void *), + &alloc_size) || + check_add_overflow(sizeof(*props) + sizeof(*package), alloc_size, + &alloc_size)) { + acpi_handle_warn(handle, + "can't allocate memory for %u buffer props", + properties->package.count); + return; + } + + props = kvzalloc(alloc_size, GFP_KERNEL); + if (!props) + return; + + props->guid = &buffer_prop_guid; + props->bufs = (void *)(props + 1); + props->properties = (void *)(props->bufs + properties->package.count); + + /* Outer package */ + package = props->properties; + package->type = ACPI_TYPE_PACKAGE; + package->package.elements = package + 1; + count = &package->package.count; + *count = 0; + + /* Inner packages */ + package++; + + for (i = 0; i < properties->package.count; i++) { + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + union acpi_object *property = &properties->package.elements[i]; + union acpi_object *prop, *obj, *buf_obj; + acpi_status status; + + if (property->type != ACPI_TYPE_PACKAGE || + property->package.count != 2) { + acpi_handle_warn(handle, + "buffer property %u has %u entries\n", + i, property->package.count); + continue; + } + + prop = &property->package.elements[0]; + obj = &property->package.elements[1]; + + if (prop->type != ACPI_TYPE_STRING || + obj->type != ACPI_TYPE_STRING) { + acpi_handle_warn(handle, + "wrong object types %u and %u\n", + prop->type, obj->type); + continue; + } + + status = acpi_evaluate_object_typed(handle, obj->string.pointer, + NULL, &buf, + ACPI_TYPE_BUFFER); + if (ACPI_FAILURE(status)) { + acpi_handle_warn(handle, + "can't evaluate \"%*pE\" as buffer\n", + obj->string.length, + obj->string.pointer); + continue; + } + + package->type = ACPI_TYPE_PACKAGE; + package->package.elements = prop; + package->package.count = 2; + + buf_obj = buf.pointer; + + /* Replace the string object with a buffer object */ + obj->type = ACPI_TYPE_BUFFER; + obj->buffer.length = buf_obj->buffer.length; + obj->buffer.pointer = buf_obj->buffer.pointer; + + props->bufs[i] = buf.pointer; + package++; + (*count)++; + } + + if (*count) + list_add(&props->list, &data->properties); + else + kvfree(props); +} + +static bool acpi_extract_properties(acpi_handle scope, union acpi_object *desc, struct acpi_device_data *data) { int i; @@ -350,7 +490,8 @@ static bool acpi_extract_properties(const union acpi_object *desc, /* Look for the device properties GUID. */ for (i = 0; i < desc->package.count; i += 2) { - const union acpi_object *guid, *properties; + const union acpi_object *guid; + union acpi_object *properties; guid = &desc->package.elements[i]; properties = &desc->package.elements[i + 1]; @@ -364,6 +505,12 @@ static bool acpi_extract_properties(const union acpi_object *desc, properties->type != ACPI_TYPE_PACKAGE) break; + if (guid_equal((guid_t *)guid->buffer.pointer, + &buffer_prop_guid)) { + acpi_data_add_buffer_props(scope, data, properties); + continue; + } + if (!acpi_is_property_guid((guid_t *)guid->buffer.pointer)) continue; @@ -410,7 +557,7 @@ void acpi_init_properties(struct acpi_device *adev) if (ACPI_FAILURE(status)) goto out; - if (acpi_extract_properties(buf.pointer, &adev->data)) { + if (acpi_extract_properties(adev->handle, buf.pointer, &adev->data)) { adev->data.pointer = buf.pointer; if (acpi_of) acpi_init_of_compatible(adev); @@ -422,6 +569,9 @@ void acpi_init_properties(struct acpi_device *adev) if (!adev->data.pointer) { acpi_handle_debug(adev->handle, "Invalid _DSD data, skipping\n"); ACPI_FREE(buf.pointer); + } else { + if (!acpi_tie_nondev_subnodes(&adev->data)) + acpi_untie_nondev_subnodes(&adev->data); } out: @@ -438,8 +588,14 @@ static void acpi_free_device_properties(struct list_head *list) struct acpi_device_properties *props, *tmp; list_for_each_entry_safe(props, tmp, list, list) { + u32 i; + list_del(&props->list); - kfree(props); + /* Buffer data properties were separately allocated */ + if (props->bufs) + for (i = 0; i < props->properties->package.count; i++) + ACPI_FREE(props->bufs[i]); + kvfree(props); } } @@ -462,6 +618,7 @@ static void acpi_destroy_nondev_subnodes(struct list_head *list) void acpi_free_properties(struct acpi_device *adev) { + acpi_untie_nondev_subnodes(&adev->data); acpi_destroy_nondev_subnodes(&adev->data.subnodes); ACPI_FREE((void *)adev->data.pointer); adev->data.of_compatible = NULL; @@ -633,6 +790,58 @@ acpi_fwnode_get_named_child_node(const struct fwnode_handle *fwnode, return NULL; } +static int acpi_get_ref_args(struct fwnode_reference_args *args, + struct fwnode_handle *ref_fwnode, + const union acpi_object **element, + const union acpi_object *end, size_t num_args) +{ + u32 nargs = 0, i; + + /* + * Find the referred data extension node under the + * referred device node. + */ + for (; *element < end && (*element)->type == ACPI_TYPE_STRING; + (*element)++) { + const char *child_name = (*element)->string.pointer; + + ref_fwnode = acpi_fwnode_get_named_child_node(ref_fwnode, child_name); + if (!ref_fwnode) + return -EINVAL; + } + + /* + * Assume the following integer elements are all args. Stop counting on + * the first reference or end of the package arguments. In case of + * neither reference, nor integer, return an error, we can't parse it. + */ + for (i = 0; (*element) + i < end && i < num_args; i++) { + acpi_object_type type = (*element)[i].type; + + if (type == ACPI_TYPE_LOCAL_REFERENCE) + break; + + if (type == ACPI_TYPE_INTEGER) + nargs++; + else + return -EINVAL; + } + + if (nargs > NR_FWNODE_REFERENCE_ARGS) + return -EINVAL; + + if (args) { + args->fwnode = ref_fwnode; + args->nargs = nargs; + for (i = 0; i < nargs; i++) + args->args[i] = (*element)[i].integer.value; + } + + (*element) += nargs; + + return 0; +} + /** * __acpi_node_get_property_reference - returns handle to the referenced object * @fwnode: Firmware node to get the property from @@ -686,11 +895,9 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, if (ret) return ret == -EINVAL ? -ENOENT : -EINVAL; - /* - * The simplest case is when the value is a single reference. Just - * return that reference then. - */ - if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) { + switch (obj->type) { + case ACPI_TYPE_LOCAL_REFERENCE: + /* Plain single reference without arguments. */ if (index) return -ENOENT; @@ -701,19 +908,21 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, args->fwnode = acpi_fwnode_handle(device); args->nargs = 0; return 0; + case ACPI_TYPE_PACKAGE: + /* + * If it is not a single reference, then it is a package of + * references followed by number of ints as follows: + * + * Package () { REF, INT, REF, INT, INT } + * + * The index argument is then used to determine which reference + * the caller wants (along with the arguments). + */ + break; + default: + return -EINVAL; } - /* - * If it is not a single reference, then it is a package of - * references followed by number of ints as follows: - * - * Package () { REF, INT, REF, INT, INT } - * - * The index argument is then used to determine which reference - * the caller wants (along with the arguments). - */ - if (obj->type != ACPI_TYPE_PACKAGE) - return -EINVAL; if (index >= obj->package.count) return -ENOENT; @@ -721,66 +930,30 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, end = element + obj->package.count; while (element < end) { - u32 nargs, i; - - if (element->type == ACPI_TYPE_LOCAL_REFERENCE) { - struct fwnode_handle *ref_fwnode; - + switch (element->type) { + case ACPI_TYPE_LOCAL_REFERENCE: device = acpi_fetch_acpi_dev(element->reference.handle); if (!device) return -EINVAL; - nargs = 0; element++; - /* - * Find the referred data extension node under the - * referred device node. - */ - for (ref_fwnode = acpi_fwnode_handle(device); - element < end && element->type == ACPI_TYPE_STRING; - element++) { - ref_fwnode = acpi_fwnode_get_named_child_node( - ref_fwnode, element->string.pointer); - if (!ref_fwnode) - return -EINVAL; - } - - /* - * Assume the following integer elements are all args. - * Stop counting on the first reference or end of the - * package arguments. In case of neither reference, - * nor integer, return an error, we can't parse it. - */ - for (i = 0; element + i < end && i < num_args; i++) { - int type = element[i].type; - - if (type == ACPI_TYPE_LOCAL_REFERENCE) - break; - if (type == ACPI_TYPE_INTEGER) - nargs++; - else - return -EINVAL; - } - - if (nargs > NR_FWNODE_REFERENCE_ARGS) - return -EINVAL; - - if (idx == index) { - args->fwnode = ref_fwnode; - args->nargs = nargs; - for (i = 0; i < nargs; i++) - args->args[i] = element[i].integer.value; + ret = acpi_get_ref_args(idx == index ? args : NULL, + acpi_fwnode_handle(device), + &element, end, num_args); + if (ret < 0) + return ret; + if (idx == index) return 0; - } - element += nargs; - } else if (element->type == ACPI_TYPE_INTEGER) { + break; + case ACPI_TYPE_INTEGER: if (idx == index) return -ENOENT; element++; - } else { + break; + default: return -EINVAL; } @@ -852,67 +1025,37 @@ static int acpi_data_prop_read_single(const struct acpi_device_data *data, return ret; } -static int acpi_copy_property_array_u8(const union acpi_object *items, u8 *val, - size_t nval) -{ - int i; - - for (i = 0; i < nval; i++) { - if (items[i].type != ACPI_TYPE_INTEGER) - return -EPROTO; - if (items[i].integer.value > U8_MAX) - return -EOVERFLOW; - - val[i] = items[i].integer.value; - } - return 0; -} - -static int acpi_copy_property_array_u16(const union acpi_object *items, - u16 *val, size_t nval) -{ - int i; - - for (i = 0; i < nval; i++) { - if (items[i].type != ACPI_TYPE_INTEGER) - return -EPROTO; - if (items[i].integer.value > U16_MAX) - return -EOVERFLOW; - - val[i] = items[i].integer.value; - } - return 0; -} - -static int acpi_copy_property_array_u32(const union acpi_object *items, - u32 *val, size_t nval) -{ - int i; - - for (i = 0; i < nval; i++) { - if (items[i].type != ACPI_TYPE_INTEGER) - return -EPROTO; - if (items[i].integer.value > U32_MAX) - return -EOVERFLOW; - - val[i] = items[i].integer.value; - } - return 0; -} - -static int acpi_copy_property_array_u64(const union acpi_object *items, - u64 *val, size_t nval) -{ - int i; - - for (i = 0; i < nval; i++) { - if (items[i].type != ACPI_TYPE_INTEGER) - return -EPROTO; - - val[i] = items[i].integer.value; - } - return 0; -} +#define acpi_copy_property_array_uint(items, val, nval) \ + ({ \ + typeof(items) __items = items; \ + typeof(val) __val = val; \ + typeof(nval) __nval = nval; \ + size_t i; \ + int ret = 0; \ + \ + for (i = 0; i < __nval; i++) { \ + if (__items->type == ACPI_TYPE_BUFFER) { \ + __val[i] = __items->buffer.pointer[i]; \ + continue; \ + } \ + if (__items[i].type != ACPI_TYPE_INTEGER) { \ + ret = -EPROTO; \ + break; \ + } \ + if (__items[i].integer.value > _Generic(__val, \ + u8: U8_MAX, \ + u16: U16_MAX, \ + u32: U32_MAX, \ + u64: U64_MAX, \ + default: 0U)) { \ + ret = -EOVERFLOW; \ + break; \ + } \ + \ + __val[i] = __items[i].integer.value; \ + } \ + ret; \ + }) static int acpi_copy_property_array_string(const union acpi_object *items, char **val, size_t nval) @@ -954,31 +1097,54 @@ static int acpi_data_prop_read(const struct acpi_device_data *data, } ret = acpi_data_get_property_array(data, propname, ACPI_TYPE_ANY, &obj); + if (ret && proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64) + ret = acpi_data_get_property(data, propname, ACPI_TYPE_BUFFER, + &obj); if (ret) return ret; - if (!val) + if (!val) { + if (obj->type == ACPI_TYPE_BUFFER) + return obj->buffer.length; + return obj->package.count; + } - if (proptype != DEV_PROP_STRING && nval > obj->package.count) - return -EOVERFLOW; + switch (proptype) { + case DEV_PROP_STRING: + break; + case DEV_PROP_U8 ... DEV_PROP_U64: + if (obj->type == ACPI_TYPE_BUFFER) { + if (nval > obj->buffer.length) + return -EOVERFLOW; + break; + } + fallthrough; + default: + if (nval > obj->package.count) + return -EOVERFLOW; + break; + } if (nval == 0) return -EINVAL; - items = obj->package.elements; + if (obj->type != ACPI_TYPE_BUFFER) + items = obj->package.elements; + else + items = obj; switch (proptype) { case DEV_PROP_U8: - ret = acpi_copy_property_array_u8(items, (u8 *)val, nval); + ret = acpi_copy_property_array_uint(items, (u8 *)val, nval); break; case DEV_PROP_U16: - ret = acpi_copy_property_array_u16(items, (u16 *)val, nval); + ret = acpi_copy_property_array_uint(items, (u16 *)val, nval); break; case DEV_PROP_U32: - ret = acpi_copy_property_array_u32(items, (u32 *)val, nval); + ret = acpi_copy_property_array_uint(items, (u32 *)val, nval); break; case DEV_PROP_U64: - ret = acpi_copy_property_array_u64(items, (u64 *)val, nval); + ret = acpi_copy_property_array_uint(items, (u64 *)val, nval); break; case DEV_PROP_STRING: ret = acpi_copy_property_array_string( diff --git a/drivers/acpi/viot.c b/drivers/acpi/viot.c index 647f11cf165d..6132092dab2a 100644 --- a/drivers/acpi/viot.c +++ b/drivers/acpi/viot.c @@ -88,7 +88,7 @@ static int __init viot_get_pci_iommu_fwnode(struct viot_iommu *viommu, return -ENODEV; } - fwnode = pdev->dev.fwnode; + fwnode = dev_fwnode(&pdev->dev); if (!fwnode) { /* * PCI devices aren't necessarily described by ACPI. Create a @@ -101,7 +101,7 @@ static int __init viot_get_pci_iommu_fwnode(struct viot_iommu *viommu, } set_primary_fwnode(&pdev->dev, fwnode); } - viommu->fwnode = pdev->dev.fwnode; + viommu->fwnode = dev_fwnode(&pdev->dev); pci_dev_put(pdev); return 0; } @@ -314,7 +314,7 @@ static int viot_dev_iommu_init(struct device *dev, struct viot_iommu *viommu, return -ENODEV; /* We're not translating ourself */ - if (viommu->fwnode == dev->fwnode) + if (device_match_fwnode(dev, viommu->fwnode)) return -EINVAL; ops = iommu_ops_from_fwnode(viommu->fwnode); diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c index 81ce81a75fc6..681cb3786794 100644 --- a/drivers/atm/idt77252.c +++ b/drivers/atm/idt77252.c @@ -3752,6 +3752,7 @@ static void __exit idt77252_exit(void) card = idt77252_chain; dev = card->atmdev; idt77252_chain = card->next; + del_timer_sync(&card->tst_timer); if (dev->phy->stop) dev->phy->stop(dev); diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 0d8ec2fe5740..f9e39301c4af 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1297,7 +1297,7 @@ static void rbd_osd_submit(struct ceph_osd_request *osd_req) dout("%s osd_req %p for obj_req %p objno %llu %llu~%llu\n", __func__, osd_req, obj_req, obj_req->ex.oe_objno, obj_req->ex.oe_off, obj_req->ex.oe_len); - ceph_osdc_start_request(osd_req->r_osdc, osd_req, false); + ceph_osdc_start_request(osd_req->r_osdc, osd_req); } /* @@ -2081,7 +2081,7 @@ static int rbd_object_map_update(struct rbd_obj_request *obj_req, u64 snap_id, if (ret) return ret; - ceph_osdc_start_request(osdc, req, false); + ceph_osdc_start_request(osdc, req); return 0; } @@ -4768,7 +4768,7 @@ static int rbd_obj_read_sync(struct rbd_device *rbd_dev, if (ret) goto out_req; - ceph_osdc_start_request(osdc, req, false); + ceph_osdc_start_request(osdc, req); ret = ceph_osdc_wait_request(osdc, req); if (ret >= 0) ceph_copy_from_page_vector(pages, buf, 0, ret); diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index d7d72e8f6e55..30255fcaf181 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -101,6 +101,14 @@ static inline blk_status_t virtblk_result(struct virtblk_req *vbr) } } +static inline struct virtio_blk_vq *get_virtio_blk_vq(struct blk_mq_hw_ctx *hctx) +{ + struct virtio_blk *vblk = hctx->queue->queuedata; + struct virtio_blk_vq *vq = &vblk->vqs[hctx->queue_num]; + + return vq; +} + static int virtblk_add_req(struct virtqueue *vq, struct virtblk_req *vbr) { struct scatterlist hdr, status, *sgs[3]; @@ -416,7 +424,7 @@ static void virtio_queue_rqs(struct request **rqlist) struct request *requeue_list = NULL; rq_list_for_each_safe(rqlist, req, next) { - struct virtio_blk_vq *vq = req->mq_hctx->driver_data; + struct virtio_blk_vq *vq = get_virtio_blk_vq(req->mq_hctx); bool kick; if (!virtblk_prep_rq_batch(req)) { @@ -837,7 +845,7 @@ static void virtblk_complete_batch(struct io_comp_batch *iob) static int virtblk_poll(struct blk_mq_hw_ctx *hctx, struct io_comp_batch *iob) { struct virtio_blk *vblk = hctx->queue->queuedata; - struct virtio_blk_vq *vq = hctx->driver_data; + struct virtio_blk_vq *vq = get_virtio_blk_vq(hctx); struct virtblk_req *vbr; unsigned long flags; unsigned int len; @@ -862,22 +870,10 @@ static int virtblk_poll(struct blk_mq_hw_ctx *hctx, struct io_comp_batch *iob) return found; } -static int virtblk_init_hctx(struct blk_mq_hw_ctx *hctx, void *data, - unsigned int hctx_idx) -{ - struct virtio_blk *vblk = data; - struct virtio_blk_vq *vq = &vblk->vqs[hctx_idx]; - - WARN_ON(vblk->tag_set.tags[hctx_idx] != hctx->tags); - hctx->driver_data = vq; - return 0; -} - static const struct blk_mq_ops virtio_mq_ops = { .queue_rq = virtio_queue_rq, .queue_rqs = virtio_queue_rqs, .commit_rqs = virtio_commit_rqs, - .init_hctx = virtblk_init_hctx, .complete = virtblk_request_done, .map_queues = virtblk_map_queues, .poll = virtblk_poll, diff --git a/drivers/clocksource/timer-riscv.c b/drivers/clocksource/timer-riscv.c index e460df7ed799..969a552da8d2 100644 --- a/drivers/clocksource/timer-riscv.c +++ b/drivers/clocksource/timer-riscv.c @@ -7,6 +7,9 @@ * either be read from the "time" and "timeh" CSRs, and can use the SBI to * setup events, or directly accessed using MMIO registers. */ + +#define pr_fmt(fmt) "riscv-timer: " fmt + #include <linux/clocksource.h> #include <linux/clockchips.h> #include <linux/cpu.h> @@ -20,14 +23,28 @@ #include <linux/of_irq.h> #include <clocksource/timer-riscv.h> #include <asm/smp.h> +#include <asm/hwcap.h> #include <asm/sbi.h> #include <asm/timex.h> +static DEFINE_STATIC_KEY_FALSE(riscv_sstc_available); + static int riscv_clock_next_event(unsigned long delta, struct clock_event_device *ce) { + u64 next_tval = get_cycles64() + delta; + csr_set(CSR_IE, IE_TIE); - sbi_set_timer(get_cycles64() + delta); + if (static_branch_likely(&riscv_sstc_available)) { +#if defined(CONFIG_32BIT) + csr_write(CSR_STIMECMP, next_tval & 0xFFFFFFFF); + csr_write(CSR_STIMECMPH, next_tval >> 32); +#else + csr_write(CSR_STIMECMP, next_tval); +#endif + } else + sbi_set_timer(next_tval); + return 0; } @@ -166,6 +183,12 @@ static int __init riscv_timer_init_dt(struct device_node *n) if (error) pr_err("cpu hp setup state failed for RISCV timer [%d]\n", error); + + if (riscv_isa_extension_available(NULL, SSTC)) { + pr_info("Timer interrupt in S-mode is available via sstc extension\n"); + static_branch_enable(&riscv_sstc_available); + } + return error; } diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index eb0c2d041f13..86d670c71286 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1226,7 +1226,7 @@ retry: ret = dma_resv_lock_slow_interruptible(obj->resv, acquire_ctx); if (ret) { - ww_acquire_done(acquire_ctx); + ww_acquire_fini(acquire_ctx); return ret; } } @@ -1251,7 +1251,7 @@ retry: goto retry; } - ww_acquire_done(acquire_ctx); + ww_acquire_fini(acquire_ctx); return ret; } } diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index 8ad0e02991ca..904fc893c905 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -302,6 +302,7 @@ static int drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem, ret = dma_buf_vmap(obj->import_attach->dmabuf, map); if (!ret) { if (WARN_ON(map->is_iomem)) { + dma_buf_vunmap(obj->import_attach->dmabuf, map); ret = -EIO; goto err_put_pages; } diff --git a/drivers/input/input-core-private.h b/drivers/input/input-core-private.h new file mode 100644 index 000000000000..116834cf8868 --- /dev/null +++ b/drivers/input/input-core-private.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _INPUT_CORE_PRIVATE_H +#define _INPUT_CORE_PRIVATE_H + +/* + * Functions and definitions that are private to input core, + * should not be used by input drivers or handlers. + */ + +struct input_dev; + +void input_mt_release_slots(struct input_dev *dev); +void input_handle_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value); + +#endif /* _INPUT_CORE_PRIVATE_H */ diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index 44fe6f2f063c..14b53dac1253 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -8,6 +8,7 @@ #include <linux/input/mt.h> #include <linux/export.h> #include <linux/slab.h> +#include "input-core-private.h" #define TRKID_SGN ((TRKID_MAX + 1) >> 1) @@ -259,10 +260,13 @@ static void __input_mt_drop_unused(struct input_dev *dev, struct input_mt *mt) { int i; + lockdep_assert_held(&dev->event_lock); + for (i = 0; i < mt->num_slots; i++) { - if (!input_mt_is_used(mt, &mt->slots[i])) { - input_mt_slot(dev, i); - input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + if (input_mt_is_active(&mt->slots[i]) && + !input_mt_is_used(mt, &mt->slots[i])) { + input_handle_event(dev, EV_ABS, ABS_MT_SLOT, i); + input_handle_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); } } } @@ -278,13 +282,44 @@ void input_mt_drop_unused(struct input_dev *dev) struct input_mt *mt = dev->mt; if (mt) { + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + __input_mt_drop_unused(dev, mt); mt->frame++; + + spin_unlock_irqrestore(&dev->event_lock, flags); } } EXPORT_SYMBOL(input_mt_drop_unused); /** + * input_mt_release_slots() - Deactivate all slots + * @dev: input device with allocated MT slots + * + * Lift all active slots. + */ +void input_mt_release_slots(struct input_dev *dev) +{ + struct input_mt *mt = dev->mt; + + lockdep_assert_held(&dev->event_lock); + + if (mt) { + /* This will effectively mark all slots unused. */ + mt->frame++; + + __input_mt_drop_unused(dev, mt); + + if (test_bit(ABS_PRESSURE, dev->absbit)) + input_handle_event(dev, EV_ABS, ABS_PRESSURE, 0); + + mt->frame++; + } +} + +/** * input_mt_sync_frame() - synchronize mt frame * @dev: input device with allocated MT slots * @@ -300,8 +335,13 @@ void input_mt_sync_frame(struct input_dev *dev) if (!mt) return; - if (mt->flags & INPUT_MT_DROP_UNUSED) + if (mt->flags & INPUT_MT_DROP_UNUSED) { + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); __input_mt_drop_unused(dev, mt); + spin_unlock_irqrestore(&dev->event_lock, flags); + } if ((mt->flags & INPUT_MT_POINTER) && !(mt->flags & INPUT_MT_SEMI_MT)) use_count = true; diff --git a/drivers/input/input.c b/drivers/input/input.c index 1365c9dfb5f2..ebb2b7f0f8ff 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -24,6 +24,7 @@ #include <linux/mutex.h> #include <linux/rcupdate.h> #include "input-compat.h" +#include "input-core-private.h" #include "input-poller.h" MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); @@ -142,6 +143,8 @@ static void input_pass_values(struct input_dev *dev, struct input_handle *handle; struct input_value *v; + lockdep_assert_held(&dev->event_lock); + if (!count) return; @@ -174,44 +177,6 @@ static void input_pass_values(struct input_dev *dev, } } -static void input_pass_event(struct input_dev *dev, - unsigned int type, unsigned int code, int value) -{ - struct input_value vals[] = { { type, code, value } }; - - input_pass_values(dev, vals, ARRAY_SIZE(vals)); -} - -/* - * Generate software autorepeat event. Note that we take - * dev->event_lock here to avoid racing with input_event - * which may cause keys get "stuck". - */ -static void input_repeat_key(struct timer_list *t) -{ - struct input_dev *dev = from_timer(dev, t, timer); - unsigned long flags; - - spin_lock_irqsave(&dev->event_lock, flags); - - if (test_bit(dev->repeat_key, dev->key) && - is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) { - struct input_value vals[] = { - { EV_KEY, dev->repeat_key, 2 }, - input_value_sync - }; - - input_set_timestamp(dev, ktime_get()); - input_pass_values(dev, vals, ARRAY_SIZE(vals)); - - if (dev->rep[REP_PERIOD]) - mod_timer(&dev->timer, jiffies + - msecs_to_jiffies(dev->rep[REP_PERIOD])); - } - - spin_unlock_irqrestore(&dev->event_lock, flags); -} - #define INPUT_IGNORE_EVENT 0 #define INPUT_PASS_TO_HANDLERS 1 #define INPUT_PASS_TO_DEVICE 2 @@ -275,6 +240,10 @@ static int input_get_disposition(struct input_dev *dev, int disposition = INPUT_IGNORE_EVENT; int value = *pval; + /* filter-out events from inhibited devices */ + if (dev->inhibited) + return INPUT_IGNORE_EVENT; + switch (type) { case EV_SYN: @@ -375,19 +344,9 @@ static int input_get_disposition(struct input_dev *dev, return disposition; } -static void input_handle_event(struct input_dev *dev, - unsigned int type, unsigned int code, int value) +static void input_event_dispose(struct input_dev *dev, int disposition, + unsigned int type, unsigned int code, int value) { - int disposition; - - /* filter-out events from inhibited devices */ - if (dev->inhibited) - return; - - disposition = input_get_disposition(dev, type, code, &value); - if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN) - add_input_randomness(type, code, value); - if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) dev->event(dev, type, code, value); @@ -426,7 +385,22 @@ static void input_handle_event(struct input_dev *dev, input_pass_values(dev, dev->vals, dev->num_vals); dev->num_vals = 0; } +} +void input_handle_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) +{ + int disposition; + + lockdep_assert_held(&dev->event_lock); + + disposition = input_get_disposition(dev, type, code, &value); + if (disposition != INPUT_IGNORE_EVENT) { + if (type != EV_SYN) + add_input_randomness(type, code, value); + + input_event_dispose(dev, disposition, type, code, value); + } } /** @@ -613,7 +587,7 @@ static void __input_release_device(struct input_handle *handle) lockdep_is_held(&dev->mutex)); if (grabber == handle) { rcu_assign_pointer(dev->grab, NULL); - /* Make sure input_pass_event() notices that grab is gone */ + /* Make sure input_pass_values() notices that grab is gone */ synchronize_rcu(); list_for_each_entry(handle, &dev->h_list, d_node) @@ -736,7 +710,7 @@ void input_close_device(struct input_handle *handle) if (!--handle->open) { /* - * synchronize_rcu() makes sure that input_pass_event() + * synchronize_rcu() makes sure that input_pass_values() * completed and that no more input events are delivered * through this handle */ @@ -751,22 +725,21 @@ EXPORT_SYMBOL(input_close_device); * Simulate keyup events for all keys that are marked as pressed. * The function must be called with dev->event_lock held. */ -static void input_dev_release_keys(struct input_dev *dev) +static bool input_dev_release_keys(struct input_dev *dev) { bool need_sync = false; int code; + lockdep_assert_held(&dev->event_lock); + if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { for_each_set_bit(code, dev->key, KEY_CNT) { - input_pass_event(dev, EV_KEY, code, 0); + input_handle_event(dev, EV_KEY, code, 0); need_sync = true; } - - if (need_sync) - input_pass_event(dev, EV_SYN, SYN_REPORT, 1); - - memset(dev->key, 0, sizeof(dev->key)); } + + return need_sync; } /* @@ -793,7 +766,8 @@ static void input_disconnect_device(struct input_dev *dev) * generate events even after we done here but they will not * reach any handlers. */ - input_dev_release_keys(dev); + if (input_dev_release_keys(dev)) + input_handle_event(dev, EV_SYN, SYN_REPORT, 1); list_for_each_entry(handle, &dev->h_list, d_node) handle->open = 0; @@ -1004,12 +978,16 @@ int input_set_keycode(struct input_dev *dev, } else if (test_bit(EV_KEY, dev->evbit) && !is_event_supported(old_keycode, dev->keybit, KEY_MAX) && __test_and_clear_bit(old_keycode, dev->key)) { - struct input_value vals[] = { - { EV_KEY, old_keycode, 0 }, - input_value_sync - }; - - input_pass_values(dev, vals, ARRAY_SIZE(vals)); + /* + * We have to use input_event_dispose() here directly instead + * of input_handle_event() because the key we want to release + * here is considered no longer supported by the device and + * input_handle_event() will ignore it. + */ + input_event_dispose(dev, INPUT_PASS_TO_HANDLERS, + EV_KEY, old_keycode, 0); + input_event_dispose(dev, INPUT_PASS_TO_HANDLERS | INPUT_FLUSH, + EV_SYN, SYN_REPORT, 1); } out: @@ -1784,7 +1762,8 @@ void input_reset_device(struct input_dev *dev) spin_lock_irqsave(&dev->event_lock, flags); input_dev_toggle(dev, true); - input_dev_release_keys(dev); + if (input_dev_release_keys(dev)) + input_handle_event(dev, EV_SYN, SYN_REPORT, 1); spin_unlock_irqrestore(&dev->event_lock, flags); mutex_unlock(&dev->mutex); @@ -1806,7 +1785,9 @@ static int input_inhibit_device(struct input_dev *dev) } spin_lock_irq(&dev->event_lock); + input_mt_release_slots(dev); input_dev_release_keys(dev); + input_handle_event(dev, EV_SYN, SYN_REPORT, 1); input_dev_toggle(dev, false); spin_unlock_irq(&dev->event_lock); @@ -1857,7 +1838,8 @@ static int input_dev_suspend(struct device *dev) * Keys that are pressed now are unlikely to be * still pressed when we resume. */ - input_dev_release_keys(input_dev); + if (input_dev_release_keys(input_dev)) + input_handle_event(input_dev, EV_SYN, SYN_REPORT, 1); /* Turn off LEDs and sounds, if any are active. */ input_dev_toggle(input_dev, false); @@ -1891,7 +1873,8 @@ static int input_dev_freeze(struct device *dev) * Keys that are pressed now are unlikely to be * still pressed when we resume. */ - input_dev_release_keys(input_dev); + if (input_dev_release_keys(input_dev)) + input_handle_event(input_dev, EV_SYN, SYN_REPORT, 1); spin_unlock_irq(&input_dev->event_lock); @@ -2259,6 +2242,34 @@ static void devm_input_device_unregister(struct device *dev, void *res) __input_unregister_device(input); } +/* + * Generate software autorepeat event. Note that we take + * dev->event_lock here to avoid racing with input_event + * which may cause keys get "stuck". + */ +static void input_repeat_key(struct timer_list *t) +{ + struct input_dev *dev = from_timer(dev, t, timer); + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + + if (!dev->inhibited && + test_bit(dev->repeat_key, dev->key) && + is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) { + + input_set_timestamp(dev, ktime_get()); + input_handle_event(dev, EV_KEY, dev->repeat_key, 2); + input_handle_event(dev, EV_SYN, SYN_REPORT, 1); + + if (dev->rep[REP_PERIOD]) + mod_timer(&dev->timer, jiffies + + msecs_to_jiffies(dev->rep[REP_PERIOD])); + } + + spin_unlock_irqrestore(&dev->event_lock, flags); +} + /** * input_enable_softrepeat - enable software autorepeat * @dev: input device diff --git a/drivers/input/joystick/adc-joystick.c b/drivers/input/joystick/adc-joystick.c index 78ebca7d400a..e0cfdc84763f 100644 --- a/drivers/input/joystick/adc-joystick.c +++ b/drivers/input/joystick/adc-joystick.c @@ -222,13 +222,6 @@ static int adc_joystick_probe(struct platform_device *pdev) if (error) return error; - input_set_drvdata(input, joy); - error = input_register_device(input); - if (error) { - dev_err(dev, "Unable to register input device\n"); - return error; - } - joy->buffer = iio_channel_get_all_cb(dev, adc_joystick_handle, joy); if (IS_ERR(joy->buffer)) { dev_err(dev, "Unable to allocate callback buffer\n"); @@ -241,6 +234,14 @@ static int adc_joystick_probe(struct platform_device *pdev) return error; } + input_set_drvdata(input, joy); + + error = input_register_device(input); + if (error) { + dev_err(dev, "Unable to register input device\n"); + return error; + } + return 0; } diff --git a/drivers/input/joystick/sensehat-joystick.c b/drivers/input/joystick/sensehat-joystick.c index 5ad1fe4ff496..a84df39d3b2f 100644 --- a/drivers/input/joystick/sensehat-joystick.c +++ b/drivers/input/joystick/sensehat-joystick.c @@ -98,10 +98,8 @@ static int sensehat_joystick_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "Could not retrieve interrupt request"); + if (irq < 0) return irq; - } error = devm_request_threaded_irq(&pdev->dev, irq, NULL, sensehat_joystick_report, diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 4ea79db8f134..a20ee693b22b 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -795,7 +795,7 @@ config KEYBOARD_MT6779 config KEYBOARD_MTK_PMIC tristate "MediaTek PMIC keys support" - depends on MFD_MT6397 + depends on MFD_MT6397 || COMPILE_TEST help Say Y here if you want to use the pmic keys (powerkey/homekey). diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index 1592da4de336..1a1a05d7cd42 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -8,17 +8,19 @@ * Copyright (C) 2008-2010 Analog Devices Inc. */ -#include <linux/module.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/i2c.h> +#include <linux/input.h> #include <linux/interrupt.h> #include <linux/irq.h> -#include <linux/workqueue.h> -#include <linux/errno.h> -#include <linux/pm.h> +#include <linux/ktime.h> +#include <linux/module.h> #include <linux/platform_device.h> -#include <linux/input.h> -#include <linux/i2c.h> -#include <linux/gpio/driver.h> +#include <linux/pm.h> #include <linux/slab.h> +#include <linux/timekeeping.h> #include <linux/platform_data/adp5588.h> @@ -36,18 +38,18 @@ * asserted. */ #define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4) +#define WA_DELAYED_READOUT_TIME 25 struct adp5588_kpad { struct i2c_client *client; struct input_dev *input; - struct delayed_work work; + ktime_t irq_time; unsigned long delay; unsigned short keycode[ADP5588_KEYMAPSIZE]; const struct adp5588_gpi_map *gpimap; unsigned short gpimapsize; #ifdef CONFIG_GPIOLIB unsigned char gpiomap[ADP5588_MAXGPIO]; - bool export_gpio; struct gpio_chip gc; struct mutex gpio_lock; /* Protect cached dir, dat_out */ u8 dat_out[3]; @@ -179,6 +181,21 @@ static int adp5588_build_gpiomap(struct adp5588_kpad *kpad, return n_unused; } +static void adp5588_gpio_do_teardown(void *_kpad) +{ + struct adp5588_kpad *kpad = _kpad; + struct device *dev = &kpad->client->dev; + const struct adp5588_kpad_platform_data *pdata = dev_get_platdata(dev); + const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; + int error; + + error = gpio_data->teardown(kpad->client, + kpad->gc.base, kpad->gc.ngpio, + gpio_data->context); + if (error) + dev_warn(&kpad->client->dev, "teardown failed %d\n", error); +} + static int adp5588_gpio_add(struct adp5588_kpad *kpad) { struct device *dev = &kpad->client->dev; @@ -195,8 +212,6 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad) return 0; } - kpad->export_gpio = true; - kpad->gc.direction_input = adp5588_gpio_direction_input; kpad->gc.direction_output = adp5588_gpio_direction_output; kpad->gc.get = adp5588_gpio_get_value; @@ -210,9 +225,9 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad) mutex_init(&kpad->gpio_lock); - error = gpiochip_add_data(&kpad->gc, kpad); + error = devm_gpiochip_add_data(dev, &kpad->gc, kpad); if (error) { - dev_err(dev, "gpiochip_add failed, err: %d\n", error); + dev_err(dev, "gpiochip_add failed: %d\n", error); return error; } @@ -227,41 +242,24 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad) kpad->gc.base, kpad->gc.ngpio, gpio_data->context); if (error) - dev_warn(dev, "setup failed, %d\n", error); + dev_warn(dev, "setup failed: %d\n", error); } - return 0; -} - -static void adp5588_gpio_remove(struct adp5588_kpad *kpad) -{ - struct device *dev = &kpad->client->dev; - const struct adp5588_kpad_platform_data *pdata = dev_get_platdata(dev); - const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; - int error; - - if (!kpad->export_gpio) - return; - if (gpio_data->teardown) { - error = gpio_data->teardown(kpad->client, - kpad->gc.base, kpad->gc.ngpio, - gpio_data->context); + error = devm_add_action(dev, adp5588_gpio_do_teardown, kpad); if (error) - dev_warn(dev, "teardown failed %d\n", error); + dev_warn(dev, "failed to schedule teardown: %d\n", + error); } - gpiochip_remove(&kpad->gc); + return 0; } + #else static inline int adp5588_gpio_add(struct adp5588_kpad *kpad) { return 0; } - -static inline void adp5588_gpio_remove(struct adp5588_kpad *kpad) -{ -} #endif static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt) @@ -289,13 +287,36 @@ static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt) } } -static void adp5588_work(struct work_struct *work) +static irqreturn_t adp5588_hard_irq(int irq, void *handle) { - struct adp5588_kpad *kpad = container_of(work, - struct adp5588_kpad, work.work); + struct adp5588_kpad *kpad = handle; + + kpad->irq_time = ktime_get(); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t adp5588_thread_irq(int irq, void *handle) +{ + struct adp5588_kpad *kpad = handle; struct i2c_client *client = kpad->client; + ktime_t target_time, now; + unsigned long delay; int status, ev_cnt; + /* + * Readout needs to wait for at least 25ms after the notification + * for REVID < 4. + */ + if (kpad->delay) { + target_time = ktime_add_ms(kpad->irq_time, kpad->delay); + now = ktime_get(); + if (ktime_before(now, target_time)) { + delay = ktime_to_us(ktime_sub(target_time, now)); + usleep_range(delay, delay + 1000); + } + } + status = adp5588_read(client, INT_STAT); if (status & ADP5588_OVR_FLOW_INT) /* Unlikely and should never happen */ @@ -308,20 +329,8 @@ static void adp5588_work(struct work_struct *work) input_sync(kpad->input); } } - adp5588_write(client, INT_STAT, status); /* Status is W1C */ -} - -static irqreturn_t adp5588_irq(int irq, void *handle) -{ - struct adp5588_kpad *kpad = handle; - /* - * use keventd context to read the event fifo registers - * Schedule readout at least 25ms after notification for - * REVID < 4 - */ - - schedule_delayed_work(&kpad->work, kpad->delay); + adp5588_write(client, INT_STAT, status); /* Status is W1C */ return IRQ_HANDLED; } @@ -496,30 +505,27 @@ static int adp5588_probe(struct i2c_client *client, return -EINVAL; } - kpad = kzalloc(sizeof(*kpad), GFP_KERNEL); - input = input_allocate_device(); - if (!kpad || !input) { - error = -ENOMEM; - goto err_free_mem; - } + kpad = devm_kzalloc(&client->dev, sizeof(*kpad), GFP_KERNEL); + if (!kpad) + return -ENOMEM; + + input = devm_input_allocate_device(&client->dev); + if (!input) + return -ENOMEM; kpad->client = client; kpad->input = input; - INIT_DELAYED_WORK(&kpad->work, adp5588_work); ret = adp5588_read(client, DEV_ID); - if (ret < 0) { - error = ret; - goto err_free_mem; - } + if (ret < 0) + return ret; revid = (u8) ret & ADP5588_DEVICE_ID_MASK; if (WA_DELAYED_READOUT_REVID(revid)) - kpad->delay = msecs_to_jiffies(30); + kpad->delay = msecs_to_jiffies(WA_DELAYED_READOUT_TIME); input->name = client->name; input->phys = "adp5588-keys/input0"; - input->dev.parent = &client->dev; input_set_drvdata(input, kpad); @@ -556,95 +562,63 @@ static int adp5588_probe(struct i2c_client *client, error = input_register_device(input); if (error) { - dev_err(&client->dev, "unable to register input device\n"); - goto err_free_mem; + dev_err(&client->dev, "unable to register input device: %d\n", + error); + return error; } - error = request_irq(client->irq, adp5588_irq, - IRQF_TRIGGER_FALLING, - client->dev.driver->name, kpad); + error = devm_request_threaded_irq(&client->dev, client->irq, + adp5588_hard_irq, adp5588_thread_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->dev.driver->name, kpad); if (error) { - dev_err(&client->dev, "irq %d busy?\n", client->irq); - goto err_unreg_dev; + dev_err(&client->dev, "failed to request irq %d: %d\n", + client->irq, error); + return error; } error = adp5588_setup(client); if (error) - goto err_free_irq; + return error; if (kpad->gpimapsize) adp5588_report_switch_state(kpad); error = adp5588_gpio_add(kpad); if (error) - goto err_free_irq; - - device_init_wakeup(&client->dev, 1); - i2c_set_clientdata(client, kpad); + return error; dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq); return 0; - - err_free_irq: - free_irq(client->irq, kpad); - cancel_delayed_work_sync(&kpad->work); - err_unreg_dev: - input_unregister_device(input); - input = NULL; - err_free_mem: - input_free_device(input); - kfree(kpad); - - return error; } static int adp5588_remove(struct i2c_client *client) { - struct adp5588_kpad *kpad = i2c_get_clientdata(client); - adp5588_write(client, CFG, 0); - free_irq(client->irq, kpad); - cancel_delayed_work_sync(&kpad->work); - input_unregister_device(kpad->input); - adp5588_gpio_remove(kpad); - kfree(kpad); + /* all resources will be freed by devm */ return 0; } -#ifdef CONFIG_PM -static int adp5588_suspend(struct device *dev) +static int __maybe_unused adp5588_suspend(struct device *dev) { - struct adp5588_kpad *kpad = dev_get_drvdata(dev); - struct i2c_client *client = kpad->client; + struct i2c_client *client = to_i2c_client(dev); disable_irq(client->irq); - cancel_delayed_work_sync(&kpad->work); - - if (device_may_wakeup(&client->dev)) - enable_irq_wake(client->irq); return 0; } -static int adp5588_resume(struct device *dev) +static int __maybe_unused adp5588_resume(struct device *dev) { - struct adp5588_kpad *kpad = dev_get_drvdata(dev); - struct i2c_client *client = kpad->client; - - if (device_may_wakeup(&client->dev)) - disable_irq_wake(client->irq); + struct i2c_client *client = to_i2c_client(dev); enable_irq(client->irq); return 0; } -static const struct dev_pm_ops adp5588_dev_pm_ops = { - .suspend = adp5588_suspend, - .resume = adp5588_resume, -}; -#endif +static SIMPLE_DEV_PM_OPS(adp5588_dev_pm_ops, adp5588_suspend, adp5588_resume); static const struct i2c_device_id adp5588_id[] = { { "adp5588-keys", 0 }, @@ -656,9 +630,7 @@ MODULE_DEVICE_TABLE(i2c, adp5588_id); static struct i2c_driver adp5588_driver = { .driver = { .name = KBUILD_MODNAME, -#ifdef CONFIG_PM .pm = &adp5588_dev_pm_ops, -#endif }, .probe = adp5588_probe, .remove = adp5588_remove, diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index cc73a149da28..c14136b733a9 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -12,6 +12,7 @@ // expensive. #include <linux/module.h> +#include <linux/acpi.h> #include <linux/bitops.h> #include <linux/i2c.h> #include <linux/input.h> @@ -518,6 +519,50 @@ static int cros_ec_keyb_register_bs(struct cros_ec_keyb *ckdev, return 0; } +static void cros_ec_keyb_parse_vivaldi_physmap(struct cros_ec_keyb *ckdev) +{ + u32 *physmap = ckdev->vdata.function_row_physmap; + unsigned int row, col, scancode; + int n_physmap; + int error; + int i; + + n_physmap = device_property_count_u32(ckdev->dev, + "function-row-physmap"); + if (n_physmap <= 0) + return; + + if (n_physmap >= VIVALDI_MAX_FUNCTION_ROW_KEYS) { + dev_warn(ckdev->dev, + "only up to %d top row keys is supported (%d specified)\n", + VIVALDI_MAX_FUNCTION_ROW_KEYS, n_physmap); + n_physmap = VIVALDI_MAX_FUNCTION_ROW_KEYS; + } + + error = device_property_read_u32_array(ckdev->dev, + "function-row-physmap", + physmap, n_physmap); + if (error) { + dev_warn(ckdev->dev, + "failed to parse function-row-physmap property: %d\n", + error); + return; + } + + /* + * Convert (in place) from row/column encoding to matrix "scancode" + * used by the driver. + */ + for (i = 0; i < n_physmap; i++) { + row = KEY_ROW(physmap[i]); + col = KEY_COL(physmap[i]); + scancode = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); + physmap[i] = scancode; + } + + ckdev->vdata.num_function_row_keys = n_physmap; +} + /** * cros_ec_keyb_register_matrix - Register matrix keys * @@ -534,11 +579,6 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) struct input_dev *idev; const char *phys; int err; - struct property *prop; - const __be32 *p; - u32 *physmap; - u32 key_pos; - unsigned int row, col, scancode, n_physmap; err = matrix_keypad_parse_properties(dev, &ckdev->rows, &ckdev->cols); if (err) @@ -573,7 +613,7 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) idev->id.product = 0; idev->dev.parent = dev; - ckdev->ghost_filter = of_property_read_bool(dev->of_node, + ckdev->ghost_filter = device_property_read_bool(dev, "google,needs-ghost-filter"); err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows, ckdev->cols, @@ -589,22 +629,7 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) input_set_drvdata(idev, ckdev); ckdev->idev = idev; cros_ec_keyb_compute_valid_keys(ckdev); - - physmap = ckdev->vdata.function_row_physmap; - n_physmap = 0; - of_property_for_each_u32(dev->of_node, "function-row-physmap", - prop, p, key_pos) { - if (n_physmap == VIVALDI_MAX_FUNCTION_ROW_KEYS) { - dev_warn(dev, "Only support up to %d top row keys\n", - VIVALDI_MAX_FUNCTION_ROW_KEYS); - break; - } - row = KEY_ROW(key_pos); - col = KEY_COL(key_pos); - scancode = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); - physmap[n_physmap++] = scancode; - } - ckdev->vdata.num_function_row_keys = n_physmap; + cros_ec_keyb_parse_vivaldi_physmap(ckdev); err = input_register_device(ckdev->idev); if (err) { @@ -653,14 +678,19 @@ static const struct attribute_group cros_ec_keyb_attr_group = { static int cros_ec_keyb_probe(struct platform_device *pdev) { - struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); + struct cros_ec_device *ec; struct device *dev = &pdev->dev; struct cros_ec_keyb *ckdev; bool buttons_switches_only = device_get_match_data(dev); int err; - if (!dev->of_node) - return -ENODEV; + /* + * If the parent ec device has not been probed yet, defer the probe of + * this keyboard/button driver until later. + */ + ec = dev_get_drvdata(pdev->dev.parent); + if (!ec) + return -EPROBE_DEFER; ckdev = devm_kzalloc(dev, sizeof(*ckdev), GFP_KERNEL); if (!ckdev) @@ -713,6 +743,14 @@ static int cros_ec_keyb_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_ACPI +static const struct acpi_device_id cros_ec_keyb_acpi_match[] = { + { "GOOG0007", true }, + { } +}; +MODULE_DEVICE_TABLE(acpi, cros_ec_keyb_acpi_match); +#endif + #ifdef CONFIG_OF static const struct of_device_id cros_ec_keyb_of_match[] = { { .compatible = "google,cros-ec-keyb" }, @@ -730,6 +768,7 @@ static struct platform_driver cros_ec_keyb_driver = { .driver = { .name = "cros-ec-keyb", .of_match_table = of_match_ptr(cros_ec_keyb_of_match), + .acpi_match_table = ACPI_PTR(cros_ec_keyb_acpi_match), .pm = &cros_ec_keyb_pm_ops, }, }; diff --git a/drivers/input/keyboard/mt6779-keypad.c b/drivers/input/keyboard/mt6779-keypad.c index 2e7c9187c10f..bf447bf598fb 100644 --- a/drivers/input/keyboard/mt6779-keypad.c +++ b/drivers/input/keyboard/mt6779-keypad.c @@ -17,6 +17,11 @@ #define MTK_KPD_DEBOUNCE 0x0018 #define MTK_KPD_DEBOUNCE_MASK GENMASK(13, 0) #define MTK_KPD_DEBOUNCE_MAX_MS 256 +#define MTK_KPD_SEL 0x0020 +#define MTK_KPD_SEL_COL GENMASK(15, 10) +#define MTK_KPD_SEL_ROW GENMASK(9, 4) +#define MTK_KPD_SEL_COLMASK(c) GENMASK((c) + 9, 10) +#define MTK_KPD_SEL_ROWMASK(r) GENMASK((r) + 3, 4) #define MTK_KPD_NUM_MEMS 5 #define MTK_KPD_NUM_BITS 136 /* 4*32+8 MEM5 only use 8 BITS */ @@ -42,7 +47,7 @@ static irqreturn_t mt6779_keypad_irq_handler(int irq, void *dev_id) const unsigned short *keycode = keypad->input_dev->keycode; DECLARE_BITMAP(new_state, MTK_KPD_NUM_BITS); DECLARE_BITMAP(change, MTK_KPD_NUM_BITS); - unsigned int bit_nr; + unsigned int bit_nr, key; unsigned int row, col; unsigned int scancode; unsigned int row_shift = get_count_order(keypad->n_cols); @@ -61,8 +66,10 @@ static irqreturn_t mt6779_keypad_irq_handler(int irq, void *dev_id) if (bit_nr % 32 >= 16) continue; - row = bit_nr / 32; - col = bit_nr % 32; + key = bit_nr / 32 * 16 + bit_nr % 32; + row = key / 9; + col = key % 9; + scancode = MATRIX_SCAN_CODE(row, col, row_shift); /* 1: not pressed, 0: pressed */ pressed = !test_bit(bit_nr, new_state); @@ -159,6 +166,11 @@ static int mt6779_keypad_pdrv_probe(struct platform_device *pdev) regmap_write(keypad->regmap, MTK_KPD_DEBOUNCE, (debounce * (1 << 5)) & MTK_KPD_DEBOUNCE_MASK); + regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_ROW, + MTK_KPD_SEL_ROWMASK(keypad->n_rows)); + regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_COL, + MTK_KPD_SEL_COLMASK(keypad->n_cols)); + keypad->clk = devm_clk_get(&pdev->dev, "kpd"); if (IS_ERR(keypad->clk)) return PTR_ERR(keypad->clk); diff --git a/drivers/input/keyboard/mtk-pmic-keys.c b/drivers/input/keyboard/mtk-pmic-keys.c index c31ab4368388..6404081253ea 100644 --- a/drivers/input/keyboard/mtk-pmic-keys.c +++ b/drivers/input/keyboard/mtk-pmic-keys.c @@ -18,17 +18,9 @@ #include <linux/platform_device.h> #include <linux/regmap.h> -#define MTK_PMIC_PWRKEY_RST_EN_MASK 0x1 -#define MTK_PMIC_PWRKEY_RST_EN_SHIFT 6 -#define MTK_PMIC_HOMEKEY_RST_EN_MASK 0x1 -#define MTK_PMIC_HOMEKEY_RST_EN_SHIFT 5 -#define MTK_PMIC_RST_DU_MASK 0x3 -#define MTK_PMIC_RST_DU_SHIFT 8 - -#define MTK_PMIC_PWRKEY_RST \ - (MTK_PMIC_PWRKEY_RST_EN_MASK << MTK_PMIC_PWRKEY_RST_EN_SHIFT) -#define MTK_PMIC_HOMEKEY_RST \ - (MTK_PMIC_HOMEKEY_RST_EN_MASK << MTK_PMIC_HOMEKEY_RST_EN_SHIFT) +#define MTK_PMIC_RST_DU_MASK GENMASK(9, 8) +#define MTK_PMIC_PWRKEY_RST BIT(6) +#define MTK_PMIC_HOMEKEY_RST BIT(5) #define MTK_PMIC_PWRKEY_INDEX 0 #define MTK_PMIC_HOMEKEY_INDEX 1 @@ -39,50 +31,58 @@ struct mtk_pmic_keys_regs { u32 deb_mask; u32 intsel_reg; u32 intsel_mask; + u32 rst_en_mask; }; #define MTK_PMIC_KEYS_REGS(_deb_reg, _deb_mask, \ - _intsel_reg, _intsel_mask) \ + _intsel_reg, _intsel_mask, _rst_mask) \ { \ .deb_reg = _deb_reg, \ .deb_mask = _deb_mask, \ .intsel_reg = _intsel_reg, \ .intsel_mask = _intsel_mask, \ + .rst_en_mask = _rst_mask, \ } struct mtk_pmic_regs { const struct mtk_pmic_keys_regs keys_regs[MTK_PMIC_MAX_KEY_COUNT]; u32 pmic_rst_reg; + u32 rst_lprst_mask; /* Long-press reset timeout bitmask */ }; static const struct mtk_pmic_regs mt6397_regs = { .keys_regs[MTK_PMIC_PWRKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6397_CHRSTATUS, - 0x8, MT6397_INT_RSV, 0x10), + 0x8, MT6397_INT_RSV, 0x10, MTK_PMIC_PWRKEY_RST), .keys_regs[MTK_PMIC_HOMEKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6397_OCSTATUS2, - 0x10, MT6397_INT_RSV, 0x8), + 0x10, MT6397_INT_RSV, 0x8, MTK_PMIC_HOMEKEY_RST), .pmic_rst_reg = MT6397_TOP_RST_MISC, + .rst_lprst_mask = MTK_PMIC_RST_DU_MASK, }; static const struct mtk_pmic_regs mt6323_regs = { .keys_regs[MTK_PMIC_PWRKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6323_CHRSTATUS, - 0x2, MT6323_INT_MISC_CON, 0x10), + 0x2, MT6323_INT_MISC_CON, 0x10, MTK_PMIC_PWRKEY_RST), .keys_regs[MTK_PMIC_HOMEKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6323_CHRSTATUS, - 0x4, MT6323_INT_MISC_CON, 0x8), + 0x4, MT6323_INT_MISC_CON, 0x8, MTK_PMIC_HOMEKEY_RST), .pmic_rst_reg = MT6323_TOP_RST_MISC, + .rst_lprst_mask = MTK_PMIC_RST_DU_MASK, }; static const struct mtk_pmic_regs mt6358_regs = { .keys_regs[MTK_PMIC_PWRKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6358_TOPSTATUS, - 0x2, MT6358_PSC_TOP_INT_CON0, 0x5), + 0x2, MT6358_PSC_TOP_INT_CON0, 0x5, + MTK_PMIC_PWRKEY_RST), .keys_regs[MTK_PMIC_HOMEKEY_INDEX] = MTK_PMIC_KEYS_REGS(MT6358_TOPSTATUS, - 0x8, MT6358_PSC_TOP_INT_CON0, 0xa), + 0x8, MT6358_PSC_TOP_INT_CON0, 0xa, + MTK_PMIC_HOMEKEY_RST), .pmic_rst_reg = MT6358_TOP_RST_MISC, + .rst_lprst_mask = MTK_PMIC_RST_DU_MASK, }; struct mtk_pmic_keys_info { @@ -108,53 +108,49 @@ enum mtk_pmic_keys_lp_mode { }; static void mtk_pmic_keys_lp_reset_setup(struct mtk_pmic_keys *keys, - u32 pmic_rst_reg) + const struct mtk_pmic_regs *regs) { - int ret; + const struct mtk_pmic_keys_regs *kregs_home, *kregs_pwr; u32 long_press_mode, long_press_debounce; + u32 value, mask; + int error; + + kregs_home = keys->keys[MTK_PMIC_HOMEKEY_INDEX].regs; + kregs_pwr = keys->keys[MTK_PMIC_PWRKEY_INDEX].regs; - ret = of_property_read_u32(keys->dev->of_node, - "power-off-time-sec", &long_press_debounce); - if (ret) + error = of_property_read_u32(keys->dev->of_node, "power-off-time-sec", + &long_press_debounce); + if (error) long_press_debounce = 0; - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_RST_DU_MASK << MTK_PMIC_RST_DU_SHIFT, - long_press_debounce << MTK_PMIC_RST_DU_SHIFT); + mask = regs->rst_lprst_mask; + value = long_press_debounce << (ffs(regs->rst_lprst_mask) - 1); - ret = of_property_read_u32(keys->dev->of_node, - "mediatek,long-press-mode", &long_press_mode); - if (ret) + error = of_property_read_u32(keys->dev->of_node, + "mediatek,long-press-mode", + &long_press_mode); + if (error) long_press_mode = LP_DISABLE; switch (long_press_mode) { - case LP_ONEKEY: - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_PWRKEY_RST, - MTK_PMIC_PWRKEY_RST); - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_HOMEKEY_RST, - 0); - break; case LP_TWOKEY: - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_PWRKEY_RST, - MTK_PMIC_PWRKEY_RST); - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_HOMEKEY_RST, - MTK_PMIC_HOMEKEY_RST); - break; + value |= kregs_home->rst_en_mask; + fallthrough; + + case LP_ONEKEY: + value |= kregs_pwr->rst_en_mask; + fallthrough; + case LP_DISABLE: - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_PWRKEY_RST, - 0); - regmap_update_bits(keys->regmap, pmic_rst_reg, - MTK_PMIC_HOMEKEY_RST, - 0); + mask |= kregs_home->rst_en_mask; + mask |= kregs_pwr->rst_en_mask; break; + default: break; } + + regmap_update_bits(keys->regmap, regs->pmic_rst_reg, mask, value); } static irqreturn_t mtk_pmic_keys_irq_handler_thread(int irq, void *data) @@ -358,7 +354,7 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev) return error; } - mtk_pmic_keys_lp_reset_setup(keys, mtk_pmic_regs->pmic_rst_reg); + mtk_pmic_keys_lp_reset_setup(keys, mtk_pmic_regs); platform_set_drvdata(pdev, keys); diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 8a7ce41b8c56..ee9d04a3f0d5 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -179,11 +179,9 @@ static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id) int error; u64 keys; - error = pm_runtime_get_sync(dev); - if (error < 0) { - pm_runtime_put_noidle(dev); + error = pm_runtime_resume_and_get(dev); + if (error) return IRQ_NONE; - } low = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0); high = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32); @@ -207,11 +205,9 @@ static int omap4_keypad_open(struct input_dev *input) struct device *dev = input->dev.parent; int error; - error = pm_runtime_get_sync(dev); - if (error < 0) { - pm_runtime_put_noidle(dev); + error = pm_runtime_resume_and_get(dev); + if (error) return error; - } disable_irq(keypad_data->irq); @@ -254,9 +250,10 @@ static void omap4_keypad_close(struct input_dev *input) struct device *dev = input->dev.parent; int error; - error = pm_runtime_get_sync(dev); - if (error < 0) - pm_runtime_put_noidle(dev); + error = pm_runtime_resume_and_get(dev); + if (error) + dev_err(dev, "%s: pm_runtime_resume_and_get() failed: %d\n", + __func__, error); disable_irq(keypad_data->irq); omap4_keypad_stop(keypad_data); @@ -392,10 +389,9 @@ static int omap4_keypad_probe(struct platform_device *pdev) * Enable clocks for the keypad module so that we can read * revision register. */ - error = pm_runtime_get_sync(dev); - if (error < 0) { - dev_err(dev, "pm_runtime_get_sync() failed\n"); - pm_runtime_put_noidle(dev); + error = pm_runtime_resume_and_get(dev); + if (error) { + dev_err(dev, "pm_runtime_resume_and_get() failed\n"); return error; } diff --git a/drivers/input/misc/iqs7222.c b/drivers/input/misc/iqs7222.c index 6b4138771a3f..b2e8097a2e6d 100644 --- a/drivers/input/misc/iqs7222.c +++ b/drivers/input/misc/iqs7222.c @@ -40,7 +40,6 @@ #define IQS7222_SLDR_SETUP_2_RES_MASK GENMASK(15, 8) #define IQS7222_SLDR_SETUP_2_RES_SHIFT 8 #define IQS7222_SLDR_SETUP_2_TOP_SPEED_MASK GENMASK(7, 0) -#define IQS7222_SLDR_SETUP_3_CHAN_SEL_MASK GENMASK(9, 0) #define IQS7222_GPIO_SETUP_0_GPIO_EN BIT(0) @@ -54,6 +53,9 @@ #define IQS7222_SYS_SETUP_ACK_RESET BIT(0) #define IQS7222_EVENT_MASK_ATI BIT(12) +#define IQS7222_EVENT_MASK_SLDR BIT(10) +#define IQS7222_EVENT_MASK_TOUCH BIT(1) +#define IQS7222_EVENT_MASK_PROX BIT(0) #define IQS7222_COMMS_HOLD BIT(0) #define IQS7222_COMMS_ERROR 0xEEEE @@ -92,11 +94,11 @@ enum iqs7222_reg_key_id { enum iqs7222_reg_grp_id { IQS7222_REG_GRP_STAT, + IQS7222_REG_GRP_FILT, IQS7222_REG_GRP_CYCLE, IQS7222_REG_GRP_GLBL, IQS7222_REG_GRP_BTN, IQS7222_REG_GRP_CHAN, - IQS7222_REG_GRP_FILT, IQS7222_REG_GRP_SLDR, IQS7222_REG_GRP_GPIO, IQS7222_REG_GRP_SYS, @@ -135,12 +137,12 @@ struct iqs7222_event_desc { static const struct iqs7222_event_desc iqs7222_kp_events[] = { { .name = "event-prox", - .enable = BIT(0), + .enable = IQS7222_EVENT_MASK_PROX, .reg_key = IQS7222_REG_KEY_PROX, }, { .name = "event-touch", - .enable = BIT(1), + .enable = IQS7222_EVENT_MASK_TOUCH, .reg_key = IQS7222_REG_KEY_TOUCH, }, }; @@ -556,13 +558,6 @@ static const struct iqs7222_prop_desc iqs7222_props[] = { .label = "current reference trim", }, { - .name = "azoteq,rf-filt-enable", - .reg_grp = IQS7222_REG_GRP_GLBL, - .reg_offset = 0, - .reg_shift = 15, - .reg_width = 1, - }, - { .name = "azoteq,max-counts", .reg_grp = IQS7222_REG_GRP_GLBL, .reg_offset = 0, @@ -1272,9 +1267,22 @@ static int iqs7222_ati_trigger(struct iqs7222_private *iqs7222) struct i2c_client *client = iqs7222->client; ktime_t ati_timeout; u16 sys_status = 0; - u16 sys_setup = iqs7222->sys_setup[0] & ~IQS7222_SYS_SETUP_ACK_RESET; + u16 sys_setup; int error, i; + /* + * The reserved fields of the system setup register may have changed + * as a result of other registers having been written. As such, read + * the register's latest value to avoid unexpected behavior when the + * register is written in the loop that follows. + */ + error = iqs7222_read_word(iqs7222, IQS7222_SYS_SETUP, &sys_setup); + if (error) + return error; + + sys_setup &= ~IQS7222_SYS_SETUP_INTF_MODE_MASK; + sys_setup &= ~IQS7222_SYS_SETUP_PWR_MODE_MASK; + for (i = 0; i < IQS7222_NUM_RETRIES; i++) { /* * Trigger ATI from streaming and normal-power modes so that @@ -1299,12 +1307,15 @@ static int iqs7222_ati_trigger(struct iqs7222_private *iqs7222) if (error) return error; - if (sys_status & IQS7222_SYS_STATUS_ATI_ACTIVE) - continue; + if (sys_status & IQS7222_SYS_STATUS_RESET) + return 0; if (sys_status & IQS7222_SYS_STATUS_ATI_ERROR) break; + if (sys_status & IQS7222_SYS_STATUS_ATI_ACTIVE) + continue; + /* * Use stream-in-touch mode if either slider reports * absolute position. @@ -1321,7 +1332,7 @@ static int iqs7222_ati_trigger(struct iqs7222_private *iqs7222) dev_err(&client->dev, "ATI attempt %d of %d failed with status 0x%02X, %s\n", i + 1, IQS7222_NUM_RETRIES, (u8)sys_status, - i < IQS7222_NUM_RETRIES ? "retrying..." : "stopping"); + i + 1 < IQS7222_NUM_RETRIES ? "retrying" : "stopping"); } return -ETIMEDOUT; @@ -1334,6 +1345,34 @@ static int iqs7222_dev_init(struct iqs7222_private *iqs7222, int dir) int error, i, j, k; /* + * Acknowledge reset before writing any registers in case the device + * suffers a spurious reset during initialization. Because this step + * may change the reserved fields of the second filter beta register, + * its cache must be updated. + * + * Writing the second filter beta register, in turn, may clobber the + * system status register. As such, the filter beta register pair is + * written first to protect against this hazard. + */ + if (dir == WRITE) { + u16 reg = dev_desc->reg_grps[IQS7222_REG_GRP_FILT].base + 1; + u16 filt_setup; + + error = iqs7222_write_word(iqs7222, IQS7222_SYS_SETUP, + iqs7222->sys_setup[0] | + IQS7222_SYS_SETUP_ACK_RESET); + if (error) + return error; + + error = iqs7222_read_word(iqs7222, reg, &filt_setup); + if (error) + return error; + + iqs7222->filt_setup[1] &= GENMASK(7, 0); + iqs7222->filt_setup[1] |= (filt_setup & ~GENMASK(7, 0)); + } + + /* * Take advantage of the stop-bit disable function, if available, to * save the trouble of having to reopen a communication window after * each burst read or write. @@ -1957,8 +1996,8 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index) int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row; int ext_chan = rounddown(num_chan, 10); int count, error, reg_offset, i; + u16 *event_mask = &iqs7222->sys_setup[dev_desc->event_offset]; u16 *sldr_setup = iqs7222->sldr_setup[sldr_index]; - u16 *sys_setup = iqs7222->sys_setup; unsigned int chan_sel[4], val; error = iqs7222_parse_props(iqs7222, &sldr_node, sldr_index, @@ -2003,7 +2042,7 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index) reg_offset = dev_desc->sldr_res < U16_MAX ? 0 : 1; sldr_setup[0] |= count; - sldr_setup[3 + reg_offset] &= ~IQS7222_SLDR_SETUP_3_CHAN_SEL_MASK; + sldr_setup[3 + reg_offset] &= ~GENMASK(ext_chan - 1, 0); for (i = 0; i < ARRAY_SIZE(chan_sel); i++) { sldr_setup[5 + reg_offset + i] = 0; @@ -2081,17 +2120,19 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index) sldr_setup[0] |= dev_desc->wheel_enable; } + /* + * The absence of a register offset makes it safe to assume the device + * supports gestures, each of which is first disabled until explicitly + * enabled. + */ + if (!reg_offset) + for (i = 0; i < ARRAY_SIZE(iqs7222_sl_events); i++) + sldr_setup[9] &= ~iqs7222_sl_events[i].enable; + for (i = 0; i < ARRAY_SIZE(iqs7222_sl_events); i++) { const char *event_name = iqs7222_sl_events[i].name; struct fwnode_handle *event_node; - /* - * The absence of a register offset means the remaining fields - * in the group represent gesture settings. - */ - if (iqs7222_sl_events[i].enable && !reg_offset) - sldr_setup[9] &= ~iqs7222_sl_events[i].enable; - event_node = fwnode_get_named_child_node(sldr_node, event_name); if (!event_node) continue; @@ -2104,6 +2145,22 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index) if (error) return error; + /* + * The press/release event does not expose a direct GPIO link, + * but one can be emulated by tying each of the participating + * channels to the same GPIO. + */ + error = iqs7222_gpio_select(iqs7222, event_node, + i ? iqs7222_sl_events[i].enable + : sldr_setup[3 + reg_offset], + i ? 1568 + sldr_index * 30 + : sldr_setup[4 + reg_offset]); + if (error) + return error; + + if (!reg_offset) + sldr_setup[9] |= iqs7222_sl_events[i].enable; + error = fwnode_property_read_u32(event_node, "linux,code", &val); if (error) { @@ -2115,26 +2172,20 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index) iqs7222->sl_code[sldr_index][i] = val; input_set_capability(iqs7222->keypad, EV_KEY, val); - /* - * The press/release event is determined based on whether the - * coordinate field reports 0xFFFF and has no explicit enable - * control. - */ - if (!iqs7222_sl_events[i].enable || reg_offset) - continue; - - sldr_setup[9] |= iqs7222_sl_events[i].enable; - - error = iqs7222_gpio_select(iqs7222, event_node, - iqs7222_sl_events[i].enable, - 1568 + sldr_index * 30); - if (error) - return error; - if (!dev_desc->event_offset) continue; - sys_setup[dev_desc->event_offset] |= BIT(10 + sldr_index); + /* + * The press/release event is determined based on whether the + * coordinate field reports 0xFFFF and solely relies on touch + * or proximity interrupts to be unmasked. + */ + if (i && !reg_offset) + *event_mask |= (IQS7222_EVENT_MASK_SLDR << sldr_index); + else if (sldr_setup[4 + reg_offset] == dev_desc->touch_link) + *event_mask |= IQS7222_EVENT_MASK_TOUCH; + else + *event_mask |= IQS7222_EVENT_MASK_PROX; } /* @@ -2227,11 +2278,6 @@ static int iqs7222_parse_all(struct iqs7222_private *iqs7222) return error; } - sys_setup[0] &= ~IQS7222_SYS_SETUP_INTF_MODE_MASK; - sys_setup[0] &= ~IQS7222_SYS_SETUP_PWR_MODE_MASK; - - sys_setup[0] |= IQS7222_SYS_SETUP_ACK_RESET; - return iqs7222_parse_props(iqs7222, NULL, 0, IQS7222_REG_GRP_SYS, IQS7222_REG_KEY_NONE); } @@ -2299,29 +2345,37 @@ static int iqs7222_report(struct iqs7222_private *iqs7222) input_report_abs(iqs7222->keypad, iqs7222->sl_axis[i], sldr_pos); - for (j = 0; j < ARRAY_SIZE(iqs7222_sl_events); j++) { - u16 mask = iqs7222_sl_events[j].mask; - u16 val = iqs7222_sl_events[j].val; + input_report_key(iqs7222->keypad, iqs7222->sl_code[i][0], + sldr_pos < dev_desc->sldr_res); - if (!iqs7222_sl_events[j].enable) { - input_report_key(iqs7222->keypad, - iqs7222->sl_code[i][j], - sldr_pos < dev_desc->sldr_res); - continue; - } + /* + * A maximum resolution indicates the device does not support + * gestures, in which case the remaining fields are ignored. + */ + if (dev_desc->sldr_res == U16_MAX) + continue; - /* - * The remaining offsets represent gesture state, and - * are discarded in the case of IQS7222C because only - * absolute position is reported. - */ - if (num_stat < IQS7222_MAX_COLS_STAT) - continue; + if (!(le16_to_cpu(status[1]) & IQS7222_EVENT_MASK_SLDR << i)) + continue; + + /* + * Skip the press/release event, as it does not have separate + * status fields and is handled separately. + */ + for (j = 1; j < ARRAY_SIZE(iqs7222_sl_events); j++) { + u16 mask = iqs7222_sl_events[j].mask; + u16 val = iqs7222_sl_events[j].val; input_report_key(iqs7222->keypad, iqs7222->sl_code[i][j], (state & mask) == val); } + + input_sync(iqs7222->keypad); + + for (j = 1; j < ARRAY_SIZE(iqs7222_sl_events); j++) + input_report_key(iqs7222->keypad, + iqs7222->sl_code[i][j], 0); } input_sync(iqs7222->keypad); diff --git a/drivers/input/mouse/cyapa_gen6.c b/drivers/input/mouse/cyapa_gen6.c index 812edfced86e..0caaf3e64215 100644 --- a/drivers/input/mouse/cyapa_gen6.c +++ b/drivers/input/mouse/cyapa_gen6.c @@ -57,7 +57,7 @@ struct pip_app_resp_head { * The value of data_status can be the first byte of data or * the command status or the unsupported command code depending on the * requested command code. - */ + */ u8 data_status; } __packed; diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c index 23507fce3a2b..18ccbd45004a 100644 --- a/drivers/input/mouse/gpio_mouse.c +++ b/drivers/input/mouse/gpio_mouse.c @@ -41,7 +41,7 @@ struct gpio_mouse { /* * Timer function which is run every scan_ms ms when the device is opened. - * The dev input variable is set to the the input_dev pointer. + * The dev input variable is set to the input_dev pointer. */ static void gpio_mouse_scan(struct input_dev *input) { diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 148a7c5fd0e2..4fbec7bbecca 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -67,25 +67,84 @@ static inline void i8042_write_command(int val) #include <linux/dmi.h> -static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { +#define SERIO_QUIRK_NOKBD BIT(0) +#define SERIO_QUIRK_NOAUX BIT(1) +#define SERIO_QUIRK_NOMUX BIT(2) +#define SERIO_QUIRK_FORCEMUX BIT(3) +#define SERIO_QUIRK_UNLOCK BIT(4) +#define SERIO_QUIRK_PROBE_DEFER BIT(5) +#define SERIO_QUIRK_RESET_ALWAYS BIT(6) +#define SERIO_QUIRK_RESET_NEVER BIT(7) +#define SERIO_QUIRK_DIECT BIT(8) +#define SERIO_QUIRK_DUMBKBD BIT(9) +#define SERIO_QUIRK_NOLOOP BIT(10) +#define SERIO_QUIRK_NOTIMEOUT BIT(11) +#define SERIO_QUIRK_KBDRESET BIT(12) +#define SERIO_QUIRK_DRITEK BIT(13) +#define SERIO_QUIRK_NOPNP BIT(14) + +/* Quirk table for different mainboards. Options similar or identical to i8042 + * module parameters. + * ORDERING IS IMPORTANT! The first match will be apllied and the rest ignored. + * This allows entries to overwrite vendor wide quirks on a per device basis. + * Where this is irrelevant, entries are sorted case sensitive by DMI_SYS_VENDOR + * and/or DMI_BOARD_VENDOR to make it easier to avoid dublicate entries. + */ +static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = { { - /* - * Arima-Rioworks HDAMB - - * AUX LOOP command does not raise AUX IRQ - */ .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "RIOWORKS"), - DMI_MATCH(DMI_BOARD_NAME, "HDAMB"), - DMI_MATCH(DMI_BOARD_VERSION, "Rev E"), + DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"), + DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* ASUS G1S */ .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_BOARD_NAME, "G1S"), - DMI_MATCH(DMI_BOARD_VERSION, "1.0"), + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X750LN"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) + }, + { + /* Asus X450LCP */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_NEVER) + }, + { + /* ASUS ZenBook UX425UA */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX425UA"), }, + .driver_data = (void *)(SERIO_QUIRK_PROBE_DEFER | SERIO_QUIRK_RESET_NEVER) + }, + { + /* ASUS ZenBook UM325UA */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"), + }, + .driver_data = (void *)(SERIO_QUIRK_PROBE_DEFER | SERIO_QUIRK_RESET_NEVER) + }, + /* + * On some Asus laptops, just running self tests cause problems. + */ + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + }, + .driver_data = (void *)(SERIO_QUIRK_RESET_NEVER) + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_CHASSIS_TYPE, "31"), /* Convertible Notebook */ + }, + .driver_data = (void *)(SERIO_QUIRK_RESET_NEVER) }, { /* ASUS P65UP5 - AUX LOOP command does not raise AUX IRQ */ @@ -94,585 +153,689 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { DMI_MATCH(DMI_BOARD_NAME, "P/I-P65UP5"), DMI_MATCH(DMI_BOARD_VERSION, "REV 2.X"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { + /* ASUS G1S */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "X750LN"), + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_BOARD_NAME, "G1S"), + DMI_MATCH(DMI_BOARD_VERSION, "1.0"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), - DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), - DMI_MATCH(DMI_PRODUCT_VERSION, "8500"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Acer Aspire 5710 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), - DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), - DMI_MATCH(DMI_PRODUCT_VERSION, "DL760"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Dell Embedded Box PC 3000 */ + /* Acer Aspire 7738 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Embedded Box PC 3000"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7738"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* OQO Model 01 */ + /* Acer Aspire 5536 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "OQO"), - DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "00"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"), + DMI_MATCH(DMI_PRODUCT_VERSION, "0100"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* ULI EV4873 - AUX LOOP does not work properly */ + /* + * Acer Aspire 5738z + * Touchpad stops working in mux mode when dis- + re-enabled + * with the touchpad enable/disable toggle hotkey + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ULI"), - DMI_MATCH(DMI_PRODUCT_NAME, "EV4873"), - DMI_MATCH(DMI_PRODUCT_VERSION, "5a"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5738"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Microsoft Virtual Machine */ + /* Acer Aspire One 150 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), - DMI_MATCH(DMI_PRODUCT_VERSION, "VS2005R2"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Medion MAM 2070 */ + /* Acer Aspire One 532h */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), - DMI_MATCH(DMI_PRODUCT_NAME, "MAM 2070"), - DMI_MATCH(DMI_PRODUCT_VERSION, "5a"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "AO532h"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Medion Akoya E7225 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Medion"), - DMI_MATCH(DMI_PRODUCT_NAME, "Akoya E7225"), - DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A114-31"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Blue FB5601 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "blue"), - DMI_MATCH(DMI_PRODUCT_NAME, "FB5601"), - DMI_MATCH(DMI_PRODUCT_VERSION, "M606"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A314-31"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Gigabyte M912 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "M912"), - DMI_MATCH(DMI_PRODUCT_VERSION, "01"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A315-31"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Gigabyte M1022M netbook */ .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co.,Ltd."), - DMI_MATCH(DMI_BOARD_NAME, "M1022E"), - DMI_MATCH(DMI_BOARD_VERSION, "1.02"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-132"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Gigabyte Spring Peak - defines wrong chassis type */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "Spring Peak"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-332"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Gigabyte T1005 - defines wrong chassis type ("Other") */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "T1005"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-432"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Gigabyte T1005M/P - defines wrong chassis type ("Other") */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "T1005M/P"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate Spin B118-RN"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, + /* + * Some Wistron based laptops need us to explicitly enable the 'Dritek + * keyboard extension' to make their extra keys start generating scancodes. + * Originally, this was just confined to older laptops, but a few Acer laptops + * have turned up in 2007 that also need this again. + */ { + /* Acer Aspire 5100 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"), - DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { + /* Acer Aspire 5610 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"), - DMI_MATCH(DMI_PRODUCT_NAME, "C15B"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { + /* Acer Aspire 5630 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ByteSpeed LLC"), - DMI_MATCH(DMI_PRODUCT_NAME, "ByteSpeed Laptop C15B"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, - { } -}; - -/* - * Some Fujitsu notebooks are having trouble with touchpads if - * active multiplexing mode is activated. Luckily they don't have - * external PS/2 ports so we can safely disable it. - * ... apparently some Toshibas don't like MUX mode either and - * die horrible death on reboot. - */ -static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { { - /* Fujitsu Lifebook P7010/P7010D */ + /* Acer Aspire 5650 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "P7010"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook P7010 */ + /* Acer Aspire 5680 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook P5020D */ + /* Acer Aspire 5720 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook S2000 */ + /* Acer Aspire 9110 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook S6230 */ + /* Acer TravelMate 660 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook T725 laptop */ + /* Acer TravelMate 2490 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T725"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu Lifebook U745 */ + /* Acer TravelMate 4280 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U745"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4280"), }, + .driver_data = (void *)(SERIO_QUIRK_DRITEK) }, { - /* Fujitsu T70H */ + /* Amoi M636/A737 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"), + DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Fujitsu-Siemens Lifebook T3010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T3010"), + DMI_MATCH(DMI_SYS_VENDOR, "ByteSpeed LLC"), + DMI_MATCH(DMI_PRODUCT_NAME, "ByteSpeed Laptop C15B"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Fujitsu-Siemens Lifebook E4010 */ + /* Compal HEL80I */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E4010"), + DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"), + DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Fujitsu-Siemens Amilo Pro 2010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2010"), + DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), + DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant"), + DMI_MATCH(DMI_PRODUCT_VERSION, "8500"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Fujitsu-Siemens Amilo Pro 2030 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"), + DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), + DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant"), + DMI_MATCH(DMI_PRODUCT_VERSION, "DL760"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* - * No data is coming from the touchscreen unless KBC - * is in legacy mode. - */ - /* Panasonic CF-29 */ + /* Advent 4211 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), - DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"), + DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"), + DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* - * HP Pavilion DV4017EA - - * errors on MUX ports are reported without raising AUXDATA - * causing "spurious NAK" messages. - */ + /* Dell Embedded Box PC 3000 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Embedded Box PC 3000"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* - * HP Pavilion ZT1000 - - * like DV4017EA does not raise AUXERR for errors on MUX ports. - */ + /* Dell XPS M1530 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"), - DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook ZT1000"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* - * HP Pavilion DV4270ca - - * like DV4017EA does not raise AUXERR for errors on MUX ports. - */ + /* Dell Vostro 1510 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EH476UA#ABL)"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Dell Vostro V13 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_NOTIMEOUT) }, { + /* Dell Vostro 1320 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1320"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { + /* Dell Vostro 1520 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE C850D"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1520"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { + /* Dell Vostro 1720 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"), - DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"), + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Sharp Actius MM20 */ + /* Entroware Proteus */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SHARP"), - DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"), + DMI_MATCH(DMI_SYS_VENDOR, "Entroware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Proteus"), + DMI_MATCH(DMI_PRODUCT_VERSION, "EL07R4"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS) }, + /* + * Some Fujitsu notebooks are having trouble with touchpads if + * active multiplexing mode is activated. Luckily they don't have + * external PS/2 ports so we can safely disable it. + * ... apparently some Toshibas don't like MUX mode either and + * die horrible death on reboot. + */ { - /* Sony Vaio FS-115b */ + /* Fujitsu Lifebook P7010/P7010D */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "P7010"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* - * Sony Vaio FZ-240E - - * reset and GET ID commands issued via KBD port are - * sometimes being delivered to AUX3. - */ + /* Fujitsu Lifebook P5020D */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* - * Most (all?) VAIOs do not have external PS/2 ports nor - * they implement active multiplexing properly, and - * MUX discovery usually messes up keyboard/touchpad. - */ + /* Fujitsu Lifebook S2000 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_BOARD_NAME, "VAIO"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Amoi M636/A737 */ + /* Fujitsu Lifebook S6230 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Lenovo 3000 n100 */ + /* Fujitsu Lifebook T725 laptop */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "076804U"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T725"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_NOTIMEOUT) }, { - /* Lenovo XiaoXin Air 12 */ + /* Fujitsu Lifebook U745 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "80UN"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U745"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Fujitsu T70H */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Acer Aspire 5710 */ + /* Fujitsu A544 laptop */ + /* https://bugzilla.redhat.com/show_bug.cgi?id=1111138 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK A544"), }, + .driver_data = (void *)(SERIO_QUIRK_NOTIMEOUT) }, { - /* Acer Aspire 7738 */ + /* Fujitsu AH544 laptop */ + /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7738"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK AH544"), }, + .driver_data = (void *)(SERIO_QUIRK_NOTIMEOUT) }, { - /* Gericom Bellagio */ + /* Fujitsu U574 laptop */ + /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Gericom"), - DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U574"), }, + .driver_data = (void *)(SERIO_QUIRK_NOTIMEOUT) }, { - /* IBM 2656 */ + /* Fujitsu UH554 laptop */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "IBM"), - DMI_MATCH(DMI_PRODUCT_NAME, "2656"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK UH544"), }, + .driver_data = (void *)(SERIO_QUIRK_NOTIMEOUT) }, { - /* Dell XPS M1530 */ + /* Fujitsu Lifebook P7010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Compal HEL80I */ + /* Fujitsu-Siemens Lifebook T3010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"), - DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T3010"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Dell Vostro 1510 */ + /* Fujitsu-Siemens Lifebook E4010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E4010"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Acer Aspire 5536 */ + /* Fujitsu-Siemens Amilo Pro 2010 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"), - DMI_MATCH(DMI_PRODUCT_VERSION, "0100"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2010"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Dell Vostro V13 */ + /* Fujitsu-Siemens Amilo Pro 2030 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"), + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Newer HP Pavilion dv4 models */ + /* Gigabyte M912 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "M912"), + DMI_MATCH(DMI_PRODUCT_VERSION, "01"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Asus X450LCP */ + /* Gigabyte Spring Peak - defines wrong chassis type */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "Spring Peak"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Avatar AVIU-145A6 */ + /* Gigabyte T1005 - defines wrong chassis type ("Other") */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Intel"), - DMI_MATCH(DMI_PRODUCT_NAME, "IC4I"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "T1005"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* TUXEDO BU1406 */ + /* Gigabyte T1005M/P - defines wrong chassis type ("Other") */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), - DMI_MATCH(DMI_PRODUCT_NAME, "N24_25BU"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "T1005M/P"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, + /* + * Some laptops need keyboard reset before probing for the trackpad to get + * it detected, initialised & finally work. + */ { - /* Lenovo LaVie Z */ + /* Gigabyte P35 v2 - Elantech touchpad */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo LaVie Z"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "P35V2"), }, + .driver_data = (void *)(SERIO_QUIRK_KBDRESET) + }, + { + /* Aorus branded Gigabyte X3 Plus - Elantech touchpad */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "X3"), + }, + .driver_data = (void *)(SERIO_QUIRK_KBDRESET) }, { - /* - * Acer Aspire 5738z - * Touchpad stops working in mux mode when dis- + re-enabled - * with the touchpad enable/disable toggle hotkey - */ + /* Gigabyte P34 - Elantech touchpad */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5738"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "P34"), }, + .driver_data = (void *)(SERIO_QUIRK_KBDRESET) }, { - /* Entroware Proteus */ + /* Gigabyte P57 - Elantech touchpad */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Entroware"), - DMI_MATCH(DMI_PRODUCT_NAME, "Proteus"), - DMI_MATCH(DMI_PRODUCT_VERSION, "EL07R4"), + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "P57"), }, + .driver_data = (void *)(SERIO_QUIRK_KBDRESET) + }, + { + /* Gericom Bellagio */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Gericom"), + DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) + }, + { + /* Gigabyte M1022M netbook */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co.,Ltd."), + DMI_MATCH(DMI_BOARD_NAME, "M1022E"), + DMI_MATCH(DMI_BOARD_VERSION, "1.02"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, - { } -}; - -static const struct dmi_system_id i8042_dmi_forcemux_table[] __initconst = { { /* - * Sony Vaio VGN-CS series require MUX or the touch sensor - * buttons will disturb touchpad operation + * HP Pavilion DV4017EA - + * errors on MUX ports are reported without raising AUXDATA + * causing "spurious NAK" messages. */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-CS"), + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, - { } -}; - -/* - * On some Asus laptops, just running self tests cause problems. - */ -static const struct dmi_system_id i8042_dmi_noselftest_table[] = { { + /* + * HP Pavilion ZT1000 - + * like DV4017EA does not raise AUXERR for errors on MUX ports. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"), + DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook ZT1000"), }, - }, { + .driver_data = (void *)(SERIO_QUIRK_NOMUX) + }, + { + /* + * HP Pavilion DV4270ca - + * like DV4017EA does not raise AUXERR for errors on MUX ports. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_CHASSIS_TYPE, "31"), /* Convertible Notebook */ + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EH476UA#ABL)"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, - { } -}; -static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { { - /* MSI Wind U-100 */ + /* Newer HP Pavilion dv4 models */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "U-100"), - DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_NOTIMEOUT) }, { - /* LG Electronics X110 */ + /* IBM 2656 */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "X110"), - DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."), + DMI_MATCH(DMI_SYS_VENDOR, "IBM"), + DMI_MATCH(DMI_PRODUCT_NAME, "2656"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Acer Aspire One 150 */ + /* Avatar AVIU-145A6 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"), + DMI_MATCH(DMI_SYS_VENDOR, "Intel"), + DMI_MATCH(DMI_PRODUCT_NAME, "IC4I"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Intel MBO Desktop D845PESV */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A114-31"), + DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "D845PESV"), }, + .driver_data = (void *)(SERIO_QUIRK_NOPNP) }, { + /* + * Intel NUC D54250WYK - does not have i8042 controller but + * declares PS/2 devices in DSDT. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A314-31"), + DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "D54250WYK"), }, + .driver_data = (void *)(SERIO_QUIRK_NOPNP) }, { + /* Lenovo 3000 n100 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A315-31"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "076804U"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Lenovo XiaoXin Air 12 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-132"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "80UN"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Lenovo LaVie Z */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-332"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo LaVie Z"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* Lenovo Ideapad U455 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-432"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20046"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { + /* Lenovo ThinkPad L460 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate Spin B118-RN"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L460"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Advent 4211 */ + /* Lenovo ThinkPad Twist S230u */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"), - DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "33474HU"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) + }, + { + /* LG Electronics X110 */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."), + DMI_MATCH(DMI_BOARD_NAME, "X110"), + }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { /* Medion Akoya Mini E1210 */ @@ -680,6 +843,7 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), DMI_MATCH(DMI_PRODUCT_NAME, "E1210"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { /* Medion Akoya E1222 */ @@ -687,331 +851,434 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), DMI_MATCH(DMI_PRODUCT_NAME, "E122X"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, { - /* Mivvy M310 */ + /* MSI Wind U-100 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"), - DMI_MATCH(DMI_PRODUCT_NAME, "N10"), + DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), + DMI_MATCH(DMI_BOARD_NAME, "U-100"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS | SERIO_QUIRK_NOPNP) }, { - /* Dell Vostro 1320 */ + /* + * No data is coming from the touchscreen unless KBC + * is in legacy mode. + */ + /* Panasonic CF-29 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1320"), + DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), + DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Dell Vostro 1520 */ + /* Medion Akoya E7225 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1520"), + DMI_MATCH(DMI_SYS_VENDOR, "Medion"), + DMI_MATCH(DMI_PRODUCT_NAME, "Akoya E7225"), + DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Dell Vostro 1720 */ + /* Microsoft Virtual Machine */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"), + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), + DMI_MATCH(DMI_PRODUCT_VERSION, "VS2005R2"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Lenovo Ideapad U455 */ + /* Medion MAM 2070 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "20046"), + DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), + DMI_MATCH(DMI_PRODUCT_NAME, "MAM 2070"), + DMI_MATCH(DMI_PRODUCT_VERSION, "5a"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Lenovo ThinkPad L460 */ + /* TUXEDO BU1406 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L460"), + DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), + DMI_MATCH(DMI_PRODUCT_NAME, "N24_25BU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Clevo P650RS, 650RP6, Sager NP8152-S, and others */ + /* OQO Model 01 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), - DMI_MATCH(DMI_PRODUCT_NAME, "P65xRP"), + DMI_MATCH(DMI_SYS_VENDOR, "OQO"), + DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "00"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Lenovo ThinkPad Twist S230u */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "33474HU"), + DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"), + DMI_MATCH(DMI_PRODUCT_NAME, "C15B"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* Entroware Proteus */ + /* Acer Aspire 5 A515 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Entroware"), - DMI_MATCH(DMI_PRODUCT_NAME, "Proteus"), - DMI_MATCH(DMI_PRODUCT_VERSION, "EL07R4"), + DMI_MATCH(DMI_BOARD_VENDOR, "PK"), + DMI_MATCH(DMI_BOARD_NAME, "Grumpy_PK"), }, + .driver_data = (void *)(SERIO_QUIRK_NOPNP) }, - { } -}; - -#ifdef CONFIG_PNP -static const struct dmi_system_id __initconst i8042_dmi_nopnp_table[] = { { - /* Intel MBO Desktop D845PESV */ + /* ULI EV4873 - AUX LOOP does not work properly */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "D845PESV"), - DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_SYS_VENDOR, "ULI"), + DMI_MATCH(DMI_PRODUCT_NAME, "EV4873"), + DMI_MATCH(DMI_PRODUCT_VERSION, "5a"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { /* - * Intel NUC D54250WYK - does not have i8042 controller but - * declares PS/2 devices in DSDT. + * Arima-Rioworks HDAMB - + * AUX LOOP command does not raise AUX IRQ */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "D54250WYK"), - DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_VENDOR, "RIOWORKS"), + DMI_MATCH(DMI_BOARD_NAME, "HDAMB"), + DMI_MATCH(DMI_BOARD_VERSION, "Rev E"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, { - /* MSI Wind U-100 */ + /* Sharp Actius MM20 */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "U-100"), - DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), + DMI_MATCH(DMI_SYS_VENDOR, "SHARP"), + DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { - /* Acer Aspire 5 A515 */ + /* + * Sony Vaio FZ-240E - + * reset and GET ID commands issued via KBD port are + * sometimes being delivered to AUX3. + */ .matches = { - DMI_MATCH(DMI_BOARD_NAME, "Grumpy_PK"), - DMI_MATCH(DMI_BOARD_VENDOR, "PK"), + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) + }, + { + /* + * Most (all?) VAIOs do not have external PS/2 ports nor + * they implement active multiplexing properly, and + * MUX discovery usually messes up keyboard/touchpad. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "VAIO"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, - { } -}; - -static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = { { + /* Sony Vaio FS-115b */ .matches = { - DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { + /* + * Sony Vaio VGN-CS series require MUX or the touch sensor + * buttons will disturb touchpad operation + */ .matches = { - DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-CS"), }, + .driver_data = (void *)(SERIO_QUIRK_FORCEMUX) }, { .matches = { - DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, { .matches = { - DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, - { } -}; -#endif - -static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = { { - /* Dell Vostro V13 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"), + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE C850D"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) }, + /* + * A lot of modern Clevo barebones have touchpad and/or keyboard issues + * after suspend fixable with nomux + reset + noloop + nopnp. Luckily, + * none of them have an external PS/2 port so this can safely be set for + * all of them. These two are based on a Clevo design, but have the + * board_name changed. + */ { - /* Newer HP Pavilion dv4 models */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"), + DMI_MATCH(DMI_BOARD_VENDOR, "TUXEDO"), + DMI_MATCH(DMI_BOARD_NAME, "AURA1501"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Fujitsu A544 laptop */ - /* https://bugzilla.redhat.com/show_bug.cgi?id=1111138 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK A544"), + DMI_MATCH(DMI_BOARD_VENDOR, "TUXEDO"), + DMI_MATCH(DMI_BOARD_NAME, "EDUBOOK1502"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Fujitsu AH544 laptop */ - /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ + /* Mivvy M310 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK AH544"), + DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"), + DMI_MATCH(DMI_PRODUCT_NAME, "N10"), }, + .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS) }, + /* + * Some laptops need keyboard reset before probing for the trackpad to get + * it detected, initialised & finally work. + */ { - /* Fujitsu Lifebook T725 laptop */ + /* Schenker XMG C504 - Elantech touchpad */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T725"), + DMI_MATCH(DMI_SYS_VENDOR, "XMG"), + DMI_MATCH(DMI_PRODUCT_NAME, "C504"), }, + .driver_data = (void *)(SERIO_QUIRK_KBDRESET) }, { - /* Fujitsu U574 laptop */ - /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ + /* Blue FB5601 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U574"), + DMI_MATCH(DMI_SYS_VENDOR, "blue"), + DMI_MATCH(DMI_PRODUCT_NAME, "FB5601"), + DMI_MATCH(DMI_PRODUCT_VERSION, "M606"), }, + .driver_data = (void *)(SERIO_QUIRK_NOLOOP) }, + /* + * A lot of modern Clevo barebones have touchpad and/or keyboard issues + * after suspend fixable with nomux + reset + noloop + nopnp. Luckily, + * none of them have an external PS/2 port so this can safely be set for + * all of them. + * Clevo barebones come with board_vendor and/or system_vendor set to + * either the very generic string "Notebook" and/or a different value + * for each individual reseller. The only somewhat universal way to + * identify them is by board_name. + */ { - /* Fujitsu UH554 laptop */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK UH544"), + DMI_MATCH(DMI_BOARD_NAME, "LAPQC71A"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, - { } -}; - -/* - * Some Wistron based laptops need us to explicitly enable the 'Dritek - * keyboard extension' to make their extra keys start generating scancodes. - * Originally, this was just confined to older laptops, but a few Acer laptops - * have turned up in 2007 that also need this again. - */ -static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = { { - /* Acer Aspire 5100 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"), + DMI_MATCH(DMI_BOARD_NAME, "LAPQC71B"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer Aspire 5610 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"), + DMI_MATCH(DMI_BOARD_NAME, "N140CU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer Aspire 5630 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"), + DMI_MATCH(DMI_BOARD_NAME, "N141CU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer Aspire 5650 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"), + DMI_MATCH(DMI_BOARD_NAME, "NH5xAx"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer Aspire 5680 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"), + DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, + /* + * At least one modern Clevo barebone has the touchpad connected both + * via PS/2 and i2c interface. This causes a race condition between the + * psmouse and i2c-hid driver. Since the full capability of the touchpad + * is available via the i2c interface and the device has no external + * PS/2 port, it is safe to just ignore all ps2 mouses here to avoid + * this issue. The known affected device is the + * TUXEDO InfinityBook S17 Gen6 / Clevo NS70MU which comes with one of + * the two different dmi strings below. NS50MU is not a typo! + */ { - /* Acer Aspire 5720 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"), + DMI_MATCH(DMI_BOARD_NAME, "NS50MU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOAUX | SERIO_QUIRK_NOMUX | + SERIO_QUIRK_RESET_ALWAYS | SERIO_QUIRK_NOLOOP | + SERIO_QUIRK_NOPNP) }, { - /* Acer Aspire 9110 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"), + DMI_MATCH(DMI_BOARD_NAME, "NS50_70MU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOAUX | SERIO_QUIRK_NOMUX | + SERIO_QUIRK_RESET_ALWAYS | SERIO_QUIRK_NOLOOP | + SERIO_QUIRK_NOPNP) }, { - /* Acer TravelMate 660 */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"), + DMI_MATCH(DMI_BOARD_NAME, "NJ50_70CU"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer TravelMate 2490 */ + /* + * This is only a partial board_name and might be followed by + * another letter or number. DMI_MATCH however does do partial + * matching. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"), + DMI_MATCH(DMI_PRODUCT_NAME, "P65xH"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Acer TravelMate 4280 */ + /* Clevo P650RS, 650RP6, Sager NP8152-S, and others */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4280"), + DMI_MATCH(DMI_PRODUCT_NAME, "P65xRP"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, - { } -}; - -/* - * Some laptops need keyboard reset before probing for the trackpad to get - * it detected, initialised & finally work. - */ -static const struct dmi_system_id __initconst i8042_dmi_kbdreset_table[] = { { - /* Gigabyte P35 v2 - Elantech touchpad */ + /* + * This is only a partial board_name and might be followed by + * another letter or number. DMI_MATCH however does do partial + * matching. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "P35V2"), + DMI_MATCH(DMI_PRODUCT_NAME, "P65_P67H"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, - { - /* Aorus branded Gigabyte X3 Plus - Elantech touchpad */ + { + /* + * This is only a partial board_name and might be followed by + * another letter or number. DMI_MATCH however does do partial + * matching. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "X3"), + DMI_MATCH(DMI_PRODUCT_NAME, "P65_67RP"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Gigabyte P34 - Elantech touchpad */ + /* + * This is only a partial board_name and might be followed by + * another letter or number. DMI_MATCH however does do partial + * matching. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "P34"), + DMI_MATCH(DMI_PRODUCT_NAME, "P65_67RS"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Gigabyte P57 - Elantech touchpad */ + /* + * This is only a partial board_name and might be followed by + * another letter or number. DMI_MATCH however does do partial + * matching. + */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), - DMI_MATCH(DMI_PRODUCT_NAME, "P57"), + DMI_MATCH(DMI_PRODUCT_NAME, "P67xRP"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { - /* Schenker XMG C504 - Elantech touchpad */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "XMG"), - DMI_MATCH(DMI_PRODUCT_NAME, "C504"), + DMI_MATCH(DMI_BOARD_NAME, "PB50_70DFx,DDx"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "X170SM"), }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "X170KM-G"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, { } }; -static const struct dmi_system_id i8042_dmi_probe_defer_table[] __initconst = { +#ifdef CONFIG_PNP +static const struct dmi_system_id i8042_dmi_laptop_table[] __initconst = { { - /* ASUS ZenBook UX425UA */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX425UA"), + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ }, }, { - /* ASUS ZenBook UM325UA */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"), + DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ + }, + }, + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + }, + }, + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ }, }, { } }; +#endif #endif /* CONFIG_X86 */ @@ -1167,11 +1434,6 @@ static int __init i8042_pnp_init(void) bool pnp_data_busted = false; int err; -#ifdef CONFIG_X86 - if (dmi_check_system(i8042_dmi_nopnp_table)) - i8042_nopnp = true; -#endif - if (i8042_nopnp) { pr_info("PNP detection disabled\n"); return 0; @@ -1275,6 +1537,59 @@ static inline int i8042_pnp_init(void) { return 0; } static inline void i8042_pnp_exit(void) { } #endif /* CONFIG_PNP */ + +#ifdef CONFIG_X86 +static void __init i8042_check_quirks(void) +{ + const struct dmi_system_id *device_quirk_info; + uintptr_t quirks; + + device_quirk_info = dmi_first_match(i8042_dmi_quirk_table); + if (!device_quirk_info) + return; + + quirks = (uintptr_t)device_quirk_info->driver_data; + + if (quirks & SERIO_QUIRK_NOKBD) + i8042_nokbd = true; + if (quirks & SERIO_QUIRK_NOAUX) + i8042_noaux = true; + if (quirks & SERIO_QUIRK_NOMUX) + i8042_nomux = true; + if (quirks & SERIO_QUIRK_FORCEMUX) + i8042_nomux = false; + if (quirks & SERIO_QUIRK_UNLOCK) + i8042_unlock = true; + if (quirks & SERIO_QUIRK_PROBE_DEFER) + i8042_probe_defer = true; + /* Honor module parameter when value is not default */ + if (i8042_reset == I8042_RESET_DEFAULT) { + if (quirks & SERIO_QUIRK_RESET_ALWAYS) + i8042_reset = I8042_RESET_ALWAYS; + if (quirks & SERIO_QUIRK_RESET_NEVER) + i8042_reset = I8042_RESET_NEVER; + } + if (quirks & SERIO_QUIRK_DIECT) + i8042_direct = true; + if (quirks & SERIO_QUIRK_DUMBKBD) + i8042_dumbkbd = true; + if (quirks & SERIO_QUIRK_NOLOOP) + i8042_noloop = true; + if (quirks & SERIO_QUIRK_NOTIMEOUT) + i8042_notimeout = true; + if (quirks & SERIO_QUIRK_KBDRESET) + i8042_kbdreset = true; + if (quirks & SERIO_QUIRK_DRITEK) + i8042_dritek = true; +#ifdef CONFIG_PNP + if (quirks & SERIO_QUIRK_NOPNP) + i8042_nopnp = true; +#endif +} +#else +static inline void i8042_check_quirks(void) {} +#endif + static int __init i8042_platform_init(void) { int retval; @@ -1297,45 +1612,42 @@ static int __init i8042_platform_init(void) i8042_kbd_irq = I8042_MAP_IRQ(1); i8042_aux_irq = I8042_MAP_IRQ(12); - retval = i8042_pnp_init(); - if (retval) - return retval; - #if defined(__ia64__) - i8042_reset = I8042_RESET_ALWAYS; + i8042_reset = I8042_RESET_ALWAYS; #endif + i8042_check_quirks(); + + pr_debug("Active quirks (empty means none):%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + i8042_nokbd ? " nokbd" : "", + i8042_noaux ? " noaux" : "", + i8042_nomux ? " nomux" : "", + i8042_unlock ? " unlock" : "", + i8042_probe_defer ? "probe_defer" : "", + i8042_reset == I8042_RESET_DEFAULT ? + "" : i8042_reset == I8042_RESET_ALWAYS ? + " reset_always" : " reset_never", + i8042_direct ? " direct" : "", + i8042_dumbkbd ? " dumbkbd" : "", + i8042_noloop ? " noloop" : "", + i8042_notimeout ? " notimeout" : "", + i8042_kbdreset ? " kbdreset" : "", #ifdef CONFIG_X86 - /* Honor module parameter when value is not default */ - if (i8042_reset == I8042_RESET_DEFAULT) { - if (dmi_check_system(i8042_dmi_reset_table)) - i8042_reset = I8042_RESET_ALWAYS; - - if (dmi_check_system(i8042_dmi_noselftest_table)) - i8042_reset = I8042_RESET_NEVER; - } - - if (dmi_check_system(i8042_dmi_noloop_table)) - i8042_noloop = true; - - if (dmi_check_system(i8042_dmi_nomux_table)) - i8042_nomux = true; - - if (dmi_check_system(i8042_dmi_forcemux_table)) - i8042_nomux = false; - - if (dmi_check_system(i8042_dmi_notimeout_table)) - i8042_notimeout = true; - - if (dmi_check_system(i8042_dmi_dritek_table)) - i8042_dritek = true; - - if (dmi_check_system(i8042_dmi_kbdreset_table)) - i8042_kbdreset = true; + i8042_dritek ? " dritek" : "", +#else + "", +#endif +#ifdef CONFIG_PNP + i8042_nopnp ? " nopnp" : ""); +#else + ""); +#endif - if (dmi_check_system(i8042_dmi_probe_defer_table)) - i8042_probe_defer = true; + retval = i8042_pnp_init(); + if (retval) + return retval; +#ifdef CONFIG_X86 /* * A20 was already enabled during early kernel init. But some buggy * BIOSes (in MSI Laptops) require A20 to be enabled using 8042 to diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index bb2e1cbffba7..82beddb28761 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -24,6 +24,7 @@ #include <linux/irq.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/property.h> #include <linux/ratelimit.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> @@ -47,6 +48,8 @@ #define M09_REGISTER_NUM_X 0x94 #define M09_REGISTER_NUM_Y 0x95 +#define M12_REGISTER_REPORT_RATE 0x88 + #define EV_REGISTER_THRESHOLD 0x40 #define EV_REGISTER_GAIN 0x41 #define EV_REGISTER_OFFSET_Y 0x45 @@ -127,9 +130,12 @@ struct edt_ft5x06_ts_data { int max_support_points; char name[EDT_NAME_LEN]; + char fw_version[EDT_NAME_LEN]; struct edt_reg_addr reg_addr; enum edt_ver version; + unsigned int crc_errors; + unsigned int header_errors; }; struct edt_i2c_chip_data { @@ -178,6 +184,7 @@ static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata, crc ^= buf[i]; if (crc != buf[buflen-1]) { + tsdata->crc_errors++; dev_err_ratelimited(&tsdata->client->dev, "crc error: 0x%02x expected, got 0x%02x\n", crc, buf[buflen-1]); @@ -235,6 +242,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) if (tsdata->version == EDT_M06) { if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || rdbuf[2] != datalen) { + tsdata->header_errors++; dev_err_ratelimited(dev, "Unexpected header: %02x%02x%02x!\n", rdbuf[0], rdbuf[1], rdbuf[2]); @@ -523,9 +531,55 @@ static EDT_ATTR(offset_y, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER, /* m06: range 20 to 80, m09: range 0 to 30, m12: range 1 to 255... */ static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD, M09_REGISTER_THRESHOLD, EV_REGISTER_THRESHOLD, 0, 255); -/* m06: range 3 to 14, m12: (0x64: 100Hz) */ +/* m06: range 3 to 14, m12: range 1 to 255 */ static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE, - NO_REGISTER, NO_REGISTER, 0, 255); + M12_REGISTER_REPORT_RATE, NO_REGISTER, 0, 255); + +static ssize_t model_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%s\n", tsdata->name); +} + +static DEVICE_ATTR_RO(model); + +static ssize_t fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%s\n", tsdata->fw_version); +} + +static DEVICE_ATTR_RO(fw_version); + +/* m06 only */ +static ssize_t header_errors_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%d\n", tsdata->header_errors); +} + +static DEVICE_ATTR_RO(header_errors); + +/* m06 only */ +static ssize_t crc_errors_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); + + return sysfs_emit(buf, "%d\n", tsdata->crc_errors); +} + +static DEVICE_ATTR_RO(crc_errors); static struct attribute *edt_ft5x06_attrs[] = { &edt_ft5x06_attr_gain.dattr.attr, @@ -534,6 +588,10 @@ static struct attribute *edt_ft5x06_attrs[] = { &edt_ft5x06_attr_offset_y.dattr.attr, &edt_ft5x06_attr_threshold.dattr.attr, &edt_ft5x06_attr_report_rate.dattr.attr, + &dev_attr_model.attr, + &dev_attr_fw_version.attr, + &dev_attr_header_errors.attr, + &dev_attr_crc_errors.attr, NULL }; @@ -820,13 +878,13 @@ static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) #endif /* CONFIG_DEBUGFS */ static int edt_ft5x06_ts_identify(struct i2c_client *client, - struct edt_ft5x06_ts_data *tsdata, - char *fw_version) + struct edt_ft5x06_ts_data *tsdata) { u8 rdbuf[EDT_NAME_LEN]; char *p; int error; char *model_name = tsdata->name; + char *fw_version = tsdata->fw_version; /* see what we find if we assume it is a M06 * * if we get less than EDT_NAME_LEN, we don't want @@ -1030,7 +1088,8 @@ static void edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) case EDT_M09: case EDT_M12: reg_addr->reg_threshold = M09_REGISTER_THRESHOLD; - reg_addr->reg_report_rate = NO_REGISTER; + reg_addr->reg_report_rate = tsdata->version == EDT_M12 ? + M12_REGISTER_REPORT_RATE : NO_REGISTER; reg_addr->reg_gain = M09_REGISTER_GAIN; reg_addr->reg_offset = M09_REGISTER_OFFSET; reg_addr->reg_offset_x = NO_REGISTER; @@ -1081,7 +1140,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, struct input_dev *input; unsigned long irq_flags; int error; - char fw_version[EDT_NAME_LEN]; + u32 report_rate; dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n"); @@ -1194,7 +1253,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, tsdata->input = input; tsdata->factory_mode = false; - error = edt_ft5x06_ts_identify(client, tsdata, fw_version); + error = edt_ft5x06_ts_identify(client, tsdata); if (error) { dev_err(&client->dev, "touchscreen probe failed\n"); return error; @@ -1210,9 +1269,30 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, edt_ft5x06_ts_get_defaults(&client->dev, tsdata); edt_ft5x06_ts_get_parameters(tsdata); + if (tsdata->reg_addr.reg_report_rate != NO_REGISTER && + !device_property_read_u32(&client->dev, + "report-rate-hz", &report_rate)) { + if (tsdata->version == EDT_M06) + tsdata->report_rate = clamp_val(report_rate, 30, 140); + else + tsdata->report_rate = clamp_val(report_rate, 1, 255); + + if (report_rate != tsdata->report_rate) + dev_warn(&client->dev, + "report-rate %dHz is unsupported, use %dHz\n", + report_rate, tsdata->report_rate); + + if (tsdata->version == EDT_M06) + tsdata->report_rate /= 10; + + edt_ft5x06_register_write(tsdata, + tsdata->reg_addr.reg_report_rate, + tsdata->report_rate); + } + dev_dbg(&client->dev, "Model \"%s\", Rev. \"%s\", %dx%d sensors\n", - tsdata->name, fw_version, tsdata->num_x, tsdata->num_y); + tsdata->name, tsdata->fw_version, tsdata->num_x, tsdata->num_y); input->name = tsdata->name; input->id.bustype = BUS_I2C; diff --git a/drivers/input/touchscreen/exc3000.c b/drivers/input/touchscreen/exc3000.c index cbe0dd412912..4b7eee01c6aa 100644 --- a/drivers/input/touchscreen/exc3000.c +++ b/drivers/input/touchscreen/exc3000.c @@ -220,6 +220,7 @@ static int exc3000_vendor_data_request(struct exc3000_data *data, u8 *request, { u8 buf[EXC3000_LEN_VENDOR_REQUEST] = { 0x67, 0x00, 0x42, 0x00, 0x03 }; int ret; + unsigned long time_left; mutex_lock(&data->query_lock); @@ -233,9 +234,9 @@ static int exc3000_vendor_data_request(struct exc3000_data *data, u8 *request, goto out_unlock; if (response) { - ret = wait_for_completion_timeout(&data->wait_event, - timeout * HZ); - if (ret <= 0) { + time_left = wait_for_completion_timeout(&data->wait_event, + timeout * HZ); + if (time_left == 0) { ret = -ETIMEDOUT; goto out_unlock; } diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index aa45a9fee6a0..d016505fc081 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -822,22 +822,16 @@ static int goodix_resource(struct acpi_resource *ares, void *data) struct device *dev = &ts->client->dev; struct acpi_resource_gpio *gpio; - switch (ares->type) { - case ACPI_RESOURCE_TYPE_GPIO: - gpio = &ares->data.gpio; - if (gpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) { - if (ts->gpio_int_idx == -1) { - ts->gpio_int_idx = ts->gpio_count; - } else { - dev_err(dev, "More then one GpioInt resource, ignoring ACPI GPIO resources\n"); - ts->gpio_int_idx = -2; - } + if (acpi_gpio_get_irq_resource(ares, &gpio)) { + if (ts->gpio_int_idx == -1) { + ts->gpio_int_idx = ts->gpio_count; + } else { + dev_err(dev, "More then one GpioInt resource, ignoring ACPI GPIO resources\n"); + ts->gpio_int_idx = -2; } ts->gpio_count++; - break; - default: - break; - } + } else if (acpi_gpio_get_io_resource(ares, &gpio)) + ts->gpio_count++; return 0; } diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c index 8bd03278ad9a..52f9e9eaab14 100644 --- a/drivers/input/touchscreen/zinitix.c +++ b/drivers/input/touchscreen/zinitix.c @@ -15,75 +15,75 @@ /* Register Map */ -#define BT541_SWRESET_CMD 0x0000 -#define BT541_WAKEUP_CMD 0x0001 +#define ZINITIX_SWRESET_CMD 0x0000 +#define ZINITIX_WAKEUP_CMD 0x0001 -#define BT541_IDLE_CMD 0x0004 -#define BT541_SLEEP_CMD 0x0005 +#define ZINITIX_IDLE_CMD 0x0004 +#define ZINITIX_SLEEP_CMD 0x0005 -#define BT541_CLEAR_INT_STATUS_CMD 0x0003 -#define BT541_CALIBRATE_CMD 0x0006 -#define BT541_SAVE_STATUS_CMD 0x0007 -#define BT541_SAVE_CALIBRATION_CMD 0x0008 -#define BT541_RECALL_FACTORY_CMD 0x000f +#define ZINITIX_CLEAR_INT_STATUS_CMD 0x0003 +#define ZINITIX_CALIBRATE_CMD 0x0006 +#define ZINITIX_SAVE_STATUS_CMD 0x0007 +#define ZINITIX_SAVE_CALIBRATION_CMD 0x0008 +#define ZINITIX_RECALL_FACTORY_CMD 0x000f -#define BT541_THRESHOLD 0x0020 +#define ZINITIX_THRESHOLD 0x0020 -#define BT541_LARGE_PALM_REJECT_AREA_TH 0x003F +#define ZINITIX_LARGE_PALM_REJECT_AREA_TH 0x003F -#define BT541_DEBUG_REG 0x0115 /* 0~7 */ +#define ZINITIX_DEBUG_REG 0x0115 /* 0~7 */ -#define BT541_TOUCH_MODE 0x0010 -#define BT541_CHIP_REVISION 0x0011 -#define BT541_FIRMWARE_VERSION 0x0012 +#define ZINITIX_TOUCH_MODE 0x0010 +#define ZINITIX_CHIP_REVISION 0x0011 +#define ZINITIX_FIRMWARE_VERSION 0x0012 #define ZINITIX_USB_DETECT 0x116 -#define BT541_MINOR_FW_VERSION 0x0121 +#define ZINITIX_MINOR_FW_VERSION 0x0121 -#define BT541_VENDOR_ID 0x001C -#define BT541_HW_ID 0x0014 +#define ZINITIX_VENDOR_ID 0x001C +#define ZINITIX_HW_ID 0x0014 -#define BT541_DATA_VERSION_REG 0x0013 -#define BT541_SUPPORTED_FINGER_NUM 0x0015 -#define BT541_EEPROM_INFO 0x0018 -#define BT541_INITIAL_TOUCH_MODE 0x0019 +#define ZINITIX_DATA_VERSION_REG 0x0013 +#define ZINITIX_SUPPORTED_FINGER_NUM 0x0015 +#define ZINITIX_EEPROM_INFO 0x0018 +#define ZINITIX_INITIAL_TOUCH_MODE 0x0019 -#define BT541_TOTAL_NUMBER_OF_X 0x0060 -#define BT541_TOTAL_NUMBER_OF_Y 0x0061 +#define ZINITIX_TOTAL_NUMBER_OF_X 0x0060 +#define ZINITIX_TOTAL_NUMBER_OF_Y 0x0061 -#define BT541_DELAY_RAW_FOR_HOST 0x007f +#define ZINITIX_DELAY_RAW_FOR_HOST 0x007f -#define BT541_BUTTON_SUPPORTED_NUM 0x00B0 -#define BT541_BUTTON_SENSITIVITY 0x00B2 -#define BT541_DUMMY_BUTTON_SENSITIVITY 0X00C8 +#define ZINITIX_BUTTON_SUPPORTED_NUM 0x00B0 +#define ZINITIX_BUTTON_SENSITIVITY 0x00B2 +#define ZINITIX_DUMMY_BUTTON_SENSITIVITY 0X00C8 -#define BT541_X_RESOLUTION 0x00C0 -#define BT541_Y_RESOLUTION 0x00C1 +#define ZINITIX_X_RESOLUTION 0x00C0 +#define ZINITIX_Y_RESOLUTION 0x00C1 -#define BT541_POINT_STATUS_REG 0x0080 -#define BT541_ICON_STATUS_REG 0x00AA +#define ZINITIX_POINT_STATUS_REG 0x0080 +#define ZINITIX_ICON_STATUS_REG 0x00AA -#define BT541_POINT_COORD_REG (BT541_POINT_STATUS_REG + 2) +#define ZINITIX_POINT_COORD_REG (ZINITIX_POINT_STATUS_REG + 2) -#define BT541_AFE_FREQUENCY 0x0100 -#define BT541_DND_N_COUNT 0x0122 -#define BT541_DND_U_COUNT 0x0135 +#define ZINITIX_AFE_FREQUENCY 0x0100 +#define ZINITIX_DND_N_COUNT 0x0122 +#define ZINITIX_DND_U_COUNT 0x0135 -#define BT541_RAWDATA_REG 0x0200 +#define ZINITIX_RAWDATA_REG 0x0200 -#define BT541_EEPROM_INFO_REG 0x0018 +#define ZINITIX_EEPROM_INFO_REG 0x0018 -#define BT541_INT_ENABLE_FLAG 0x00f0 -#define BT541_PERIODICAL_INTERRUPT_INTERVAL 0x00f1 +#define ZINITIX_INT_ENABLE_FLAG 0x00f0 +#define ZINITIX_PERIODICAL_INTERRUPT_INTERVAL 0x00f1 -#define BT541_BTN_WIDTH 0x016d +#define ZINITIX_BTN_WIDTH 0x016d -#define BT541_CHECKSUM_RESULT 0x012c +#define ZINITIX_CHECKSUM_RESULT 0x012c -#define BT541_INIT_FLASH 0x01d0 -#define BT541_WRITE_FLASH 0x01d1 -#define BT541_READ_FLASH 0x01d2 +#define ZINITIX_INIT_FLASH 0x01d0 +#define ZINITIX_WRITE_FLASH 0x01d1 +#define ZINITIX_READ_FLASH 0x01d2 #define ZINITIX_INTERNAL_FLAG_02 0x011e #define ZINITIX_INTERNAL_FLAG_03 0x011f @@ -196,13 +196,13 @@ static int zinitix_init_touch(struct bt541_ts_data *bt541) int i; int error; - error = zinitix_write_cmd(client, BT541_SWRESET_CMD); + error = zinitix_write_cmd(client, ZINITIX_SWRESET_CMD); if (error) { dev_err(&client->dev, "Failed to write reset command\n"); return error; } - error = zinitix_write_u16(client, BT541_INT_ENABLE_FLAG, 0x0); + error = zinitix_write_u16(client, ZINITIX_INT_ENABLE_FLAG, 0x0); if (error) { dev_err(&client->dev, "Failed to reset interrupt enable flag\n"); @@ -210,32 +210,32 @@ static int zinitix_init_touch(struct bt541_ts_data *bt541) } /* initialize */ - error = zinitix_write_u16(client, BT541_X_RESOLUTION, + error = zinitix_write_u16(client, ZINITIX_X_RESOLUTION, bt541->prop.max_x); if (error) return error; - error = zinitix_write_u16(client, BT541_Y_RESOLUTION, + error = zinitix_write_u16(client, ZINITIX_Y_RESOLUTION, bt541->prop.max_y); if (error) return error; - error = zinitix_write_u16(client, BT541_SUPPORTED_FINGER_NUM, + error = zinitix_write_u16(client, ZINITIX_SUPPORTED_FINGER_NUM, MAX_SUPPORTED_FINGER_NUM); if (error) return error; - error = zinitix_write_u16(client, BT541_INITIAL_TOUCH_MODE, + error = zinitix_write_u16(client, ZINITIX_INITIAL_TOUCH_MODE, bt541->zinitix_mode); if (error) return error; - error = zinitix_write_u16(client, BT541_TOUCH_MODE, + error = zinitix_write_u16(client, ZINITIX_TOUCH_MODE, bt541->zinitix_mode); if (error) return error; - error = zinitix_write_u16(client, BT541_INT_ENABLE_FLAG, + error = zinitix_write_u16(client, ZINITIX_INT_ENABLE_FLAG, BIT_PT_CNT_CHANGE | BIT_DOWN | BIT_MOVE | BIT_UP); if (error) @@ -243,7 +243,7 @@ static int zinitix_init_touch(struct bt541_ts_data *bt541) /* clear queue */ for (i = 0; i < 10; i++) { - zinitix_write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + zinitix_write_cmd(client, ZINITIX_CLEAR_INT_STATUS_CMD); udelay(10); } @@ -361,7 +361,7 @@ static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler) memset(&touch_event, 0, sizeof(struct touch_event)); - error = zinitix_read_data(bt541->client, BT541_POINT_STATUS_REG, + error = zinitix_read_data(bt541->client, ZINITIX_POINT_STATUS_REG, &touch_event, sizeof(struct touch_event)); if (error) { dev_err(&client->dev, "Failed to read in touchpoint struct\n"); @@ -381,7 +381,7 @@ static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler) input_sync(bt541->input_dev); out: - zinitix_write_cmd(bt541->client, BT541_CLEAR_INT_STATUS_CMD); + zinitix_write_cmd(bt541->client, ZINITIX_CLEAR_INT_STATUS_CMD); return IRQ_HANDLED; } diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index acd6d6b47434..09c7ed2650ca 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -83,7 +83,7 @@ struct dm_bufio_client { struct mutex lock; spinlock_t spinlock; - unsigned long spinlock_flags; + bool no_sleep; struct list_head lru[LIST_SIZE]; unsigned long n_buffers[LIST_SIZE]; @@ -93,8 +93,6 @@ struct dm_bufio_client { s8 sectors_per_block_bits; void (*alloc_callback)(struct dm_buffer *); void (*write_callback)(struct dm_buffer *); - bool no_sleep; - struct kmem_cache *slab_buffer; struct kmem_cache *slab_cache; struct dm_io_client *dm_io; @@ -174,7 +172,7 @@ static DEFINE_STATIC_KEY_FALSE(no_sleep_enabled); static void dm_bufio_lock(struct dm_bufio_client *c) { if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep) - spin_lock_irqsave_nested(&c->spinlock, c->spinlock_flags, dm_bufio_in_request()); + spin_lock_bh(&c->spinlock); else mutex_lock_nested(&c->lock, dm_bufio_in_request()); } @@ -182,7 +180,7 @@ static void dm_bufio_lock(struct dm_bufio_client *c) static int dm_bufio_trylock(struct dm_bufio_client *c) { if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep) - return spin_trylock_irqsave(&c->spinlock, c->spinlock_flags); + return spin_trylock_bh(&c->spinlock); else return mutex_trylock(&c->lock); } @@ -190,7 +188,7 @@ static int dm_bufio_trylock(struct dm_bufio_client *c) static void dm_bufio_unlock(struct dm_bufio_client *c) { if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep) - spin_unlock_irqrestore(&c->spinlock, c->spinlock_flags); + spin_unlock_bh(&c->spinlock); else mutex_unlock(&c->lock); } @@ -817,6 +815,10 @@ static struct dm_buffer *__get_unclaimed_buffer(struct dm_bufio_client *c) BUG_ON(test_bit(B_WRITING, &b->state)); BUG_ON(test_bit(B_DIRTY, &b->state)); + if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep && + unlikely(test_bit(B_READING, &b->state))) + continue; + if (!b->hold_count) { __make_buffer_clean(b); __unlink_buffer(b); @@ -825,6 +827,9 @@ static struct dm_buffer *__get_unclaimed_buffer(struct dm_bufio_client *c) cond_resched(); } + if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep) + return NULL; + list_for_each_entry_reverse(b, &c->lru[LIST_DIRTY], lru_list) { BUG_ON(test_bit(B_READING, &b->state)); @@ -1632,7 +1637,8 @@ static void drop_buffers(struct dm_bufio_client *c) */ static bool __try_evict_buffer(struct dm_buffer *b, gfp_t gfp) { - if (!(gfp & __GFP_FS)) { + if (!(gfp & __GFP_FS) || + (static_branch_unlikely(&no_sleep_enabled) && b->c->no_sleep)) { if (test_bit(B_READING, &b->state) || test_bit(B_WRITING, &b->state) || test_bit(B_DIRTY, &b->state)) diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 2347e83902f1..94b6cb599db4 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -38,7 +38,7 @@ #define DM_VERITY_OPT_AT_MOST_ONCE "check_at_most_once" #define DM_VERITY_OPT_TASKLET_VERIFY "try_verify_in_tasklet" -#define DM_VERITY_OPTS_MAX (3 + DM_VERITY_OPTS_FEC + \ +#define DM_VERITY_OPTS_MAX (4 + DM_VERITY_OPTS_FEC + \ DM_VERITY_ROOT_HASH_VERIFICATION_OPTS) static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE; @@ -1053,7 +1053,7 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v, struct dm_verity_sig_opts *verify_args, bool only_modifier_opts) { - int r; + int r = 0; unsigned argc; struct dm_target *ti = v->ti; const char *arg_name; @@ -1123,8 +1123,18 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v, if (r) return r; continue; + + } else if (only_modifier_opts) { + /* + * Ignore unrecognized opt, could easily be an extra + * argument to an option whose parsing was skipped. + * Normal parsing (@only_modifier_opts=false) will + * properly parse all options (and their extra args). + */ + continue; } + DMERR("Unrecognized verity feature request: %s", arg_name); ti->error = "Unrecognized verity feature request"; return -EINVAL; } while (argc && !r); diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c index 1fc161d65673..96a003eb7323 100644 --- a/drivers/md/dm-writecache.c +++ b/drivers/md/dm-writecache.c @@ -1594,7 +1594,8 @@ done: default: BUG(); - return -1; + wc_unlock(wc); + return DM_MAPIO_KILL; } } diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 007d43e46dcb..b9dbad3a8af8 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -653,6 +653,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond) { struct slave *tx_slave = NULL; + struct net_device *dev; struct arp_pkt *arp; if (!pskb_network_may_pull(skb, sizeof(*arp))) @@ -665,6 +666,15 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond) if (!bond_slave_has_mac_rx(bond, arp->mac_src)) return NULL; + dev = ip_dev_find(dev_net(bond->dev), arp->ip_src); + if (dev) { + if (netif_is_bridge_master(dev)) { + dev_put(dev); + return NULL; + } + dev_put(dev); + } + if (arp->op_code == htons(ARPOP_REPLY)) { /* the arp must be sent on the selected rx channel */ tx_slave = rlb_choose_channel(skb, bond, arp); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index e75acb14d066..50e60843020c 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2001,6 +2001,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev, for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) new_slave->target_last_arp_rx[i] = new_slave->last_rx; + new_slave->last_tx = new_slave->last_rx; + if (bond->params.miimon && !bond->params.use_carrier) { link_reporting = bond_check_dev_link(bond, slave_dev, 1); @@ -2884,8 +2886,11 @@ static void bond_arp_send(struct slave *slave, int arp_op, __be32 dest_ip, return; } - if (bond_handle_vlan(slave, tags, skb)) + if (bond_handle_vlan(slave, tags, skb)) { + slave_update_last_tx(slave); arp_xmit(skb); + } + return; } @@ -3074,8 +3079,7 @@ static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, curr_active_slave->last_link_up)) bond_validate_arp(bond, slave, tip, sip); else if (curr_arp_slave && (arp->ar_op == htons(ARPOP_REPLY)) && - bond_time_in_interval(bond, - dev_trans_start(curr_arp_slave->dev), 1)) + bond_time_in_interval(bond, slave_last_tx(curr_arp_slave), 1)) bond_validate_arp(bond, slave, sip, tip); out_unlock: @@ -3103,8 +3107,10 @@ static void bond_ns_send(struct slave *slave, const struct in6_addr *daddr, } addrconf_addr_solict_mult(daddr, &mcaddr); - if (bond_handle_vlan(slave, tags, skb)) + if (bond_handle_vlan(slave, tags, skb)) { + slave_update_last_tx(slave); ndisc_send_skb(skb, &mcaddr, saddr); + } } static void bond_ns_send_all(struct bonding *bond, struct slave *slave) @@ -3246,8 +3252,7 @@ static int bond_na_rcv(const struct sk_buff *skb, struct bonding *bond, curr_active_slave->last_link_up)) bond_validate_ns(bond, slave, saddr, daddr); else if (curr_arp_slave && - bond_time_in_interval(bond, - dev_trans_start(curr_arp_slave->dev), 1)) + bond_time_in_interval(bond, slave_last_tx(curr_arp_slave), 1)) bond_validate_ns(bond, slave, saddr, daddr); out: @@ -3335,12 +3340,12 @@ static void bond_loadbalance_arp_mon(struct bonding *bond) * so it can wait */ bond_for_each_slave_rcu(bond, slave, iter) { - unsigned long trans_start = dev_trans_start(slave->dev); + unsigned long last_tx = slave_last_tx(slave); bond_propose_link_state(slave, BOND_LINK_NOCHANGE); if (slave->link != BOND_LINK_UP) { - if (bond_time_in_interval(bond, trans_start, 1) && + if (bond_time_in_interval(bond, last_tx, 1) && bond_time_in_interval(bond, slave->last_rx, 1)) { bond_propose_link_state(slave, BOND_LINK_UP); @@ -3365,7 +3370,7 @@ static void bond_loadbalance_arp_mon(struct bonding *bond) * when the source ip is 0, so don't take the link down * if we don't know our ip yet */ - if (!bond_time_in_interval(bond, trans_start, bond->params.missed_max) || + if (!bond_time_in_interval(bond, last_tx, bond->params.missed_max) || !bond_time_in_interval(bond, slave->last_rx, bond->params.missed_max)) { bond_propose_link_state(slave, BOND_LINK_DOWN); @@ -3431,7 +3436,7 @@ re_arm: */ static int bond_ab_arp_inspect(struct bonding *bond) { - unsigned long trans_start, last_rx; + unsigned long last_tx, last_rx; struct list_head *iter; struct slave *slave; int commit = 0; @@ -3482,9 +3487,9 @@ static int bond_ab_arp_inspect(struct bonding *bond) * - (more than missed_max*delta since receive AND * the bond has an IP address) */ - trans_start = dev_trans_start(slave->dev); + last_tx = slave_last_tx(slave); if (bond_is_active_slave(slave) && - (!bond_time_in_interval(bond, trans_start, bond->params.missed_max) || + (!bond_time_in_interval(bond, last_tx, bond->params.missed_max) || !bond_time_in_interval(bond, last_rx, bond->params.missed_max))) { bond_propose_link_state(slave, BOND_LINK_DOWN); commit++; @@ -3501,8 +3506,8 @@ static int bond_ab_arp_inspect(struct bonding *bond) */ static void bond_ab_arp_commit(struct bonding *bond) { - unsigned long trans_start; struct list_head *iter; + unsigned long last_tx; struct slave *slave; bond_for_each_slave(bond, slave, iter) { @@ -3511,10 +3516,10 @@ static void bond_ab_arp_commit(struct bonding *bond) continue; case BOND_LINK_UP: - trans_start = dev_trans_start(slave->dev); + last_tx = slave_last_tx(slave); if (rtnl_dereference(bond->curr_active_slave) != slave || (!rtnl_dereference(bond->curr_active_slave) && - bond_time_in_interval(bond, trans_start, 1))) { + bond_time_in_interval(bond, last_tx, 1))) { struct slave *current_arp_slave; current_arp_slave = rtnl_dereference(bond->current_arp_slave); @@ -5333,8 +5338,14 @@ static struct net_device *bond_sk_get_lower_dev(struct net_device *dev, static netdev_tx_t bond_tls_device_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *dev) { - if (likely(bond_get_slave_by_dev(bond, tls_get_ctx(skb->sk)->netdev))) - return bond_dev_queue_xmit(bond, skb, tls_get_ctx(skb->sk)->netdev); + struct net_device *tls_netdev = rcu_dereference(tls_get_ctx(skb->sk)->netdev); + + /* tls_netdev might become NULL, even if tls_is_sk_tx_device_offloaded + * was true, if tls_device_down is running in parallel, but it's OK, + * because bond_get_slave_by_dev has a NULL check. + */ + if (likely(bond_get_slave_by_dev(bond, tls_netdev))) + return bond_dev_queue_xmit(bond, skb, tls_netdev); return bond_tx_drop(dev, skb); } #endif diff --git a/drivers/net/can/spi/mcp251x.c b/drivers/net/can/spi/mcp251x.c index e750d13c8841..c320de474f40 100644 --- a/drivers/net/can/spi/mcp251x.c +++ b/drivers/net/can/spi/mcp251x.c @@ -1070,9 +1070,6 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) mcp251x_read_2regs(spi, CANINTF, &intf, &eflag); - /* mask out flags we don't care about */ - intf &= CANINTF_RX | CANINTF_TX | CANINTF_ERR; - /* receive buffer 0 */ if (intf & CANINTF_RX0IF) { mcp251x_hw_rx(spi, 0); @@ -1082,6 +1079,18 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) if (mcp251x_is_2510(spi)) mcp251x_write_bits(spi, CANINTF, CANINTF_RX0IF, 0x00); + + /* check if buffer 1 is already known to be full, no need to re-read */ + if (!(intf & CANINTF_RX1IF)) { + u8 intf1, eflag1; + + /* intf needs to be read again to avoid a race condition */ + mcp251x_read_2regs(spi, CANINTF, &intf1, &eflag1); + + /* combine flags from both operations for error handling */ + intf |= intf1; + eflag |= eflag1; + } } /* receive buffer 1 */ @@ -1092,6 +1101,9 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) clear_intf |= CANINTF_RX1IF; } + /* mask out flags we don't care about */ + intf &= CANINTF_RX | CANINTF_TX | CANINTF_ERR; + /* any error or tx interrupt we need to clear? */ if (intf & (CANINTF_ERR | CANINTF_TX)) clear_intf |= intf & (CANINTF_ERR | CANINTF_TX); diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index d1e1a459c045..d31191686a54 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -195,7 +195,7 @@ struct __packed ems_cpc_msg { __le32 ts_sec; /* timestamp in seconds */ __le32 ts_nsec; /* timestamp in nano seconds */ - union { + union __packed { u8 generic[64]; struct cpc_can_msg can_msg; struct cpc_can_params can_params; diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 859196898a7d..aadb0bd7c24f 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -610,6 +610,9 @@ static int felix_change_tag_protocol(struct dsa_switch *ds, old_proto_ops = felix->tag_proto_ops; + if (proto_ops == old_proto_ops) + return 0; + err = proto_ops->setup(ds); if (err) goto setup_failed; diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 61ed317602e7..b4034b78c0ca 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -1137,6 +1137,7 @@ static void vsc9959_tas_min_gate_lengths(struct tc_taprio_qopt_offload *taprio, { struct tc_taprio_sched_entry *entry; u64 gate_len[OCELOT_NUM_TC]; + u8 gates_ever_opened = 0; int tc, i, n; /* Initialize arrays */ @@ -1164,16 +1165,28 @@ static void vsc9959_tas_min_gate_lengths(struct tc_taprio_qopt_offload *taprio, for (tc = 0; tc < OCELOT_NUM_TC; tc++) { if (entry->gate_mask & BIT(tc)) { gate_len[tc] += entry->interval; + gates_ever_opened |= BIT(tc); } else { /* Gate closes now, record a potential new * minimum and reinitialize length */ - if (min_gate_len[tc] > gate_len[tc]) + if (min_gate_len[tc] > gate_len[tc] && + gate_len[tc]) min_gate_len[tc] = gate_len[tc]; gate_len[tc] = 0; } } } + + /* min_gate_len[tc] actually tracks minimum *open* gate time, so for + * permanently closed gates, min_gate_len[tc] will still be U64_MAX. + * Therefore they are currently indistinguishable from permanently + * open gates. Overwrite the gate len with 0 when we know they're + * actually permanently closed, i.e. after the loop above. + */ + for (tc = 0; tc < OCELOT_NUM_TC; tc++) + if (!(gates_ever_opened & BIT(tc))) + min_gate_len[tc] = 0; } /* Update QSYS_PORT_MAX_SDU to make sure the static guard bands added by the diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index e11cc29d3264..06508eebb585 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -265,12 +265,10 @@ static void aq_nic_service_timer_cb(struct timer_list *t) static void aq_nic_polling_timer_cb(struct timer_list *t) { struct aq_nic_s *self = from_timer(self, t, polling_timer); - struct aq_vec_s *aq_vec = NULL; unsigned int i = 0U; - for (i = 0U, aq_vec = self->aq_vec[0]; - self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i]) - aq_vec_isr(i, (void *)aq_vec); + for (i = 0U; self->aq_vecs > i; ++i) + aq_vec_isr(i, (void *)self->aq_vec[i]); mod_timer(&self->polling_timer, jiffies + AQ_CFG_POLLING_TIMER_INTERVAL); @@ -1014,7 +1012,6 @@ int aq_nic_get_regs_count(struct aq_nic_s *self) u64 *aq_nic_get_stats(struct aq_nic_s *self, u64 *data) { - struct aq_vec_s *aq_vec = NULL; struct aq_stats_s *stats; unsigned int count = 0U; unsigned int i = 0U; @@ -1064,11 +1061,11 @@ u64 *aq_nic_get_stats(struct aq_nic_s *self, u64 *data) data += i; for (tc = 0U; tc < self->aq_nic_cfg.tcs; tc++) { - for (i = 0U, aq_vec = self->aq_vec[0]; - aq_vec && self->aq_vecs > i; - ++i, aq_vec = self->aq_vec[i]) { + for (i = 0U; self->aq_vecs > i; ++i) { + if (!self->aq_vec[i]) + break; data += count; - count = aq_vec_get_sw_stats(aq_vec, tc, data); + count = aq_vec_get_sw_stats(self->aq_vec[i], tc, data); } } @@ -1382,7 +1379,6 @@ int aq_nic_set_loopback(struct aq_nic_s *self) int aq_nic_stop(struct aq_nic_s *self) { - struct aq_vec_s *aq_vec = NULL; unsigned int i = 0U; netif_tx_disable(self->ndev); @@ -1400,9 +1396,8 @@ int aq_nic_stop(struct aq_nic_s *self) aq_ptp_irq_free(self); - for (i = 0U, aq_vec = self->aq_vec[0]; - self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i]) - aq_vec_stop(aq_vec); + for (i = 0U; self->aq_vecs > i; ++i) + aq_vec_stop(self->aq_vec[i]); aq_ptp_ring_stop(self); diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 2dfc1e32bbb3..93580484a3f4 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -189,8 +189,8 @@ static netdev_tx_t bgmac_dma_tx_add(struct bgmac *bgmac, } slot->skb = skb; - ring->end += nr_frags + 1; netdev_sent_queue(net_dev, skb->len); + ring->end += nr_frags + 1; wmb(); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c index 14df8cfc2946..059f96f7a96f 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c @@ -21,7 +21,6 @@ #include "bnxt_ptp.h" #include "bnxt_coredump.h" #include "bnxt_nvm_defs.h" -#include "bnxt_ethtool.h" static void __bnxt_fw_recover(struct bnxt *bp) { diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index c888ddee1fc4..7ded559842e8 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -393,6 +393,9 @@ int bcmgenet_mii_probe(struct net_device *dev) if (priv->internal_phy && !GENET_IS_V5(priv)) dev->phydev->irq = PHY_MAC_INTERRUPT; + /* Indicate that the MAC is responsible for PHY PM */ + dev->phydev->mac_managed_pm = true; + return 0; } diff --git a/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c b/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c index bfee0e4e54b1..da9973b711f4 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/ch_ktls/chcr_ktls.c @@ -1932,6 +1932,7 @@ static int chcr_ktls_xmit(struct sk_buff *skb, struct net_device *dev) int data_len, qidx, ret = 0, mss; struct tls_record_info *record; struct chcr_ktls_info *tx_info; + struct net_device *tls_netdev; struct tls_context *tls_ctx; struct sge_eth_txq *q; struct adapter *adap; @@ -1945,7 +1946,12 @@ static int chcr_ktls_xmit(struct sk_buff *skb, struct net_device *dev) mss = skb_is_gso(skb) ? skb_shinfo(skb)->gso_size : data_len; tls_ctx = tls_get_ctx(skb->sk); - if (unlikely(tls_ctx->netdev != dev)) + tls_netdev = rcu_dereference_bh(tls_ctx->netdev); + /* Don't quit on NULL: if tls_device_down is running in parallel, + * netdev might become NULL, even if tls_is_sk_tx_device_offloaded was + * true. Rather continue processing this packet. + */ + if (unlikely(tls_netdev && tls_netdev != dev)) goto out; tx_ctx = chcr_get_ktls_tx_context(tls_ctx); diff --git a/drivers/net/ethernet/engleder/tsnep_main.c b/drivers/net/ethernet/engleder/tsnep_main.c index cb069a0af7b9..a5f7152a1716 100644 --- a/drivers/net/ethernet/engleder/tsnep_main.c +++ b/drivers/net/ethernet/engleder/tsnep_main.c @@ -340,14 +340,14 @@ static int tsnep_tx_map(struct sk_buff *skb, struct tsnep_tx *tx, int count) return 0; } -static void tsnep_tx_unmap(struct tsnep_tx *tx, int count) +static void tsnep_tx_unmap(struct tsnep_tx *tx, int index, int count) { struct device *dmadev = tx->adapter->dmadev; struct tsnep_tx_entry *entry; int i; for (i = 0; i < count; i++) { - entry = &tx->entry[(tx->read + i) % TSNEP_RING_SIZE]; + entry = &tx->entry[(index + i) % TSNEP_RING_SIZE]; if (entry->len) { if (i == 0) @@ -395,7 +395,7 @@ static netdev_tx_t tsnep_xmit_frame_ring(struct sk_buff *skb, retval = tsnep_tx_map(skb, tx, count); if (retval != 0) { - tsnep_tx_unmap(tx, count); + tsnep_tx_unmap(tx, tx->write, count); dev_kfree_skb_any(entry->skb); entry->skb = NULL; @@ -464,7 +464,7 @@ static bool tsnep_tx_poll(struct tsnep_tx *tx, int napi_budget) if (skb_shinfo(entry->skb)->nr_frags > 0) count += skb_shinfo(entry->skb)->nr_frags; - tsnep_tx_unmap(tx, count); + tsnep_tx_unmap(tx, tx->read, count); if ((skb_shinfo(entry->skb)->tx_flags & SKBTX_IN_PROGRESS) && (__le32_to_cpu(entry->desc_wb->properties) & @@ -1282,7 +1282,7 @@ MODULE_DEVICE_TABLE(of, tsnep_of_match); static struct platform_driver tsnep_driver = { .driver = { .name = TSNEP, - .of_match_table = of_match_ptr(tsnep_of_match), + .of_match_table = tsnep_of_match, }, .probe = tsnep_probe, .remove = tsnep_remove, diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index cd9ec80522e7..75d51572693d 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -1660,8 +1660,8 @@ static int dpaa2_eth_add_bufs(struct dpaa2_eth_priv *priv, buf_array[i] = addr; /* tracing point */ - trace_dpaa2_eth_buf_seed(priv->net_dev, - page, DPAA2_ETH_RX_BUF_RAW_SIZE, + trace_dpaa2_eth_buf_seed(priv->net_dev, page_address(page), + DPAA2_ETH_RX_BUF_RAW_SIZE, addr, priv->rx_buf_size, bpid); } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c index 6809b8b4c556..7282a826d81e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -2580,6 +2580,12 @@ static void __rvu_flr_handler(struct rvu *rvu, u16 pcifunc) rvu_blklf_teardown(rvu, pcifunc, BLKADDR_NPA); rvu_reset_lmt_map_tbl(rvu, pcifunc); rvu_detach_rsrcs(rvu, NULL, pcifunc); + /* In scenarios where PF/VF drivers detach NIXLF without freeing MCAM + * entries, check and free the MCAM entries explicitly to avoid leak. + * Since LF is detached use LF number as -1. + */ + rvu_npc_free_mcam_entries(rvu, pcifunc, -1); + mutex_unlock(&rvu->flr_lock); } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index 583ead4dd246..1e348fd0d930 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -1097,6 +1097,9 @@ static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc, void rvu_npc_disable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf) { + if (nixlf < 0) + return; + npc_enadis_default_entries(rvu, pcifunc, nixlf, false); /* Delete multicast and promisc MCAM entries */ @@ -1136,6 +1139,9 @@ bool rvu_npc_enable_mcam_by_entry_index(struct rvu *rvu, int entry, int intf, bo void rvu_npc_enable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf) { + if (nixlf < 0) + return; + /* Enables only broadcast match entry. Promisc/Allmulti are enabled * in set_rx_mode mbox handler. */ @@ -1675,7 +1681,7 @@ static void npc_load_kpu_profile(struct rvu *rvu) * Firmware database method. * Default KPU profile. */ - if (!request_firmware(&fw, kpu_profile, rvu->dev)) { + if (!request_firmware_direct(&fw, kpu_profile, rvu->dev)) { dev_info(rvu->dev, "Loading KPU profile from firmware: %s\n", kpu_profile); rvu->kpu_fwdata = kzalloc(fw->size, GFP_KERNEL); @@ -1939,6 +1945,7 @@ static void rvu_npc_hw_init(struct rvu *rvu, int blkaddr) static void rvu_npc_setup_interfaces(struct rvu *rvu, int blkaddr) { + struct npc_mcam_kex *mkex = rvu->kpu.mkex; struct npc_mcam *mcam = &rvu->hw->mcam; struct rvu_hwinfo *hw = rvu->hw; u64 nibble_ena, rx_kex, tx_kex; @@ -1951,15 +1958,15 @@ static void rvu_npc_setup_interfaces(struct rvu *rvu, int blkaddr) mcam->counters.max--; mcam->rx_miss_act_cntr = mcam->counters.max; - rx_kex = npc_mkex_default.keyx_cfg[NIX_INTF_RX]; - tx_kex = npc_mkex_default.keyx_cfg[NIX_INTF_TX]; + rx_kex = mkex->keyx_cfg[NIX_INTF_RX]; + tx_kex = mkex->keyx_cfg[NIX_INTF_TX]; nibble_ena = FIELD_GET(NPC_PARSE_NIBBLE, rx_kex); nibble_ena = rvu_npc_get_tx_nibble_cfg(rvu, nibble_ena); if (nibble_ena) { tx_kex &= ~NPC_PARSE_NIBBLE; tx_kex |= FIELD_PREP(NPC_PARSE_NIBBLE, nibble_ena); - npc_mkex_default.keyx_cfg[NIX_INTF_TX] = tx_kex; + mkex->keyx_cfg[NIX_INTF_TX] = tx_kex; } /* Configure RX interfaces */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index a400aa22da79..7c4e1acd0f77 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -467,7 +467,8 @@ do { \ NPC_SCAN_HDR(NPC_VLAN_TAG1, NPC_LID_LB, NPC_LT_LB_CTAG, 2, 2); NPC_SCAN_HDR(NPC_VLAN_TAG2, NPC_LID_LB, NPC_LT_LB_STAG_QINQ, 2, 2); NPC_SCAN_HDR(NPC_DMAC, NPC_LID_LA, la_ltype, la_start, 6); - NPC_SCAN_HDR(NPC_SMAC, NPC_LID_LA, la_ltype, la_start, 6); + /* SMAC follows the DMAC(which is 6 bytes) */ + NPC_SCAN_HDR(NPC_SMAC, NPC_LID_LA, la_ltype, la_start + 6, 6); /* PF_FUNC is 2 bytes at 0th byte of NPC_LT_LA_IH_NIX_ETHER */ NPC_SCAN_HDR(NPC_PF_FUNC, NPC_LID_LA, NPC_LT_LA_IH_NIX_ETHER, 0, 2); } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index fb8db5888d2f..d686c7b6252f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -632,6 +632,12 @@ int otx2_txschq_config(struct otx2_nic *pfvf, int lvl) req->num_regs++; req->reg[1] = NIX_AF_TL3X_SCHEDULE(schq); req->regval[1] = dwrr_val; + if (lvl == hw->txschq_link_cfg_lvl) { + req->num_regs++; + req->reg[2] = NIX_AF_TL3_TL2X_LINKX_CFG(schq, hw->tx_link); + /* Enable this queue and backpressure */ + req->regval[2] = BIT_ULL(13) | BIT_ULL(12); + } } else if (lvl == NIX_TXSCH_LVL_TL2) { parent = hw->txschq_list[NIX_TXSCH_LVL_TL1][0]; req->reg[0] = NIX_AF_TL2X_PARENT(schq); @@ -641,11 +647,12 @@ int otx2_txschq_config(struct otx2_nic *pfvf, int lvl) req->reg[1] = NIX_AF_TL2X_SCHEDULE(schq); req->regval[1] = TXSCH_TL1_DFLT_RR_PRIO << 24 | dwrr_val; - req->num_regs++; - req->reg[2] = NIX_AF_TL3_TL2X_LINKX_CFG(schq, hw->tx_link); - /* Enable this queue and backpressure */ - req->regval[2] = BIT_ULL(13) | BIT_ULL(12); - + if (lvl == hw->txschq_link_cfg_lvl) { + req->num_regs++; + req->reg[2] = NIX_AF_TL3_TL2X_LINKX_CFG(schq, hw->tx_link); + /* Enable this queue and backpressure */ + req->regval[2] = BIT_ULL(13) | BIT_ULL(12); + } } else if (lvl == NIX_TXSCH_LVL_TL1) { /* Default config for TL1. * For VF this is always ignored. @@ -1591,6 +1598,8 @@ void mbox_handler_nix_txsch_alloc(struct otx2_nic *pf, for (schq = 0; schq < rsp->schq[lvl]; schq++) pf->hw.txschq_list[lvl][schq] = rsp->schq_list[lvl][schq]; + + pf->hw.txschq_link_cfg_lvl = rsp->link_cfg_lvl; } EXPORT_SYMBOL(mbox_handler_nix_txsch_alloc); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index e795f9ee76dd..b28029cc4316 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -195,6 +195,7 @@ struct otx2_hw { u16 sqb_size; /* NIX */ + u8 txschq_link_cfg_lvl; u16 txschq_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC]; u16 matchall_ipolicer; u32 dwrr_mtu; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c index d87bbb0be7c8..e6f64d890fb3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c @@ -506,7 +506,7 @@ int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv, int err; attr.ttl = tun_key->ttl; - attr.fl.fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tun_key->tos), tun_key->label); + attr.fl.fl6.flowlabel = ip6_make_flowinfo(tun_key->tos, tun_key->label); attr.fl.fl6.daddr = tun_key->u.ipv6.dst; attr.fl.fl6.saddr = tun_key->u.ipv6.src; @@ -620,7 +620,7 @@ int mlx5e_tc_tun_update_header_ipv6(struct mlx5e_priv *priv, attr.ttl = tun_key->ttl; - attr.fl.fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tun_key->tos), tun_key->label); + attr.fl.fl6.flowlabel = ip6_make_flowinfo(tun_key->tos, tun_key->label); attr.fl.fl6.daddr = tun_key->u.ipv6.dst; attr.fl.fl6.saddr = tun_key->u.ipv6.src; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c index 6b6c7044b64a..0aef69527226 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c @@ -808,6 +808,7 @@ bool mlx5e_ktls_handle_tx_skb(struct net_device *netdev, struct mlx5e_txqsq *sq, { struct mlx5e_ktls_offload_context_tx *priv_tx; struct mlx5e_sq_stats *stats = sq->stats; + struct net_device *tls_netdev; struct tls_context *tls_ctx; int datalen; u32 seq; @@ -819,7 +820,12 @@ bool mlx5e_ktls_handle_tx_skb(struct net_device *netdev, struct mlx5e_txqsq *sq, mlx5e_tx_mpwqe_ensure_complete(sq); tls_ctx = tls_get_ctx(skb->sk); - if (WARN_ON_ONCE(tls_ctx->netdev != netdev)) + tls_netdev = rcu_dereference_bh(tls_ctx->netdev); + /* Don't WARN on NULL: if tls_device_down is running in parallel, + * netdev might become NULL, even if tls_is_sk_tx_device_offloaded was + * true. Rather continue processing this packet. + */ + if (WARN_ON_ONCE(tls_netdev && tls_netdev != netdev)) goto err_out; priv_tx = mlx5e_get_ktls_tx_priv_ctx(tls_ctx); diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c index d9bf584234a6..bb1cd4bae82e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c @@ -328,7 +328,6 @@ static void mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 module) static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) { unsigned int max_ports = mlxsw_core_max_ports(mlxsw_m->core); - struct devlink *devlink = priv_to_devlink(mlxsw_m->core); u8 last_module = max_ports; int i; int err; @@ -357,7 +356,6 @@ static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) } /* Create port objects for each valid entry */ - devl_lock(devlink); for (i = 0; i < mlxsw_m->max_ports; i++) { if (mlxsw_m->module_to_port[i] > 0) { err = mlxsw_m_port_create(mlxsw_m, @@ -367,7 +365,6 @@ static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m) goto err_module_to_port_create; } } - devl_unlock(devlink); return 0; @@ -377,7 +374,6 @@ err_module_to_port_create: mlxsw_m_port_remove(mlxsw_m, mlxsw_m->module_to_port[i]); } - devl_unlock(devlink); i = max_ports; err_module_to_port_map: for (i--; i > 0; i--) @@ -390,10 +386,8 @@ err_module_to_port_alloc: static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m) { - struct devlink *devlink = priv_to_devlink(mlxsw_m->core); int i; - devl_lock(devlink); for (i = 0; i < mlxsw_m->max_ports; i++) { if (mlxsw_m->module_to_port[i] > 0) { mlxsw_m_port_remove(mlxsw_m, @@ -401,7 +395,6 @@ static void mlxsw_m_ports_remove(struct mlxsw_m *mlxsw_m) mlxsw_m_port_module_unmap(mlxsw_m, i); } } - devl_unlock(devlink); kfree(mlxsw_m->module_to_port); kfree(mlxsw_m->ports); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index c922dfab8080..eeb1455a4e5d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -1395,6 +1395,8 @@ nfp_port_get_module_info(struct net_device *netdev, u8 data; port = nfp_port_from_netdev(netdev); + /* update port state to get latest interface */ + set_bit(NFP_PORT_CHANGED, &port->flags); eth_port = nfp_port_get_eth_port(port); if (!eth_port) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c index 34c0d2ddf9ef..a8286d0032d1 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c @@ -874,7 +874,6 @@ area_cache_get(struct nfp_cpp *cpp, u32 id, } /* Adjust the start address to be cache size aligned */ - cache->id = id; cache->addr = addr & ~(u64)(cache->size - 1); /* Re-init to the new ID and address */ @@ -894,6 +893,8 @@ area_cache_get(struct nfp_cpp *cpp, u32 id, return NULL; } + cache->id = id; + exit: /* Adjust offset */ *offset = addr - cache->addr; diff --git a/drivers/net/ethernet/wangxun/Kconfig b/drivers/net/ethernet/wangxun/Kconfig index baa1f0a5cc37..b4a4fa0a58f8 100644 --- a/drivers/net/ethernet/wangxun/Kconfig +++ b/drivers/net/ethernet/wangxun/Kconfig @@ -7,12 +7,12 @@ config NET_VENDOR_WANGXUN bool "Wangxun devices" default y help - If you have a network (Ethernet) card belonging to this class, say Y. + If you have a network (Ethernet) card from Wangxun(R), say Y. Note that the answer to this question doesn't directly affect the kernel: saying N will just cause the configurator to skip all - the questions about Intel cards. If you say Y, you will be asked for - your specific card in the following questions. + the questions about Wangxun(R) cards. If you say Y, you will + be asked for your specific card in the following questions. if NET_VENDOR_WANGXUN diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 018d365f9deb..7962c37b3f14 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -797,7 +797,8 @@ static struct rtable *geneve_get_v4_rt(struct sk_buff *skb, struct geneve_sock *gs4, struct flowi4 *fl4, const struct ip_tunnel_info *info, - __be16 dport, __be16 sport) + __be16 dport, __be16 sport, + __u8 *full_tos) { bool use_cache = ip_tunnel_dst_cache_usable(skb, info); struct geneve_dev *geneve = netdev_priv(dev); @@ -823,6 +824,8 @@ static struct rtable *geneve_get_v4_rt(struct sk_buff *skb, use_cache = false; } fl4->flowi4_tos = RT_TOS(tos); + if (full_tos) + *full_tos = tos; dst_cache = (struct dst_cache *)&info->dst_cache; if (use_cache) { @@ -876,8 +879,7 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb, use_cache = false; } - fl6->flowlabel = ip6_make_flowinfo(RT_TOS(prio), - info->key.label); + fl6->flowlabel = ip6_make_flowinfo(prio, info->key.label); dst_cache = (struct dst_cache *)&info->dst_cache; if (use_cache) { dst = dst_cache_get_ip6(dst_cache, &fl6->saddr); @@ -911,6 +913,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, const struct ip_tunnel_key *key = &info->key; struct rtable *rt; struct flowi4 fl4; + __u8 full_tos; __u8 tos, ttl; __be16 df = 0; __be16 sport; @@ -921,7 +924,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true); rt = geneve_get_v4_rt(skb, dev, gs4, &fl4, info, - geneve->cfg.info.key.tp_dst, sport); + geneve->cfg.info.key.tp_dst, sport, &full_tos); if (IS_ERR(rt)) return PTR_ERR(rt); @@ -965,7 +968,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; } else { - tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, ip_hdr(skb), skb); + tos = ip_tunnel_ecn_encap(full_tos, ip_hdr(skb), skb); if (geneve->cfg.ttl_inherit) ttl = ip_tunnel_get_ttl(ip_hdr(skb), skb); else @@ -1149,7 +1152,7 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) 1, USHRT_MAX, true); rt = geneve_get_v4_rt(skb, dev, gs4, &fl4, info, - geneve->cfg.info.key.tp_dst, sport); + geneve->cfg.info.key.tp_dst, sport, NULL); if (IS_ERR(rt)) return PTR_ERR(rt); diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index f1683ce6b561..ee6087e7b2bf 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -162,6 +162,19 @@ static struct macsec_rx_sa *macsec_rxsa_get(struct macsec_rx_sa __rcu *ptr) return sa; } +static struct macsec_rx_sa *macsec_active_rxsa_get(struct macsec_rx_sc *rx_sc) +{ + struct macsec_rx_sa *sa = NULL; + int an; + + for (an = 0; an < MACSEC_NUM_AN; an++) { + sa = macsec_rxsa_get(rx_sc->sa[an]); + if (sa) + break; + } + return sa; +} + static void free_rx_sc_rcu(struct rcu_head *head) { struct macsec_rx_sc *rx_sc = container_of(head, struct macsec_rx_sc, rcu_head); @@ -500,18 +513,28 @@ static void macsec_encrypt_finish(struct sk_buff *skb, struct net_device *dev) skb->protocol = eth_hdr(skb)->h_proto; } +static unsigned int macsec_msdu_len(struct sk_buff *skb) +{ + struct macsec_dev *macsec = macsec_priv(skb->dev); + struct macsec_secy *secy = &macsec->secy; + bool sci_present = macsec_skb_cb(skb)->has_sci; + + return skb->len - macsec_hdr_len(sci_present) - secy->icv_len; +} + static void macsec_count_tx(struct sk_buff *skb, struct macsec_tx_sc *tx_sc, struct macsec_tx_sa *tx_sa) { + unsigned int msdu_len = macsec_msdu_len(skb); struct pcpu_tx_sc_stats *txsc_stats = this_cpu_ptr(tx_sc->stats); u64_stats_update_begin(&txsc_stats->syncp); if (tx_sc->encrypt) { - txsc_stats->stats.OutOctetsEncrypted += skb->len; + txsc_stats->stats.OutOctetsEncrypted += msdu_len; txsc_stats->stats.OutPktsEncrypted++; this_cpu_inc(tx_sa->stats->OutPktsEncrypted); } else { - txsc_stats->stats.OutOctetsProtected += skb->len; + txsc_stats->stats.OutOctetsProtected += msdu_len; txsc_stats->stats.OutPktsProtected++; this_cpu_inc(tx_sa->stats->OutPktsProtected); } @@ -541,9 +564,10 @@ static void macsec_encrypt_done(struct crypto_async_request *base, int err) aead_request_free(macsec_skb_cb(skb)->req); rcu_read_lock_bh(); - macsec_encrypt_finish(skb, dev); macsec_count_tx(skb, &macsec->secy.tx_sc, macsec_skb_cb(skb)->tx_sa); - len = skb->len; + /* packet is encrypted/protected so tx_bytes must be calculated */ + len = macsec_msdu_len(skb) + 2 * ETH_ALEN; + macsec_encrypt_finish(skb, dev); ret = dev_queue_xmit(skb); count_tx(dev, ret, len); rcu_read_unlock_bh(); @@ -702,6 +726,7 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb, macsec_skb_cb(skb)->req = req; macsec_skb_cb(skb)->tx_sa = tx_sa; + macsec_skb_cb(skb)->has_sci = sci_present; aead_request_set_callback(req, 0, macsec_encrypt_done, skb); dev_hold(skb->dev); @@ -743,15 +768,17 @@ static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsLate++; u64_stats_update_end(&rxsc_stats->syncp); + secy->netdev->stats.rx_dropped++; return false; } if (secy->validate_frames != MACSEC_VALIDATE_DISABLED) { + unsigned int msdu_len = macsec_msdu_len(skb); u64_stats_update_begin(&rxsc_stats->syncp); if (hdr->tci_an & MACSEC_TCI_E) - rxsc_stats->stats.InOctetsDecrypted += skb->len; + rxsc_stats->stats.InOctetsDecrypted += msdu_len; else - rxsc_stats->stats.InOctetsValidated += skb->len; + rxsc_stats->stats.InOctetsValidated += msdu_len; u64_stats_update_end(&rxsc_stats->syncp); } @@ -764,6 +791,8 @@ static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsNotValid++; u64_stats_update_end(&rxsc_stats->syncp); + this_cpu_inc(rx_sa->stats->InPktsNotValid); + secy->netdev->stats.rx_errors++; return false; } @@ -856,9 +885,9 @@ static void macsec_decrypt_done(struct crypto_async_request *base, int err) macsec_finalize_skb(skb, macsec->secy.icv_len, macsec_extra_len(macsec_skb_cb(skb)->has_sci)); + len = skb->len; macsec_reset_skb(skb, macsec->secy.netdev); - len = skb->len; if (gro_cells_receive(&macsec->gro_cells, skb) == NET_RX_SUCCESS) count_rx(dev, len); @@ -1049,6 +1078,7 @@ static enum rx_handler_result handle_not_macsec(struct sk_buff *skb) u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsNoTag++; u64_stats_update_end(&secy_stats->syncp); + macsec->secy.netdev->stats.rx_dropped++; continue; } @@ -1158,6 +1188,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsBadTag++; u64_stats_update_end(&secy_stats->syncp); + secy->netdev->stats.rx_errors++; goto drop_nosa; } @@ -1168,11 +1199,15 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) /* If validateFrames is Strict or the C bit in the * SecTAG is set, discard */ + struct macsec_rx_sa *active_rx_sa = macsec_active_rxsa_get(rx_sc); if (hdr->tci_an & MACSEC_TCI_C || secy->validate_frames == MACSEC_VALIDATE_STRICT) { u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsNotUsingSA++; u64_stats_update_end(&rxsc_stats->syncp); + secy->netdev->stats.rx_errors++; + if (active_rx_sa) + this_cpu_inc(active_rx_sa->stats->InPktsNotUsingSA); goto drop_nosa; } @@ -1182,6 +1217,8 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsUnusedSA++; u64_stats_update_end(&rxsc_stats->syncp); + if (active_rx_sa) + this_cpu_inc(active_rx_sa->stats->InPktsUnusedSA); goto deliver; } @@ -1202,6 +1239,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsLate++; u64_stats_update_end(&rxsc_stats->syncp); + macsec->secy.netdev->stats.rx_dropped++; goto drop; } } @@ -1230,6 +1268,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) deliver: macsec_finalize_skb(skb, secy->icv_len, macsec_extra_len(macsec_skb_cb(skb)->has_sci)); + len = skb->len; macsec_reset_skb(skb, secy->netdev); if (rx_sa) @@ -1237,7 +1276,6 @@ deliver: macsec_rxsc_put(rx_sc); skb_orphan(skb); - len = skb->len; ret = gro_cells_receive(&macsec->gro_cells, skb); if (ret == NET_RX_SUCCESS) count_rx(dev, len); @@ -1279,6 +1317,7 @@ nosci: u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsNoSCI++; u64_stats_update_end(&secy_stats->syncp); + macsec->secy.netdev->stats.rx_errors++; continue; } @@ -3404,6 +3443,7 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } + len = skb->len; skb = macsec_encrypt(skb, dev); if (IS_ERR(skb)) { if (PTR_ERR(skb) != -EINPROGRESS) @@ -3414,7 +3454,6 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb, macsec_count_tx(skb, &macsec->secy.tx_sc, macsec_skb_cb(skb)->tx_sa); macsec_encrypt_finish(skb, dev); - len = skb->len; ret = dev_queue_xmit(skb); count_tx(dev, ret, len); return ret; @@ -3662,6 +3701,7 @@ static void macsec_get_stats64(struct net_device *dev, s->rx_dropped = dev->stats.rx_dropped; s->tx_dropped = dev->stats.tx_dropped; + s->rx_errors = dev->stats.rx_errors; } static int macsec_get_iflink(const struct net_device *dev) diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 1e38039c5c56..6939563d3b7c 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -535,7 +535,7 @@ static int dp83867_of_init_io_impedance(struct phy_device *phydev) cell = of_nvmem_cell_get(of_node, "io_impedance_ctrl"); if (IS_ERR(cell)) { ret = PTR_ERR(cell); - if (ret != -ENOENT) + if (ret != -ENOENT && ret != -EOPNOTSUPP) return phydev_err_probe(phydev, ret, "failed to get nvmem cell io_impedance_ctrl\n"); diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c index 29b1df03f3e8..a87a4b3ffce4 100644 --- a/drivers/net/phy/phy-c45.c +++ b/drivers/net/phy/phy-c45.c @@ -190,44 +190,42 @@ EXPORT_SYMBOL_GPL(genphy_c45_pma_setup_forced); */ static int genphy_c45_baset1_an_config_aneg(struct phy_device *phydev) { + u16 adv_l_mask, adv_l = 0; + u16 adv_m_mask, adv_m = 0; int changed = 0; - u16 adv_l = 0; - u16 adv_m = 0; int ret; + adv_l_mask = MDIO_AN_T1_ADV_L_FORCE_MS | MDIO_AN_T1_ADV_L_PAUSE_CAP | + MDIO_AN_T1_ADV_L_PAUSE_ASYM; + adv_m_mask = MDIO_AN_T1_ADV_M_MST | MDIO_AN_T1_ADV_M_B10L; + switch (phydev->master_slave_set) { case MASTER_SLAVE_CFG_MASTER_FORCE: + adv_m |= MDIO_AN_T1_ADV_M_MST; + fallthrough; case MASTER_SLAVE_CFG_SLAVE_FORCE: adv_l |= MDIO_AN_T1_ADV_L_FORCE_MS; break; case MASTER_SLAVE_CFG_MASTER_PREFERRED: + adv_m |= MDIO_AN_T1_ADV_M_MST; + fallthrough; case MASTER_SLAVE_CFG_SLAVE_PREFERRED: break; case MASTER_SLAVE_CFG_UNKNOWN: case MASTER_SLAVE_CFG_UNSUPPORTED: - return 0; + /* if master/slave role is not specified, do not overwrite it */ + adv_l_mask &= ~MDIO_AN_T1_ADV_L_FORCE_MS; + adv_m_mask &= ~MDIO_AN_T1_ADV_M_MST; + break; default: phydev_warn(phydev, "Unsupported Master/Slave mode\n"); return -EOPNOTSUPP; } - switch (phydev->master_slave_set) { - case MASTER_SLAVE_CFG_MASTER_FORCE: - case MASTER_SLAVE_CFG_MASTER_PREFERRED: - adv_m |= MDIO_AN_T1_ADV_M_MST; - break; - case MASTER_SLAVE_CFG_SLAVE_FORCE: - case MASTER_SLAVE_CFG_SLAVE_PREFERRED: - break; - default: - break; - } - adv_l |= linkmode_adv_to_mii_t1_adv_l_t(phydev->advertising); ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_L, - (MDIO_AN_T1_ADV_L_FORCE_MS | MDIO_AN_T1_ADV_L_PAUSE_CAP - | MDIO_AN_T1_ADV_L_PAUSE_ASYM), adv_l); + adv_l_mask, adv_l); if (ret < 0) return ret; if (ret > 0) @@ -236,7 +234,7 @@ static int genphy_c45_baset1_an_config_aneg(struct phy_device *phydev) adv_m |= linkmode_adv_to_mii_t1_adv_m_t(phydev->advertising); ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_M, - MDIO_AN_T1_ADV_M_MST | MDIO_AN_T1_ADV_M_B10L, adv_m); + adv_m_mask, adv_m); if (ret < 0) return ret; if (ret > 0) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index a74b320f5b27..0c6efd792690 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -316,6 +316,12 @@ static __maybe_unused int mdio_bus_phy_resume(struct device *dev) phydev->suspended_by_mdio_bus = 0; + /* If we managed to get here with the PHY state machine in a state other + * than PHY_HALTED this is an indication that something went wrong and + * we should most likely be using MAC managed PM and we are not. + */ + WARN_ON(phydev->state != PHY_HALTED && !phydev->mac_managed_pm); + ret = phy_init_hw(phydev); if (ret < 0) return ret; diff --git a/drivers/net/plip/plip.c b/drivers/net/plip/plip.c index dafd3e9ebbf8..c8791e9b451d 100644 --- a/drivers/net/plip/plip.c +++ b/drivers/net/plip/plip.c @@ -1111,7 +1111,7 @@ plip_open(struct net_device *dev) /* Any address will do - we take the first. We already have the first two bytes filled with 0xfc, from plip_init_dev(). */ - const struct in_ifaddr *ifa = rcu_dereference(in_dev->ifa_list); + const struct in_ifaddr *ifa = rtnl_dereference(in_dev->ifa_list); if (ifa != NULL) { dev_addr_mod(dev, 2, &ifa->ifa_local, 4); } diff --git a/drivers/net/tap.c b/drivers/net/tap.c index c3d42062559d..9e75ed3f08ce 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -716,10 +716,20 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, skb_reset_mac_header(skb); skb->protocol = eth_hdr(skb)->h_proto; + rcu_read_lock(); + tap = rcu_dereference(q->tap); + if (!tap) { + kfree_skb(skb); + rcu_read_unlock(); + return total_len; + } + skb->dev = tap->dev; + if (vnet_hdr_len) { err = virtio_net_hdr_to_skb(skb, &vnet_hdr, tap_is_little_endian(q)); if (err) { + rcu_read_unlock(); drop_reason = SKB_DROP_REASON_DEV_HDR; goto err_kfree; } @@ -732,8 +742,6 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, __vlan_get_protocol(skb, skb->protocol, &depth) != 0) skb_set_network_header(skb, depth); - rcu_read_lock(); - tap = rcu_dereference(q->tap); /* copy skb_ubuf_info for callback when skb has no error */ if (zerocopy) { skb_zcopy_init(skb, msg_control); @@ -742,14 +750,8 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control, uarg->callback(NULL, uarg, false); } - if (tap) { - skb->dev = tap->dev; - dev_queue_xmit(skb); - } else { - kfree_skb(skb); - } + dev_queue_xmit(skb); rcu_read_unlock(); - return total_len; err_kfree: diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index 0ad468a00064..aff39bf3161d 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -1680,7 +1680,7 @@ static const struct driver_info ax88179_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1693,7 +1693,7 @@ static const struct driver_info ax88178a_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1706,7 +1706,7 @@ static const struct driver_info cypress_GX3_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1719,7 +1719,7 @@ static const struct driver_info dlink_dub1312_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1732,7 +1732,7 @@ static const struct driver_info sitecom_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1745,7 +1745,7 @@ static const struct driver_info samsung_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1758,7 +1758,7 @@ static const struct driver_info lenovo_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1771,7 +1771,7 @@ static const struct driver_info belkin_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1784,7 +1784,7 @@ static const struct driver_info toshiba_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1797,7 +1797,7 @@ static const struct driver_info mct_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1810,7 +1810,7 @@ static const struct driver_info at_umc2000_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1823,7 +1823,7 @@ static const struct driver_info at_umc200_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; @@ -1836,7 +1836,7 @@ static const struct driver_info at_umc2000sp_info = { .link_reset = ax88179_link_reset, .reset = ax88179_reset, .stop = ax88179_stop, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_SEND_ZLP, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, .rx_fixup = ax88179_rx_fixup, .tx_fixup = ax88179_tx_fixup, }; diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 571a399c195d..709e3c59e340 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1390,6 +1390,8 @@ static const struct usb_device_id products[] = { {QMI_QUIRK_SET_DTR(0x1e2d, 0x00b0, 4)}, /* Cinterion CLS8 */ {QMI_FIXED_INTF(0x1e2d, 0x00b7, 0)}, /* Cinterion MV31 RmNet */ {QMI_FIXED_INTF(0x1e2d, 0x00b9, 0)}, /* Cinterion MV31 RmNet based on new baseline */ + {QMI_FIXED_INTF(0x1e2d, 0x00f3, 0)}, /* Cinterion MV32-W-A RmNet */ + {QMI_FIXED_INTF(0x1e2d, 0x00f4, 0)}, /* Cinterion MV32-W-B RmNet */ {QMI_FIXED_INTF(0x413c, 0x81a2, 8)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81a3, 8)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81a4, 8)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */ diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 2cb833b3006a..466da01ba2e3 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -312,7 +312,6 @@ static bool veth_skb_is_eligible_for_gro(const struct net_device *dev, static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) { struct veth_priv *rcv_priv, *priv = netdev_priv(dev); - struct netdev_queue *queue = NULL; struct veth_rq *rq = NULL; struct net_device *rcv; int length = skb->len; @@ -330,7 +329,6 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) rxq = skb_get_queue_mapping(skb); if (rxq < rcv->real_num_rx_queues) { rq = &rcv_priv->rq[rxq]; - queue = netdev_get_tx_queue(dev, rxq); /* The napi pointer is available when an XDP program is * attached or when GRO is enabled @@ -342,8 +340,6 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) skb_tx_timestamp(skb); if (likely(veth_forward_skb(rcv, skb, rq, use_napi) == NET_RX_SUCCESS)) { - if (queue) - txq_trans_cond_update(queue); if (!use_napi) dev_lstats_add(dev, length); } else { diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index ec8e1b3108c3..d934774e9733 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -135,6 +135,9 @@ struct send_queue { struct virtnet_sq_stats stats; struct napi_struct napi; + + /* Record whether sq is in reset state. */ + bool reset; }; /* Internal representation of a receive virtqueue */ @@ -267,6 +270,12 @@ struct virtnet_info { u8 duplex; u32 speed; + /* Interrupt coalescing settings */ + u32 tx_usecs; + u32 rx_usecs; + u32 tx_max_packets; + u32 rx_max_packets; + unsigned long guest_offloads; unsigned long guest_offloads_capable; @@ -284,6 +293,9 @@ struct padded_vnet_hdr { char padding[12]; }; +static void virtnet_rq_free_unused_buf(struct virtqueue *vq, void *buf); +static void virtnet_sq_free_unused_buf(struct virtqueue *vq, void *buf); + static bool is_xdp_frame(void *ptr) { return (unsigned long)ptr & VIRTIO_XDP_FLAG; @@ -1057,8 +1069,11 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, case XDP_TX: stats->xdp_tx++; xdpf = xdp_convert_buff_to_frame(&xdp); - if (unlikely(!xdpf)) + if (unlikely(!xdpf)) { + if (unlikely(xdp_page != page)) + put_page(xdp_page); goto err_xdp; + } err = virtnet_xdp_xmit(dev, 1, &xdpf, 0); if (unlikely(!err)) { xdp_return_frame_rx_napi(xdpf); @@ -1625,6 +1640,11 @@ static void virtnet_poll_cleantx(struct receive_queue *rq) return; if (__netif_tx_trylock(txq)) { + if (sq->reset) { + __netif_tx_unlock(txq); + return; + } + do { virtqueue_disable_cb(sq->vq); free_old_xmit_skbs(sq, true); @@ -1872,6 +1892,70 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } +static int virtnet_rx_resize(struct virtnet_info *vi, + struct receive_queue *rq, u32 ring_num) +{ + bool running = netif_running(vi->dev); + int err, qindex; + + qindex = rq - vi->rq; + + if (running) + napi_disable(&rq->napi); + + err = virtqueue_resize(rq->vq, ring_num, virtnet_rq_free_unused_buf); + if (err) + netdev_err(vi->dev, "resize rx fail: rx queue index: %d err: %d\n", qindex, err); + + if (!try_fill_recv(vi, rq, GFP_KERNEL)) + schedule_delayed_work(&vi->refill, 0); + + if (running) + virtnet_napi_enable(rq->vq, &rq->napi); + return err; +} + +static int virtnet_tx_resize(struct virtnet_info *vi, + struct send_queue *sq, u32 ring_num) +{ + bool running = netif_running(vi->dev); + struct netdev_queue *txq; + int err, qindex; + + qindex = sq - vi->sq; + + if (running) + virtnet_napi_tx_disable(&sq->napi); + + txq = netdev_get_tx_queue(vi->dev, qindex); + + /* 1. wait all ximt complete + * 2. fix the race of netif_stop_subqueue() vs netif_start_subqueue() + */ + __netif_tx_lock_bh(txq); + + /* Prevent rx poll from accessing sq. */ + sq->reset = true; + + /* Prevent the upper layer from trying to send packets. */ + netif_stop_subqueue(vi->dev, qindex); + + __netif_tx_unlock_bh(txq); + + err = virtqueue_resize(sq->vq, ring_num, virtnet_sq_free_unused_buf); + if (err) + netdev_err(vi->dev, "resize tx fail: tx queue index: %d err: %d\n", qindex, err); + + __netif_tx_lock_bh(txq); + sq->reset = false; + netif_tx_wake_queue(txq); + __netif_tx_unlock_bh(txq); + + if (running) + virtnet_napi_tx_enable(vi, sq->vq, &sq->napi); + return err; +} + /* * Send command via the control virtqueue and check status. Commands * supported by the hypervisor, as indicated by feature bits, should @@ -2282,10 +2366,57 @@ static void virtnet_get_ringparam(struct net_device *dev, { struct virtnet_info *vi = netdev_priv(dev); - ring->rx_max_pending = virtqueue_get_vring_size(vi->rq[0].vq); - ring->tx_max_pending = virtqueue_get_vring_size(vi->sq[0].vq); - ring->rx_pending = ring->rx_max_pending; - ring->tx_pending = ring->tx_max_pending; + ring->rx_max_pending = vi->rq[0].vq->num_max; + ring->tx_max_pending = vi->sq[0].vq->num_max; + ring->rx_pending = virtqueue_get_vring_size(vi->rq[0].vq); + ring->tx_pending = virtqueue_get_vring_size(vi->sq[0].vq); +} + +static int virtnet_set_ringparam(struct net_device *dev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *extack) +{ + struct virtnet_info *vi = netdev_priv(dev); + u32 rx_pending, tx_pending; + struct receive_queue *rq; + struct send_queue *sq; + int i, err; + + if (ring->rx_mini_pending || ring->rx_jumbo_pending) + return -EINVAL; + + rx_pending = virtqueue_get_vring_size(vi->rq[0].vq); + tx_pending = virtqueue_get_vring_size(vi->sq[0].vq); + + if (ring->rx_pending == rx_pending && + ring->tx_pending == tx_pending) + return 0; + + if (ring->rx_pending > vi->rq[0].vq->num_max) + return -EINVAL; + + if (ring->tx_pending > vi->sq[0].vq->num_max) + return -EINVAL; + + for (i = 0; i < vi->max_queue_pairs; i++) { + rq = vi->rq + i; + sq = vi->sq + i; + + if (ring->tx_pending != tx_pending) { + err = virtnet_tx_resize(vi, sq, ring->tx_pending); + if (err) + return err; + } + + if (ring->rx_pending != rx_pending) { + err = virtnet_rx_resize(vi, rq, ring->rx_pending); + if (err) + return err; + } + } + + return 0; } static bool virtnet_commit_rss_command(struct virtnet_info *vi) @@ -2615,27 +2746,89 @@ static int virtnet_get_link_ksettings(struct net_device *dev, return 0; } +static int virtnet_send_notf_coal_cmds(struct virtnet_info *vi, + struct ethtool_coalesce *ec) +{ + struct scatterlist sgs_tx, sgs_rx; + struct virtio_net_ctrl_coal_tx coal_tx; + struct virtio_net_ctrl_coal_rx coal_rx; + + coal_tx.tx_usecs = cpu_to_le32(ec->tx_coalesce_usecs); + coal_tx.tx_max_packets = cpu_to_le32(ec->tx_max_coalesced_frames); + sg_init_one(&sgs_tx, &coal_tx, sizeof(coal_tx)); + + if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_NOTF_COAL, + VIRTIO_NET_CTRL_NOTF_COAL_TX_SET, + &sgs_tx)) + return -EINVAL; + + /* Save parameters */ + vi->tx_usecs = ec->tx_coalesce_usecs; + vi->tx_max_packets = ec->tx_max_coalesced_frames; + + coal_rx.rx_usecs = cpu_to_le32(ec->rx_coalesce_usecs); + coal_rx.rx_max_packets = cpu_to_le32(ec->rx_max_coalesced_frames); + sg_init_one(&sgs_rx, &coal_rx, sizeof(coal_rx)); + + if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_NOTF_COAL, + VIRTIO_NET_CTRL_NOTF_COAL_RX_SET, + &sgs_rx)) + return -EINVAL; + + /* Save parameters */ + vi->rx_usecs = ec->rx_coalesce_usecs; + vi->rx_max_packets = ec->rx_max_coalesced_frames; + + return 0; +} + +static int virtnet_coal_params_supported(struct ethtool_coalesce *ec) +{ + /* usecs coalescing is supported only if VIRTIO_NET_F_NOTF_COAL + * feature is negotiated. + */ + if (ec->rx_coalesce_usecs || ec->tx_coalesce_usecs) + return -EOPNOTSUPP; + + if (ec->tx_max_coalesced_frames > 1 || + ec->rx_max_coalesced_frames != 1) + return -EINVAL; + + return 0; +} + static int virtnet_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec, struct kernel_ethtool_coalesce *kernel_coal, struct netlink_ext_ack *extack) { struct virtnet_info *vi = netdev_priv(dev); - int i, napi_weight; - - if (ec->tx_max_coalesced_frames > 1 || - ec->rx_max_coalesced_frames != 1) - return -EINVAL; + int ret, i, napi_weight; + bool update_napi = false; + /* Can't change NAPI weight if the link is up */ napi_weight = ec->tx_max_coalesced_frames ? NAPI_POLL_WEIGHT : 0; if (napi_weight ^ vi->sq[0].napi.weight) { if (dev->flags & IFF_UP) return -EBUSY; + else + update_napi = true; + } + + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_NOTF_COAL)) + ret = virtnet_send_notf_coal_cmds(vi, ec); + else + ret = virtnet_coal_params_supported(ec); + + if (ret) + return ret; + + if (update_napi) { for (i = 0; i < vi->max_queue_pairs; i++) vi->sq[i].napi.weight = napi_weight; } - return 0; + return ret; } static int virtnet_get_coalesce(struct net_device *dev, @@ -2643,16 +2836,19 @@ static int virtnet_get_coalesce(struct net_device *dev, struct kernel_ethtool_coalesce *kernel_coal, struct netlink_ext_ack *extack) { - struct ethtool_coalesce ec_default = { - .cmd = ETHTOOL_GCOALESCE, - .rx_max_coalesced_frames = 1, - }; struct virtnet_info *vi = netdev_priv(dev); - memcpy(ec, &ec_default, sizeof(ec_default)); + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_NOTF_COAL)) { + ec->rx_coalesce_usecs = vi->rx_usecs; + ec->tx_coalesce_usecs = vi->tx_usecs; + ec->tx_max_coalesced_frames = vi->tx_max_packets; + ec->rx_max_coalesced_frames = vi->rx_max_packets; + } else { + ec->rx_max_coalesced_frames = 1; - if (vi->sq[0].napi.weight) - ec->tx_max_coalesced_frames = 1; + if (vi->sq[0].napi.weight) + ec->tx_max_coalesced_frames = 1; + } return 0; } @@ -2771,10 +2967,12 @@ static int virtnet_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info) } static const struct ethtool_ops virtnet_ethtool_ops = { - .supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES, + .supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES | + ETHTOOL_COALESCE_USECS, .get_drvinfo = virtnet_get_drvinfo, .get_link = ethtool_op_get_link, .get_ringparam = virtnet_get_ringparam, + .set_ringparam = virtnet_set_ringparam, .get_strings = virtnet_get_strings, .get_sset_count = virtnet_get_sset_count, .get_ethtool_stats = virtnet_get_ethtool_stats, @@ -3168,6 +3366,27 @@ static void free_receive_page_frags(struct virtnet_info *vi) put_page(vi->rq[i].alloc_frag.page); } +static void virtnet_sq_free_unused_buf(struct virtqueue *vq, void *buf) +{ + if (!is_xdp_frame(buf)) + dev_kfree_skb(buf); + else + xdp_return_frame(ptr_to_xdp(buf)); +} + +static void virtnet_rq_free_unused_buf(struct virtqueue *vq, void *buf) +{ + struct virtnet_info *vi = vq->vdev->priv; + int i = vq2rxq(vq); + + if (vi->mergeable_rx_bufs) + put_page(virt_to_head_page(buf)); + else if (vi->big_packets) + give_pages(&vi->rq[i], buf); + else + put_page(virt_to_head_page(buf)); +} + static void free_unused_bufs(struct virtnet_info *vi) { void *buf; @@ -3175,26 +3394,14 @@ static void free_unused_bufs(struct virtnet_info *vi) for (i = 0; i < vi->max_queue_pairs; i++) { struct virtqueue *vq = vi->sq[i].vq; - while ((buf = virtqueue_detach_unused_buf(vq)) != NULL) { - if (!is_xdp_frame(buf)) - dev_kfree_skb(buf); - else - xdp_return_frame(ptr_to_xdp(buf)); - } + while ((buf = virtqueue_detach_unused_buf(vq)) != NULL) + virtnet_sq_free_unused_buf(vq, buf); } for (i = 0; i < vi->max_queue_pairs; i++) { struct virtqueue *vq = vi->rq[i].vq; - - while ((buf = virtqueue_detach_unused_buf(vq)) != NULL) { - if (vi->mergeable_rx_bufs) { - put_page(virt_to_head_page(buf)); - } else if (vi->big_packets) { - give_pages(&vi->rq[i], buf); - } else { - put_page(virt_to_head_page(buf)); - } - } + while ((buf = virtqueue_detach_unused_buf(vq)) != NULL) + virtnet_rq_free_unused_buf(vq, buf); } } @@ -3225,6 +3432,29 @@ static unsigned int mergeable_min_buf_len(struct virtnet_info *vi, struct virtqu (unsigned int)GOOD_PACKET_LEN); } +static void virtnet_config_sizes(struct virtnet_info *vi, u32 *sizes) +{ + u32 i, rx_size, tx_size; + + if (vi->speed == SPEED_UNKNOWN || vi->speed < SPEED_10000) { + rx_size = 1024; + tx_size = 1024; + + } else if (vi->speed < SPEED_40000) { + rx_size = 1024 * 4; + tx_size = 1024 * 4; + + } else { + rx_size = 1024 * 8; + tx_size = 1024 * 8; + } + + for (i = 0; i < vi->max_queue_pairs; i++) { + sizes[rxq2vq(i)] = rx_size; + sizes[txq2vq(i)] = tx_size; + } +} + static int virtnet_find_vqs(struct virtnet_info *vi) { vq_callback_t **callbacks; @@ -3232,6 +3462,7 @@ static int virtnet_find_vqs(struct virtnet_info *vi) int ret = -ENOMEM; int i, total_vqs; const char **names; + u32 *sizes; bool *ctx; /* We expect 1 RX virtqueue followed by 1 TX virtqueue, followed by @@ -3259,10 +3490,15 @@ static int virtnet_find_vqs(struct virtnet_info *vi) ctx = NULL; } + sizes = kmalloc_array(total_vqs, sizeof(*sizes), GFP_KERNEL); + if (!sizes) + goto err_sizes; + /* Parameters for control virtqueue, if any */ if (vi->has_cvq) { callbacks[total_vqs - 1] = NULL; names[total_vqs - 1] = "control"; + sizes[total_vqs - 1] = 64; } /* Allocate/initialize parameters for send/receive virtqueues */ @@ -3277,8 +3513,10 @@ static int virtnet_find_vqs(struct virtnet_info *vi) ctx[rxq2vq(i)] = true; } - ret = virtio_find_vqs_ctx(vi->vdev, total_vqs, vqs, callbacks, - names, ctx, NULL); + virtnet_config_sizes(vi, sizes); + + ret = virtio_find_vqs_ctx_size(vi->vdev, total_vqs, vqs, callbacks, + names, sizes, ctx, NULL); if (ret) goto err_find; @@ -3298,6 +3536,8 @@ static int virtnet_find_vqs(struct virtnet_info *vi) err_find: + kfree(sizes); +err_sizes: kfree(ctx); err_ctx: kfree(names); @@ -3441,6 +3681,8 @@ static bool virtnet_validate_features(struct virtio_device *vdev) VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_RSS, "VIRTIO_NET_F_CTRL_VQ") || VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_HASH_REPORT, + "VIRTIO_NET_F_CTRL_VQ") || + VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_NOTF_COAL, "VIRTIO_NET_F_CTRL_VQ"))) { return false; } @@ -3577,6 +3819,13 @@ static int virtnet_probe(struct virtio_device *vdev) if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF)) vi->mergeable_rx_bufs = true; + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_NOTF_COAL)) { + vi->rx_usecs = 0; + vi->tx_usecs = 0; + vi->tx_max_packets = 0; + vi->rx_max_packets = 0; + } + if (virtio_has_feature(vdev, VIRTIO_NET_F_HASH_REPORT)) vi->has_rss_hash_report = true; @@ -3648,6 +3897,9 @@ static int virtnet_probe(struct virtio_device *vdev) vi->curr_queue_pairs = num_online_cpus(); vi->max_queue_pairs = max_queue_pairs; + virtnet_init_settings(dev); + virtnet_update_settings(vi); + /* Allocate/initialize the rx/tx queues, and invoke find_vqs */ err = init_vqs(vi); if (err) @@ -3660,8 +3912,6 @@ static int virtnet_probe(struct virtio_device *vdev) netif_set_real_num_tx_queues(dev, vi->curr_queue_pairs); netif_set_real_num_rx_queues(dev, vi->curr_queue_pairs); - virtnet_init_settings(dev); - if (virtio_has_feature(vdev, VIRTIO_NET_F_STANDBY)) { vi->failover = net_failover_create(vi->dev); if (IS_ERR(vi->failover)) { @@ -3811,7 +4061,7 @@ static struct virtio_device_id id_table[] = { VIRTIO_NET_F_CTRL_MAC_ADDR, \ VIRTIO_NET_F_MTU, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, \ VIRTIO_NET_F_SPEED_DUPLEX, VIRTIO_NET_F_STANDBY, \ - VIRTIO_NET_F_RSS, VIRTIO_NET_F_HASH_REPORT + VIRTIO_NET_F_RSS, VIRTIO_NET_F_HASH_REPORT, VIRTIO_NET_F_NOTF_COAL static unsigned int features[] = { VIRTNET_FEATURES, diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 90811ab851fd..c3285242f74f 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2321,7 +2321,7 @@ static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan, fl6.flowi6_oif = oif; fl6.daddr = *daddr; fl6.saddr = *saddr; - fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tos), label); + fl6.flowlabel = ip6_make_flowinfo(tos, label); fl6.flowi6_mark = skb->mark; fl6.flowi6_proto = IPPROTO_UDP; fl6.fl6_dport = dport; diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index b89519ab9205..eb1d1ba3a443 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -635,7 +635,7 @@ static inline void host_int_parse_assoc_resp_info(struct wilc_vif *vif, conn_info->req_ies_len = 0; } -inline void wilc_handle_disconnect(struct wilc_vif *vif) +void wilc_handle_disconnect(struct wilc_vif *vif) { struct host_if_drv *hif_drv = vif->hif_drv; diff --git a/drivers/net/wireless/microchip/wilc1000/hif.h b/drivers/net/wireless/microchip/wilc1000/hif.h index 69ba1d469e9f..baa2881f4465 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.h +++ b/drivers/net/wireless/microchip/wilc1000/hif.h @@ -215,5 +215,6 @@ void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length); void *wilc_parse_join_bss_param(struct cfg80211_bss *bss, struct cfg80211_crypto_settings *crypto); int wilc_set_default_mgmt_key_index(struct wilc_vif *vif, u8 index); -inline void wilc_handle_disconnect(struct wilc_vif *vif); +void wilc_handle_disconnect(struct wilc_vif *vif); + #endif diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index b019755e4e21..3ece49cb18ff 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -45,7 +45,6 @@ #define NTB_EPF_MIN_DB_COUNT 3 #define NTB_EPF_MAX_DB_COUNT 31 -#define NTB_EPF_MW_OFFSET 2 #define NTB_EPF_COMMAND_TIMEOUT 1000 /* 1 Sec */ @@ -67,6 +66,7 @@ struct ntb_epf_dev { enum pci_barno ctrl_reg_bar; enum pci_barno peer_spad_reg_bar; enum pci_barno db_reg_bar; + enum pci_barno mw_bar; unsigned int mw_count; unsigned int spad_count; @@ -92,6 +92,8 @@ struct ntb_epf_data { enum pci_barno peer_spad_reg_bar; /* BAR that contains Doorbell region and Memory window '1' */ enum pci_barno db_reg_bar; + /* BAR that contains memory windows*/ + enum pci_barno mw_bar; }; static int ntb_epf_send_command(struct ntb_epf_dev *ndev, u32 command, @@ -411,7 +413,7 @@ static int ntb_epf_mw_set_trans(struct ntb_dev *ntb, int pidx, int idx, return -EINVAL; } - bar = idx + NTB_EPF_MW_OFFSET; + bar = idx + ndev->mw_bar; mw_size = pci_resource_len(ntb->pdev, bar); @@ -453,7 +455,7 @@ static int ntb_epf_peer_mw_get_addr(struct ntb_dev *ntb, int idx, if (idx == 0) offset = readl(ndev->ctrl_reg + NTB_EPF_MW1_OFFSET); - bar = idx + NTB_EPF_MW_OFFSET; + bar = idx + ndev->mw_bar; if (base) *base = pci_resource_start(ndev->ntb.pdev, bar) + offset; @@ -565,6 +567,7 @@ static int ntb_epf_init_pci(struct ntb_epf_dev *ndev, struct pci_dev *pdev) { struct device *dev = ndev->dev; + size_t spad_sz, spad_off; int ret; pci_set_drvdata(pdev, ndev); @@ -599,10 +602,16 @@ static int ntb_epf_init_pci(struct ntb_epf_dev *ndev, goto err_dma_mask; } - ndev->peer_spad_reg = pci_iomap(pdev, ndev->peer_spad_reg_bar, 0); - if (!ndev->peer_spad_reg) { - ret = -EIO; - goto err_dma_mask; + if (ndev->peer_spad_reg_bar) { + ndev->peer_spad_reg = pci_iomap(pdev, ndev->peer_spad_reg_bar, 0); + if (!ndev->peer_spad_reg) { + ret = -EIO; + goto err_dma_mask; + } + } else { + spad_sz = 4 * readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT); + spad_off = readl(ndev->ctrl_reg + NTB_EPF_SPAD_OFFSET); + ndev->peer_spad_reg = ndev->ctrl_reg + spad_off + spad_sz; } ndev->db_reg = pci_iomap(pdev, ndev->db_reg_bar, 0); @@ -657,6 +666,7 @@ static int ntb_epf_pci_probe(struct pci_dev *pdev, enum pci_barno peer_spad_reg_bar = BAR_1; enum pci_barno ctrl_reg_bar = BAR_0; enum pci_barno db_reg_bar = BAR_2; + enum pci_barno mw_bar = BAR_2; struct device *dev = &pdev->dev; struct ntb_epf_data *data; struct ntb_epf_dev *ndev; @@ -671,17 +681,16 @@ static int ntb_epf_pci_probe(struct pci_dev *pdev, data = (struct ntb_epf_data *)id->driver_data; if (data) { - if (data->peer_spad_reg_bar) - peer_spad_reg_bar = data->peer_spad_reg_bar; - if (data->ctrl_reg_bar) - ctrl_reg_bar = data->ctrl_reg_bar; - if (data->db_reg_bar) - db_reg_bar = data->db_reg_bar; + peer_spad_reg_bar = data->peer_spad_reg_bar; + ctrl_reg_bar = data->ctrl_reg_bar; + db_reg_bar = data->db_reg_bar; + mw_bar = data->mw_bar; } ndev->peer_spad_reg_bar = peer_spad_reg_bar; ndev->ctrl_reg_bar = ctrl_reg_bar; ndev->db_reg_bar = db_reg_bar; + ndev->mw_bar = mw_bar; ndev->dev = dev; ntb_epf_init_struct(ndev, pdev); @@ -729,6 +738,14 @@ static const struct ntb_epf_data j721e_data = { .ctrl_reg_bar = BAR_0, .peer_spad_reg_bar = BAR_1, .db_reg_bar = BAR_2, + .mw_bar = BAR_2, +}; + +static const struct ntb_epf_data mx8_data = { + .ctrl_reg_bar = BAR_0, + .peer_spad_reg_bar = BAR_0, + .db_reg_bar = BAR_2, + .mw_bar = BAR_4, }; static const struct pci_device_id ntb_epf_pci_tbl[] = { @@ -737,6 +754,11 @@ static const struct pci_device_id ntb_epf_pci_tbl[] = { .class = PCI_CLASS_MEMORY_RAM << 8, .class_mask = 0xffff00, .driver_data = (kernel_ulong_t)&j721e_data, }, + { + PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x0809), + .class = PCI_CLASS_MEMORY_RAM << 8, .class_mask = 0xffff00, + .driver_data = (kernel_ulong_t)&mx8_data, + }, { }, }; diff --git a/drivers/ntb/hw/idt/ntb_hw_idt.c b/drivers/ntb/hw/idt/ntb_hw_idt.c index 733557231ed0..0ed6f809ff2e 100644 --- a/drivers/ntb/hw/idt/ntb_hw_idt.c +++ b/drivers/ntb/hw/idt/ntb_hw_idt.c @@ -2406,7 +2406,7 @@ static ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf, "\t%hhu.\t", idx); else off += scnprintf(strbuf + off, size - off, - "\t%hhu-%hhu.\t", idx, idx + cnt - 1); + "\t%hhu-%d.\t", idx, idx + cnt - 1); off += scnprintf(strbuf + off, size - off, "%s BAR%hhu, ", idt_get_mw_name(data), ndev->mws[idx].bar); @@ -2435,7 +2435,7 @@ static ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf, "\t%hhu.\t", idx); else off += scnprintf(strbuf + off, size - off, - "\t%hhu-%hhu.\t", idx, idx + cnt - 1); + "\t%hhu-%d.\t", idx, idx + cnt - 1); off += scnprintf(strbuf + off, size - off, "%s BAR%hhu, ", idt_get_mw_name(data), @@ -2480,7 +2480,7 @@ static ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf, int src; data = idt_ntb_msg_read(&ndev->ntb, &src, idx); off += scnprintf(strbuf + off, size - off, - "\t%hhu. 0x%08x from peer %hhu (Port %hhu)\n", + "\t%hhu. 0x%08x from peer %d (Port %hhu)\n", idx, data, src, ndev->peers[src].port); } off += scnprintf(strbuf + off, size - off, "\n"); diff --git a/drivers/ntb/hw/intel/ntb_hw_gen1.c b/drivers/ntb/hw/intel/ntb_hw_gen1.c index e5f14e20a9ff..84772013812b 100644 --- a/drivers/ntb/hw/intel/ntb_hw_gen1.c +++ b/drivers/ntb/hw/intel/ntb_hw_gen1.c @@ -763,7 +763,7 @@ static ssize_t ndev_debugfs_read(struct file *filp, char __user *ubuf, return ndev_ntb_debugfs_read(filp, ubuf, count, offp); else if (pdev_is_gen3(ndev->ntb.pdev)) return ndev_ntb3_debugfs_read(filp, ubuf, count, offp); - else if (pdev_is_gen4(ndev->ntb.pdev)) + else if (pdev_is_gen4(ndev->ntb.pdev) || pdev_is_gen5(ndev->ntb.pdev)) return ndev_ntb4_debugfs_read(filp, ubuf, count, offp); return -ENXIO; @@ -1874,7 +1874,7 @@ static int intel_ntb_pci_probe(struct pci_dev *pdev, rc = gen3_init_dev(ndev); if (rc) goto err_init_dev; - } else if (pdev_is_gen4(pdev)) { + } else if (pdev_is_gen4(pdev) || pdev_is_gen5(pdev)) { ndev->ntb.ops = &intel_ntb4_ops; rc = intel_ntb_init_pci(ndev, pdev); if (rc) @@ -1904,7 +1904,8 @@ static int intel_ntb_pci_probe(struct pci_dev *pdev, err_register: ndev_deinit_debugfs(ndev); - if (pdev_is_gen1(pdev) || pdev_is_gen3(pdev) || pdev_is_gen4(pdev)) + if (pdev_is_gen1(pdev) || pdev_is_gen3(pdev) || + pdev_is_gen4(pdev) || pdev_is_gen5(pdev)) xeon_deinit_dev(ndev); err_init_dev: intel_ntb_deinit_pci(ndev); @@ -1920,7 +1921,8 @@ static void intel_ntb_pci_remove(struct pci_dev *pdev) ntb_unregister_device(&ndev->ntb); ndev_deinit_debugfs(ndev); - if (pdev_is_gen1(pdev) || pdev_is_gen3(pdev) || pdev_is_gen4(pdev)) + if (pdev_is_gen1(pdev) || pdev_is_gen3(pdev) || + pdev_is_gen4(pdev) || pdev_is_gen5(pdev)) xeon_deinit_dev(ndev); intel_ntb_deinit_pci(ndev); kfree(ndev); @@ -2047,6 +2049,8 @@ static const struct pci_device_id intel_ntb_pci_tbl[] = { /* GEN4 */ {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_ICX)}, + /* GEN5 PCIe */ + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_GNR)}, {0} }; MODULE_DEVICE_TABLE(pci, intel_ntb_pci_tbl); diff --git a/drivers/ntb/hw/intel/ntb_hw_gen4.c b/drivers/ntb/hw/intel/ntb_hw_gen4.c index 4081fc538ff4..22cac7975b3c 100644 --- a/drivers/ntb/hw/intel/ntb_hw_gen4.c +++ b/drivers/ntb/hw/intel/ntb_hw_gen4.c @@ -197,7 +197,7 @@ int gen4_init_dev(struct intel_ntb_dev *ndev) ppd1 = ioread32(ndev->self_mmio + GEN4_PPD1_OFFSET); if (pdev_is_ICX(pdev)) ndev->ntb.topo = gen4_ppd_topo(ndev, ppd1); - else if (pdev_is_SPR(pdev)) + else if (pdev_is_SPR(pdev) || pdev_is_gen5(pdev)) ndev->ntb.topo = spr_ppd_topo(ndev, ppd1); dev_dbg(&pdev->dev, "ppd %#x topo %s\n", ppd1, ntb_topo_string(ndev->ntb.topo)); diff --git a/drivers/ntb/hw/intel/ntb_hw_intel.h b/drivers/ntb/hw/intel/ntb_hw_intel.h index b233d1c6ba2d..da4d5fe55bab 100644 --- a/drivers/ntb/hw/intel/ntb_hw_intel.h +++ b/drivers/ntb/hw/intel/ntb_hw_intel.h @@ -70,6 +70,7 @@ #define PCI_DEVICE_ID_INTEL_NTB_SS_BDX 0x6F0F #define PCI_DEVICE_ID_INTEL_NTB_B2B_SKX 0x201C #define PCI_DEVICE_ID_INTEL_NTB_B2B_ICX 0x347e +#define PCI_DEVICE_ID_INTEL_NTB_B2B_GNR 0x0db4 /* Ntb control and link status */ #define NTB_CTL_CFG_LOCK BIT(0) @@ -228,4 +229,10 @@ static inline int pdev_is_gen4(struct pci_dev *pdev) return 0; } + +static inline int pdev_is_gen5(struct pci_dev *pdev) +{ + return pdev->device == PCI_DEVICE_ID_INTEL_NTB_B2B_GNR; +} + #endif diff --git a/drivers/ntb/test/ntb_tool.c b/drivers/ntb/test/ntb_tool.c index b7bf3f863d79..5ee0afa621a9 100644 --- a/drivers/ntb/test/ntb_tool.c +++ b/drivers/ntb/test/ntb_tool.c @@ -367,14 +367,16 @@ static ssize_t tool_fn_write(struct tool_ctx *tc, u64 bits; int n; + if (*offp) + return 0; + buf = kmalloc(size + 1, GFP_KERNEL); if (!buf) return -ENOMEM; - ret = simple_write_to_buffer(buf, size, offp, ubuf, size); - if (ret < 0) { + if (copy_from_user(buf, ubuf, size)) { kfree(buf); - return ret; + return -EFAULT; } buf[size] = 0; diff --git a/drivers/nvdimm/virtio_pmem.c b/drivers/nvdimm/virtio_pmem.c index 995b6cdc67ed..20da455d2ef6 100644 --- a/drivers/nvdimm/virtio_pmem.c +++ b/drivers/nvdimm/virtio_pmem.c @@ -81,17 +81,24 @@ static int virtio_pmem_probe(struct virtio_device *vdev) ndr_desc.res = &res; ndr_desc.numa_node = nid; ndr_desc.flush = async_pmem_flush; + ndr_desc.provider_data = vdev; set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags); set_bit(ND_REGION_ASYNC, &ndr_desc.flags); + /* + * The NVDIMM region could be available before the + * virtio_device_ready() that is called by + * virtio_dev_probe(), so we set device ready here. + */ + virtio_device_ready(vdev); nd_region = nvdimm_pmem_region_create(vpmem->nvdimm_bus, &ndr_desc); if (!nd_region) { dev_err(&vdev->dev, "failed to create nvdimm region\n"); err = -ENXIO; goto out_nd; } - nd_region->provider_data = dev_to_virtio(nd_region->dev.parent->parent); return 0; out_nd: + virtio_reset_device(vdev); nvdimm_bus_unregister(vpmem->nvdimm_bus); out_vq: vdev->config->del_vqs(vdev); diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c index 5207a2348257..10cc4a814602 100644 --- a/drivers/nvme/host/fabrics.c +++ b/drivers/nvme/host/fabrics.c @@ -270,6 +270,12 @@ static void nvmf_log_connect_error(struct nvme_ctrl *ctrl, { int err_sctype = errval & ~NVME_SC_DNR; + if (errval < 0) { + dev_err(ctrl->device, + "Connect command failed, errno: %d\n", errval); + return; + } + switch (err_sctype) { case NVME_SC_CONNECT_INVALID_PARAM: if (offset >> 16) { @@ -1230,7 +1236,7 @@ static int __init nvmf_init(void) nvmf_device = device_create(nvmf_class, NULL, MKDEV(0, 0), NULL, "ctl"); if (IS_ERR(nvmf_device)) { - pr_err("couldn't create nvme-fabris device!\n"); + pr_err("couldn't create nvme-fabrics device!\n"); ret = PTR_ERR(nvmf_device); goto out_destroy_class; } diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 9987797620b6..127abaf9ba5d 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -2533,6 +2533,8 @@ __nvme_fc_abort_outstanding_ios(struct nvme_fc_ctrl *ctrl, bool start_queues) blk_mq_tagset_busy_iter(&ctrl->admin_tag_set, nvme_fc_terminate_exchange, &ctrl->ctrl); blk_mq_tagset_wait_completed_request(&ctrl->admin_tag_set); + if (start_queues) + nvme_start_admin_queue(&ctrl->ctrl); } static void @@ -3878,6 +3880,7 @@ static int fc_parse_cgrpid(const char *buf, u64 *id) static ssize_t fc_appid_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + size_t orig_count = count; u64 cgrp_id; int appid_len = 0; int cgrpid_len = 0; @@ -3902,7 +3905,7 @@ static ssize_t fc_appid_store(struct device *dev, ret = blkcg_set_fc_appid(app_id, cgrp_id, sizeof(app_id)); if (ret < 0) return ret; - return count; + return orig_count; } static DEVICE_ATTR(appid_store, 0200, NULL, fc_appid_store); #endif /* CONFIG_BLK_CGROUP_FC_APPID */ diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index de1b4463142d..3a1c37f32f30 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -3511,6 +3511,8 @@ static const struct pci_device_id nvme_id_table[] = { .driver_data = NVME_QUIRK_BOGUS_NID, }, { PCI_DEVICE(0x1cc1, 0x5350), /* ADATA XPG GAMMIX S50 */ .driver_data = NVME_QUIRK_BOGUS_NID, }, + { PCI_DEVICE(0x1dbe, 0x5236), /* ADATA XPG GAMMIX S70 */ + .driver_data = NVME_QUIRK_BOGUS_NID, }, { PCI_DEVICE(0x1e49, 0x0041), /* ZHITAI TiPro7000 NVMe SSD */ .driver_data = NVME_QUIRK_NO_DEEPEST_PS, }, { PCI_DEVICE(0xc0a9, 0x540a), /* Crucial P2 */ diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index e82dcfcda29b..044da18c06f5 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -1660,6 +1660,9 @@ static void nvme_tcp_stop_queue(struct nvme_ctrl *nctrl, int qid) struct nvme_tcp_ctrl *ctrl = to_tcp_ctrl(nctrl); struct nvme_tcp_queue *queue = &ctrl->queues[qid]; + if (!test_bit(NVME_TCP_Q_ALLOCATED, &queue->flags)) + return; + mutex_lock(&queue->queue_lock); if (test_and_clear_bit(NVME_TCP_Q_LIVE, &queue->flags)) __nvme_tcp_stop_queue(queue); diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c index c851814d6cb0..ebdf9aa81041 100644 --- a/drivers/nvme/target/fabrics-cmd-auth.c +++ b/drivers/nvme/target/fabrics-cmd-auth.c @@ -160,10 +160,10 @@ static u16 nvmet_auth_reply(struct nvmet_req *req, void *d) pr_debug("%s: ctrl %d qid %d host authenticated\n", __func__, ctrl->cntlid, req->sq->qid); if (data->cvalid) { - req->sq->dhchap_c2 = kmalloc(data->hl, GFP_KERNEL); + req->sq->dhchap_c2 = kmemdup(data->rval + data->hl, data->hl, + GFP_KERNEL); if (!req->sq->dhchap_c2) return NVME_AUTH_DHCHAP_FAILURE_FAILED; - memcpy(req->sq->dhchap_c2, data->rval + data->hl, data->hl); pr_debug("%s: ctrl %d qid %d challenge %*ph\n", __func__, ctrl->cntlid, req->sq->qid, data->hl, diff --git a/drivers/of/address.c b/drivers/of/address.c index 94f017d808c4..96f0a12e507c 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -1045,26 +1045,29 @@ phys_addr_t __init of_dma_get_max_cpu_address(struct device_node *np) * * It returns true if "dma-coherent" property was found * for this device in the DT, or if DMA is coherent by - * default for OF devices on the current platform. + * default for OF devices on the current platform and no + * "dma-noncoherent" property was found for this device. */ bool of_dma_is_coherent(struct device_node *np) { struct device_node *node; - - if (IS_ENABLED(CONFIG_OF_DMA_DEFAULT_COHERENT)) - return true; + bool is_coherent = IS_ENABLED(CONFIG_OF_DMA_DEFAULT_COHERENT); node = of_node_get(np); while (node) { if (of_property_read_bool(node, "dma-coherent")) { - of_node_put(node); - return true; + is_coherent = true; + break; + } + if (of_property_read_bool(node, "dma-noncoherent")) { + is_coherent = false; + break; } node = of_get_next_dma_parent(node); } of_node_put(node); - return false; + return is_coherent; } EXPORT_SYMBOL_GPL(of_dma_is_coherent); diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index cf1627679716..83ddb190292e 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -161,7 +161,11 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type, u32 free_win; struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - free_win = find_first_zero_bit(ep->ib_window_map, pci->num_ib_windows); + if (!ep->bar_to_atu[bar]) + free_win = find_first_zero_bit(ep->ib_window_map, pci->num_ib_windows); + else + free_win = ep->bar_to_atu[bar]; + if (free_win >= pci->num_ib_windows) { dev_err(pci->dev, "No free inbound window\n"); return -EINVAL; @@ -218,6 +222,7 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index); clear_bit(atu_index, ep->ib_window_map); ep->epf_bar[bar] = NULL; + ep->bar_to_atu[bar] = 0; } static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, @@ -245,6 +250,9 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, if (ret) return ret; + if (ep->epf_bar[bar]) + return 0; + dw_pcie_dbi_ro_wr_en(pci); dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1)); diff --git a/drivers/pci/endpoint/functions/Kconfig b/drivers/pci/endpoint/functions/Kconfig index 5f1242ca2f4e..295a033ee9a2 100644 --- a/drivers/pci/endpoint/functions/Kconfig +++ b/drivers/pci/endpoint/functions/Kconfig @@ -25,3 +25,15 @@ config PCI_EPF_NTB device tree. If in doubt, say "N" to disable Endpoint NTB driver. + +config PCI_EPF_VNTB + tristate "PCI Endpoint NTB driver" + depends on PCI_ENDPOINT + depends on NTB + select CONFIGFS_FS + help + Select this configuration option to enable the Non-Transparent + Bridge (NTB) driver for PCIe Endpoint. NTB driver implements NTB + between PCI Root Port and PCIe Endpoint. + + If in doubt, say "N" to disable Endpoint NTB driver. diff --git a/drivers/pci/endpoint/functions/Makefile b/drivers/pci/endpoint/functions/Makefile index 96ab932a537a..5c13001deaba 100644 --- a/drivers/pci/endpoint/functions/Makefile +++ b/drivers/pci/endpoint/functions/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_PCI_EPF_TEST) += pci-epf-test.o obj-$(CONFIG_PCI_EPF_NTB) += pci-epf-ntb.o +obj-$(CONFIG_PCI_EPF_VNTB) += pci-epf-vntb.o diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c new file mode 100644 index 000000000000..0ea85e1d292e --- /dev/null +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -0,0 +1,1442 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Endpoint Function Driver to implement Non-Transparent Bridge functionality + * Between PCI RC and EP + * + * Copyright (C) 2020 Texas Instruments + * Copyright (C) 2022 NXP + * + * Based on pci-epf-ntb.c + * Author: Frank Li <Frank.Li@nxp.com> + * Author: Kishon Vijay Abraham I <kishon@ti.com> + */ + +/** + * +------------+ +---------------------------------------+ + * | | | | + * +------------+ | +--------------+ + * | NTB | | | NTB | + * | NetDev | | | NetDev | + * +------------+ | +--------------+ + * | NTB | | | NTB | + * | Transfer | | | Transfer | + * +------------+ | +--------------+ + * | | | | | + * | PCI NTB | | | | + * | EPF | | | | + * | Driver | | | PCI Virtual | + * | | +---------------+ | NTB Driver | + * | | | PCI EP NTB |<------>| | + * | | | FN Driver | | | + * +------------+ +---------------+ +--------------+ + * | | | | | | + * | PCI Bus | <-----> | PCI EP Bus | | Virtual PCI | + * | | PCI | | | Bus | + * +------------+ +---------------+--------+--------------+ + * PCIe Root Port PCI EP + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include <linux/pci-epc.h> +#include <linux/pci-epf.h> +#include <linux/ntb.h> + +static struct workqueue_struct *kpcintb_workqueue; + +#define COMMAND_CONFIGURE_DOORBELL 1 +#define COMMAND_TEARDOWN_DOORBELL 2 +#define COMMAND_CONFIGURE_MW 3 +#define COMMAND_TEARDOWN_MW 4 +#define COMMAND_LINK_UP 5 +#define COMMAND_LINK_DOWN 6 + +#define COMMAND_STATUS_OK 1 +#define COMMAND_STATUS_ERROR 2 + +#define LINK_STATUS_UP BIT(0) + +#define SPAD_COUNT 64 +#define DB_COUNT 4 +#define NTB_MW_OFFSET 2 +#define DB_COUNT_MASK GENMASK(15, 0) +#define MSIX_ENABLE BIT(16) +#define MAX_DB_COUNT 32 +#define MAX_MW 4 + +enum epf_ntb_bar { + BAR_CONFIG, + BAR_DB, + BAR_MW0, + BAR_MW1, + BAR_MW2, +}; + +/* + * +--------------------------------------------------+ Base + * | | + * | | + * | | + * | Common Control Register | + * | | + * | | + * | | + * +-----------------------+--------------------------+ Base+span_offset + * | | | + * | Peer Span Space | Span Space | + * | | | + * | | | + * +-----------------------+--------------------------+ Base+span_offset + * | | | +span_count * 4 + * | | | + * | Span Space | Peer Span Space | + * | | | + * +-----------------------+--------------------------+ + * Virtual PCI PCIe Endpoint + * NTB Driver NTB Driver + */ +struct epf_ntb_ctrl { + u32 command; + u32 argument; + u16 command_status; + u16 link_status; + u32 topology; + u64 addr; + u64 size; + u32 num_mws; + u32 reserved; + u32 spad_offset; + u32 spad_count; + u32 db_entry_size; + u32 db_data[MAX_DB_COUNT]; + u32 db_offset[MAX_DB_COUNT]; +} __packed; + +struct epf_ntb { + struct ntb_dev ntb; + struct pci_epf *epf; + struct config_group group; + + u32 num_mws; + u32 db_count; + u32 spad_count; + u64 mws_size[MAX_MW]; + u64 db; + u32 vbus_number; + u16 vntb_pid; + u16 vntb_vid; + + bool linkup; + u32 spad_size; + + enum pci_barno epf_ntb_bar[6]; + + struct epf_ntb_ctrl *reg; + + phys_addr_t epf_db_phy; + void __iomem *epf_db; + + phys_addr_t vpci_mw_phy[MAX_MW]; + void __iomem *vpci_mw_addr[MAX_MW]; + + struct delayed_work cmd_handler; +}; + +#define to_epf_ntb(epf_group) container_of((epf_group), struct epf_ntb, group) +#define ntb_ndev(__ntb) container_of(__ntb, struct epf_ntb, ntb) + +static struct pci_epf_header epf_ntb_header = { + .vendorid = PCI_ANY_ID, + .deviceid = PCI_ANY_ID, + .baseclass_code = PCI_BASE_CLASS_MEMORY, + .interrupt_pin = PCI_INTERRUPT_INTA, +}; + +/** + * epf_ntb_link_up() - Raise link_up interrupt to Virtual Host + * @ntb: NTB device that facilitates communication between HOST and VHOST + * @link_up: true or false indicating Link is UP or Down + * + * Once NTB function in HOST invoke ntb_link_enable(), + * this NTB function driver will trigger a link event to vhost. + */ +static int epf_ntb_link_up(struct epf_ntb *ntb, bool link_up) +{ + if (link_up) + ntb->reg->link_status |= LINK_STATUS_UP; + else + ntb->reg->link_status &= ~LINK_STATUS_UP; + + ntb_link_event(&ntb->ntb); + return 0; +} + +/** + * epf_ntb_configure_mw() - Configure the Outbound Address Space for vhost + * to access the memory window of host + * @ntb: NTB device that facilitates communication between host and vhost + * @mw: Index of the memory window (either 0, 1, 2 or 3) + * + * EP Outbound Window + * +--------+ +-----------+ + * | | | | + * | | | | + * | | | | + * | | | | + * | | +-----------+ + * | Virtual| | Memory Win| + * | NTB | -----------> | | + * | Driver | | | + * | | +-----------+ + * | | | | + * | | | | + * +--------+ +-----------+ + * VHost PCI EP + */ +static int epf_ntb_configure_mw(struct epf_ntb *ntb, u32 mw) +{ + phys_addr_t phys_addr; + u8 func_no, vfunc_no; + u64 addr, size; + int ret = 0; + + phys_addr = ntb->vpci_mw_phy[mw]; + addr = ntb->reg->addr; + size = ntb->reg->size; + + func_no = ntb->epf->func_no; + vfunc_no = ntb->epf->vfunc_no; + + ret = pci_epc_map_addr(ntb->epf->epc, func_no, vfunc_no, phys_addr, addr, size); + if (ret) + dev_err(&ntb->epf->epc->dev, + "Failed to map memory window %d address\n", mw); + return ret; +} + +/** + * epf_ntb_teardown_mw() - Teardown the configured OB ATU + * @ntb: NTB device that facilitates communication between HOST and vHOST + * @mw: Index of the memory window (either 0, 1, 2 or 3) + * + * Teardown the configured OB ATU configured in epf_ntb_configure_mw() using + * pci_epc_unmap_addr() + */ +static void epf_ntb_teardown_mw(struct epf_ntb *ntb, u32 mw) +{ + pci_epc_unmap_addr(ntb->epf->epc, + ntb->epf->func_no, + ntb->epf->vfunc_no, + ntb->vpci_mw_phy[mw]); +} + +/** + * epf_ntb_cmd_handler() - Handle commands provided by the NTB Host + * @work: work_struct for the epf_ntb_epc + * + * Workqueue function that gets invoked for the two epf_ntb_epc + * periodically (once every 5ms) to see if it has received any commands + * from NTB host. The host can send commands to configure doorbell or + * configure memory window or to update link status. + */ +static void epf_ntb_cmd_handler(struct work_struct *work) +{ + struct epf_ntb_ctrl *ctrl; + u32 command, argument; + struct epf_ntb *ntb; + struct device *dev; + int ret; + int i; + + ntb = container_of(work, struct epf_ntb, cmd_handler.work); + + for (i = 1; i < ntb->db_count; i++) { + if (readl(ntb->epf_db + i * 4)) { + if (readl(ntb->epf_db + i * 4)) + ntb->db |= 1 << (i - 1); + + ntb_db_event(&ntb->ntb, i); + writel(0, ntb->epf_db + i * 4); + } + } + + ctrl = ntb->reg; + command = ctrl->command; + if (!command) + goto reset_handler; + argument = ctrl->argument; + + ctrl->command = 0; + ctrl->argument = 0; + + ctrl = ntb->reg; + dev = &ntb->epf->dev; + + switch (command) { + case COMMAND_CONFIGURE_DOORBELL: + ctrl->command_status = COMMAND_STATUS_OK; + break; + case COMMAND_TEARDOWN_DOORBELL: + ctrl->command_status = COMMAND_STATUS_OK; + break; + case COMMAND_CONFIGURE_MW: + ret = epf_ntb_configure_mw(ntb, argument); + if (ret < 0) + ctrl->command_status = COMMAND_STATUS_ERROR; + else + ctrl->command_status = COMMAND_STATUS_OK; + break; + case COMMAND_TEARDOWN_MW: + epf_ntb_teardown_mw(ntb, argument); + ctrl->command_status = COMMAND_STATUS_OK; + break; + case COMMAND_LINK_UP: + ntb->linkup = true; + ret = epf_ntb_link_up(ntb, true); + if (ret < 0) + ctrl->command_status = COMMAND_STATUS_ERROR; + else + ctrl->command_status = COMMAND_STATUS_OK; + goto reset_handler; + case COMMAND_LINK_DOWN: + ntb->linkup = false; + ret = epf_ntb_link_up(ntb, false); + if (ret < 0) + ctrl->command_status = COMMAND_STATUS_ERROR; + else + ctrl->command_status = COMMAND_STATUS_OK; + break; + default: + dev_err(dev, "UNKNOWN command: %d\n", command); + break; + } + +reset_handler: + queue_delayed_work(kpcintb_workqueue, &ntb->cmd_handler, + msecs_to_jiffies(5)); +} + +/** + * epf_ntb_config_sspad_bar_clear() - Clear Config + Self scratchpad BAR + * @ntb_epc: EPC associated with one of the HOST which holds peer's outbound + * address. + * + * Clear BAR0 of EP CONTROLLER 1 which contains the HOST1's config and + * self scratchpad region (removes inbound ATU configuration). While BAR0 is + * the default self scratchpad BAR, an NTB could have other BARs for self + * scratchpad (because of reserved BARs). This function can get the exact BAR + * used for self scratchpad from epf_ntb_bar[BAR_CONFIG]. + * + * Please note the self scratchpad region and config region is combined to + * a single region and mapped using the same BAR. Also note HOST2's peer + * scratchpad is HOST1's self scratchpad. + */ +static void epf_ntb_config_sspad_bar_clear(struct epf_ntb *ntb) +{ + struct pci_epf_bar *epf_bar; + enum pci_barno barno; + + barno = ntb->epf_ntb_bar[BAR_CONFIG]; + epf_bar = &ntb->epf->bar[barno]; + + pci_epc_clear_bar(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no, epf_bar); +} + +/** + * epf_ntb_config_sspad_bar_set() - Set Config + Self scratchpad BAR + * @ntb: NTB device that facilitates communication between HOST and vHOST + * + * Map BAR0 of EP CONTROLLER 1 which contains the HOST1's config and + * self scratchpad region. + * + * Please note the self scratchpad region and config region is combined to + * a single region and mapped using the same BAR. + */ +static int epf_ntb_config_sspad_bar_set(struct epf_ntb *ntb) +{ + struct pci_epf_bar *epf_bar; + enum pci_barno barno; + u8 func_no, vfunc_no; + struct device *dev; + int ret; + + dev = &ntb->epf->dev; + func_no = ntb->epf->func_no; + vfunc_no = ntb->epf->vfunc_no; + barno = ntb->epf_ntb_bar[BAR_CONFIG]; + epf_bar = &ntb->epf->bar[barno]; + + ret = pci_epc_set_bar(ntb->epf->epc, func_no, vfunc_no, epf_bar); + if (ret) { + dev_err(dev, "inft: Config/Status/SPAD BAR set failed\n"); + return ret; + } + return 0; +} + +/** + * epf_ntb_config_spad_bar_free() - Free the physical memory associated with + * config + scratchpad region + * @ntb: NTB device that facilitates communication between HOST and vHOST + */ +static void epf_ntb_config_spad_bar_free(struct epf_ntb *ntb) +{ + enum pci_barno barno; + + barno = ntb->epf_ntb_bar[BAR_CONFIG]; + pci_epf_free_space(ntb->epf, ntb->reg, barno, 0); +} + +/** + * epf_ntb_config_spad_bar_alloc() - Allocate memory for config + scratchpad + * region + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * + * Allocate the Local Memory mentioned in the above diagram. The size of + * CONFIG REGION is sizeof(struct epf_ntb_ctrl) and size of SCRATCHPAD REGION + * is obtained from "spad-count" configfs entry. + */ +static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb) +{ + size_t align; + enum pci_barno barno; + struct epf_ntb_ctrl *ctrl; + u32 spad_size, ctrl_size; + u64 size; + struct pci_epf *epf = ntb->epf; + struct device *dev = &epf->dev; + u32 spad_count; + void *base; + int i; + const struct pci_epc_features *epc_features = pci_epc_get_features(epf->epc, + epf->func_no, + epf->vfunc_no); + barno = ntb->epf_ntb_bar[BAR_CONFIG]; + size = epc_features->bar_fixed_size[barno]; + align = epc_features->align; + + if ((!IS_ALIGNED(size, align))) + return -EINVAL; + + spad_count = ntb->spad_count; + + ctrl_size = sizeof(struct epf_ntb_ctrl); + spad_size = 2 * spad_count * 4; + + if (!align) { + ctrl_size = roundup_pow_of_two(ctrl_size); + spad_size = roundup_pow_of_two(spad_size); + } else { + ctrl_size = ALIGN(ctrl_size, align); + spad_size = ALIGN(spad_size, align); + } + + if (!size) + size = ctrl_size + spad_size; + else if (size < ctrl_size + spad_size) + return -EINVAL; + + base = pci_epf_alloc_space(epf, size, barno, align, 0); + if (!base) { + dev_err(dev, "Config/Status/SPAD alloc region fail\n"); + return -ENOMEM; + } + + ntb->reg = base; + + ctrl = ntb->reg; + ctrl->spad_offset = ctrl_size; + + ctrl->spad_count = spad_count; + ctrl->num_mws = ntb->num_mws; + ntb->spad_size = spad_size; + + ctrl->db_entry_size = 4; + + for (i = 0; i < ntb->db_count; i++) { + ntb->reg->db_data[i] = 1 + i; + ntb->reg->db_offset[i] = 0; + } + + return 0; +} + +/** + * epf_ntb_configure_interrupt() - Configure MSI/MSI-X capaiblity + * @ntb: NTB device that facilitates communication between HOST and vHOST + * + * Configure MSI/MSI-X capability for each interface with number of + * interrupts equal to "db_count" configfs entry. + */ +static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) +{ + const struct pci_epc_features *epc_features; + struct device *dev; + u32 db_count; + int ret; + + dev = &ntb->epf->dev; + + epc_features = pci_epc_get_features(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no); + + if (!(epc_features->msix_capable || epc_features->msi_capable)) { + dev_err(dev, "MSI or MSI-X is required for doorbell\n"); + return -EINVAL; + } + + db_count = ntb->db_count; + if (db_count > MAX_DB_COUNT) { + dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT); + return -EINVAL; + } + + ntb->db_count = db_count; + + if (epc_features->msi_capable) { + ret = pci_epc_set_msi(ntb->epf->epc, + ntb->epf->func_no, + ntb->epf->vfunc_no, + 16); + if (ret) { + dev_err(dev, "MSI configuration failed\n"); + return ret; + } + } + + return 0; +} + +/** + * epf_ntb_db_bar_init() - Configure Doorbell window BARs + * @ntb: NTB device that facilitates communication between HOST and vHOST + */ +static int epf_ntb_db_bar_init(struct epf_ntb *ntb) +{ + const struct pci_epc_features *epc_features; + u32 align; + struct device *dev = &ntb->epf->dev; + int ret; + struct pci_epf_bar *epf_bar; + void __iomem *mw_addr; + enum pci_barno barno; + size_t size = 4 * ntb->db_count; + + epc_features = pci_epc_get_features(ntb->epf->epc, + ntb->epf->func_no, + ntb->epf->vfunc_no); + align = epc_features->align; + + if (size < 128) + size = 128; + + if (align) + size = ALIGN(size, align); + else + size = roundup_pow_of_two(size); + + barno = ntb->epf_ntb_bar[BAR_DB]; + + mw_addr = pci_epf_alloc_space(ntb->epf, size, barno, align, 0); + if (!mw_addr) { + dev_err(dev, "Failed to allocate OB address\n"); + return -ENOMEM; + } + + ntb->epf_db = mw_addr; + + epf_bar = &ntb->epf->bar[barno]; + + ret = pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no, epf_bar); + if (ret) { + dev_err(dev, "Doorbell BAR set failed\n"); + goto err_alloc_peer_mem; + } + return ret; + +err_alloc_peer_mem: + pci_epc_mem_free_addr(ntb->epf->epc, epf_bar->phys_addr, mw_addr, epf_bar->size); + return -1; +} + +static void epf_ntb_mw_bar_clear(struct epf_ntb *ntb, int num_mws); + +/** + * epf_ntb_db_bar_clear() - Clear doorbell BAR and free memory + * allocated in peer's outbound address space + * @ntb: NTB device that facilitates communication between HOST and vHOST + */ +static void epf_ntb_db_bar_clear(struct epf_ntb *ntb) +{ + enum pci_barno barno; + + barno = ntb->epf_ntb_bar[BAR_DB]; + pci_epf_free_space(ntb->epf, ntb->epf_db, barno, 0); + pci_epc_clear_bar(ntb->epf->epc, + ntb->epf->func_no, + ntb->epf->vfunc_no, + &ntb->epf->bar[barno]); +} + +/** + * epf_ntb_mw_bar_init() - Configure Memory window BARs + * @ntb: NTB device that facilitates communication between HOST and vHOST + * + */ +static int epf_ntb_mw_bar_init(struct epf_ntb *ntb) +{ + int ret = 0; + int i; + u64 size; + enum pci_barno barno; + struct device *dev = &ntb->epf->dev; + + for (i = 0; i < ntb->num_mws; i++) { + size = ntb->mws_size[i]; + barno = ntb->epf_ntb_bar[BAR_MW0 + i]; + + ntb->epf->bar[barno].barno = barno; + ntb->epf->bar[barno].size = size; + ntb->epf->bar[barno].addr = NULL; + ntb->epf->bar[barno].phys_addr = 0; + ntb->epf->bar[barno].flags |= upper_32_bits(size) ? + PCI_BASE_ADDRESS_MEM_TYPE_64 : + PCI_BASE_ADDRESS_MEM_TYPE_32; + + ret = pci_epc_set_bar(ntb->epf->epc, + ntb->epf->func_no, + ntb->epf->vfunc_no, + &ntb->epf->bar[barno]); + if (ret) { + dev_err(dev, "MW set failed\n"); + goto err_alloc_mem; + } + + /* Allocate EPC outbound memory windows to vpci vntb device */ + ntb->vpci_mw_addr[i] = pci_epc_mem_alloc_addr(ntb->epf->epc, + &ntb->vpci_mw_phy[i], + size); + if (!ntb->vpci_mw_addr[i]) { + ret = -ENOMEM; + dev_err(dev, "Failed to allocate source address\n"); + goto err_set_bar; + } + } + + return ret; + +err_set_bar: + pci_epc_clear_bar(ntb->epf->epc, + ntb->epf->func_no, + ntb->epf->vfunc_no, + &ntb->epf->bar[barno]); +err_alloc_mem: + epf_ntb_mw_bar_clear(ntb, i); + return ret; +} + +/** + * epf_ntb_mw_bar_clear() - Clear Memory window BARs + * @ntb: NTB device that facilitates communication between HOST and vHOST + */ +static void epf_ntb_mw_bar_clear(struct epf_ntb *ntb, int num_mws) +{ + enum pci_barno barno; + int i; + + for (i = 0; i < num_mws; i++) { + barno = ntb->epf_ntb_bar[BAR_MW0 + i]; + pci_epc_clear_bar(ntb->epf->epc, + ntb->epf->func_no, + ntb->epf->vfunc_no, + &ntb->epf->bar[barno]); + + pci_epc_mem_free_addr(ntb->epf->epc, + ntb->vpci_mw_phy[i], + ntb->vpci_mw_addr[i], + ntb->mws_size[i]); + } +} + +/** + * epf_ntb_epc_destroy() - Cleanup NTB EPC interface + * @ntb: NTB device that facilitates communication between HOST and vHOST + * + * Wrapper for epf_ntb_epc_destroy_interface() to cleanup all the NTB interfaces + */ +static void epf_ntb_epc_destroy(struct epf_ntb *ntb) +{ + pci_epc_remove_epf(ntb->epf->epc, ntb->epf, 0); + pci_epc_put(ntb->epf->epc); +} + +/** + * epf_ntb_init_epc_bar() - Identify BARs to be used for each of the NTB + * constructs (scratchpad region, doorbell, memorywindow) + * @ntb: NTB device that facilitates communication between HOST and vHOST + */ +static int epf_ntb_init_epc_bar(struct epf_ntb *ntb) +{ + const struct pci_epc_features *epc_features; + enum pci_barno barno; + enum epf_ntb_bar bar; + struct device *dev; + u32 num_mws; + int i; + + barno = BAR_0; + num_mws = ntb->num_mws; + dev = &ntb->epf->dev; + epc_features = pci_epc_get_features(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no); + + /* These are required BARs which are mandatory for NTB functionality */ + for (bar = BAR_CONFIG; bar <= BAR_MW0; bar++, barno++) { + barno = pci_epc_get_next_free_bar(epc_features, barno); + if (barno < 0) { + dev_err(dev, "Fail to get NTB function BAR\n"); + return barno; + } + ntb->epf_ntb_bar[bar] = barno; + } + + /* These are optional BARs which don't impact NTB functionality */ + for (bar = BAR_MW1, i = 1; i < num_mws; bar++, barno++, i++) { + barno = pci_epc_get_next_free_bar(epc_features, barno); + if (barno < 0) { + ntb->num_mws = i; + dev_dbg(dev, "BAR not available for > MW%d\n", i + 1); + } + ntb->epf_ntb_bar[bar] = barno; + } + + return 0; +} + +/** + * epf_ntb_epc_init() - Initialize NTB interface + * @ntb: NTB device that facilitates communication between HOST and vHOST2 + * + * Wrapper to initialize a particular EPC interface and start the workqueue + * to check for commands from host. This function will write to the + * EP controller HW for configuring it. + */ +static int epf_ntb_epc_init(struct epf_ntb *ntb) +{ + u8 func_no, vfunc_no; + struct pci_epc *epc; + struct pci_epf *epf; + struct device *dev; + int ret; + + epf = ntb->epf; + dev = &epf->dev; + epc = epf->epc; + func_no = ntb->epf->func_no; + vfunc_no = ntb->epf->vfunc_no; + + ret = epf_ntb_config_sspad_bar_set(ntb); + if (ret) { + dev_err(dev, "Config/self SPAD BAR init failed"); + return ret; + } + + ret = epf_ntb_configure_interrupt(ntb); + if (ret) { + dev_err(dev, "Interrupt configuration failed\n"); + goto err_config_interrupt; + } + + ret = epf_ntb_db_bar_init(ntb); + if (ret) { + dev_err(dev, "DB BAR init failed\n"); + goto err_db_bar_init; + } + + ret = epf_ntb_mw_bar_init(ntb); + if (ret) { + dev_err(dev, "MW BAR init failed\n"); + goto err_mw_bar_init; + } + + if (vfunc_no <= 1) { + ret = pci_epc_write_header(epc, func_no, vfunc_no, epf->header); + if (ret) { + dev_err(dev, "Configuration header write failed\n"); + goto err_write_header; + } + } + + INIT_DELAYED_WORK(&ntb->cmd_handler, epf_ntb_cmd_handler); + queue_work(kpcintb_workqueue, &ntb->cmd_handler.work); + + return 0; + +err_write_header: + epf_ntb_mw_bar_clear(ntb, ntb->num_mws); +err_mw_bar_init: + epf_ntb_db_bar_clear(ntb); +err_db_bar_init: +err_config_interrupt: + epf_ntb_config_sspad_bar_clear(ntb); + + return ret; +} + + +/** + * epf_ntb_epc_cleanup() - Cleanup all NTB interfaces + * @ntb: NTB device that facilitates communication between HOST1 and HOST2 + * + * Wrapper to cleanup all NTB interfaces. + */ +static void epf_ntb_epc_cleanup(struct epf_ntb *ntb) +{ + epf_ntb_db_bar_clear(ntb); + epf_ntb_mw_bar_clear(ntb, ntb->num_mws); +} + +#define EPF_NTB_R(_name) \ +static ssize_t epf_ntb_##_name##_show(struct config_item *item, \ + char *page) \ +{ \ + struct config_group *group = to_config_group(item); \ + struct epf_ntb *ntb = to_epf_ntb(group); \ + \ + return sprintf(page, "%d\n", ntb->_name); \ +} + +#define EPF_NTB_W(_name) \ +static ssize_t epf_ntb_##_name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct config_group *group = to_config_group(item); \ + struct epf_ntb *ntb = to_epf_ntb(group); \ + u32 val; \ + int ret; \ + \ + ret = kstrtou32(page, 0, &val); \ + if (ret) \ + return ret; \ + \ + ntb->_name = val; \ + \ + return len; \ +} + +#define EPF_NTB_MW_R(_name) \ +static ssize_t epf_ntb_##_name##_show(struct config_item *item, \ + char *page) \ +{ \ + struct config_group *group = to_config_group(item); \ + struct epf_ntb *ntb = to_epf_ntb(group); \ + struct device *dev = &ntb->epf->dev; \ + int win_no; \ + \ + if (sscanf(#_name, "mw%d", &win_no) != 1) \ + return -EINVAL; \ + \ + if (win_no <= 0 || win_no > ntb->num_mws) { \ + dev_err(dev, "Invalid num_nws: %d value\n", ntb->num_mws); \ + return -EINVAL; \ + } \ + \ + return sprintf(page, "%lld\n", ntb->mws_size[win_no - 1]); \ +} + +#define EPF_NTB_MW_W(_name) \ +static ssize_t epf_ntb_##_name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct config_group *group = to_config_group(item); \ + struct epf_ntb *ntb = to_epf_ntb(group); \ + struct device *dev = &ntb->epf->dev; \ + int win_no; \ + u64 val; \ + int ret; \ + \ + ret = kstrtou64(page, 0, &val); \ + if (ret) \ + return ret; \ + \ + if (sscanf(#_name, "mw%d", &win_no) != 1) \ + return -EINVAL; \ + \ + if (win_no <= 0 || win_no > ntb->num_mws) { \ + dev_err(dev, "Invalid num_nws: %d value\n", ntb->num_mws); \ + return -EINVAL; \ + } \ + \ + ntb->mws_size[win_no - 1] = val; \ + \ + return len; \ +} + +static ssize_t epf_ntb_num_mws_store(struct config_item *item, + const char *page, size_t len) +{ + struct config_group *group = to_config_group(item); + struct epf_ntb *ntb = to_epf_ntb(group); + u32 val; + int ret; + + ret = kstrtou32(page, 0, &val); + if (ret) + return ret; + + if (val > MAX_MW) + return -EINVAL; + + ntb->num_mws = val; + + return len; +} + +EPF_NTB_R(spad_count) +EPF_NTB_W(spad_count) +EPF_NTB_R(db_count) +EPF_NTB_W(db_count) +EPF_NTB_R(num_mws) +EPF_NTB_R(vbus_number) +EPF_NTB_W(vbus_number) +EPF_NTB_R(vntb_pid) +EPF_NTB_W(vntb_pid) +EPF_NTB_R(vntb_vid) +EPF_NTB_W(vntb_vid) +EPF_NTB_MW_R(mw1) +EPF_NTB_MW_W(mw1) +EPF_NTB_MW_R(mw2) +EPF_NTB_MW_W(mw2) +EPF_NTB_MW_R(mw3) +EPF_NTB_MW_W(mw3) +EPF_NTB_MW_R(mw4) +EPF_NTB_MW_W(mw4) + +CONFIGFS_ATTR(epf_ntb_, spad_count); +CONFIGFS_ATTR(epf_ntb_, db_count); +CONFIGFS_ATTR(epf_ntb_, num_mws); +CONFIGFS_ATTR(epf_ntb_, mw1); +CONFIGFS_ATTR(epf_ntb_, mw2); +CONFIGFS_ATTR(epf_ntb_, mw3); +CONFIGFS_ATTR(epf_ntb_, mw4); +CONFIGFS_ATTR(epf_ntb_, vbus_number); +CONFIGFS_ATTR(epf_ntb_, vntb_pid); +CONFIGFS_ATTR(epf_ntb_, vntb_vid); + +static struct configfs_attribute *epf_ntb_attrs[] = { + &epf_ntb_attr_spad_count, + &epf_ntb_attr_db_count, + &epf_ntb_attr_num_mws, + &epf_ntb_attr_mw1, + &epf_ntb_attr_mw2, + &epf_ntb_attr_mw3, + &epf_ntb_attr_mw4, + &epf_ntb_attr_vbus_number, + &epf_ntb_attr_vntb_pid, + &epf_ntb_attr_vntb_vid, + NULL, +}; + +static const struct config_item_type ntb_group_type = { + .ct_attrs = epf_ntb_attrs, + .ct_owner = THIS_MODULE, +}; + +/** + * epf_ntb_add_cfs() - Add configfs directory specific to NTB + * @epf: NTB endpoint function device + * @group: A pointer to the config_group structure referencing a group of + * config_items of a specific type that belong to a specific sub-system. + * + * Add configfs directory specific to NTB. This directory will hold + * NTB specific properties like db_count, spad_count, num_mws etc., + */ +static struct config_group *epf_ntb_add_cfs(struct pci_epf *epf, + struct config_group *group) +{ + struct epf_ntb *ntb = epf_get_drvdata(epf); + struct config_group *ntb_group = &ntb->group; + struct device *dev = &epf->dev; + + config_group_init_type_name(ntb_group, dev_name(dev), &ntb_group_type); + + return ntb_group; +} + +/*==== virtual PCI bus driver, which only load virtual NTB PCI driver ====*/ + +static u32 pci_space[] = { + 0xffffffff, /*DeviceID, Vendor ID*/ + 0, /*Status, Command*/ + 0xffffffff, /*Class code, subclass, prog if, revision id*/ + 0x40, /*bist, header type, latency Timer, cache line size*/ + 0, /*BAR 0*/ + 0, /*BAR 1*/ + 0, /*BAR 2*/ + 0, /*BAR 3*/ + 0, /*BAR 4*/ + 0, /*BAR 5*/ + 0, /*Cardbus cis point*/ + 0, /*Subsystem ID Subystem vendor id*/ + 0, /*ROM Base Address*/ + 0, /*Reserved, Cap. Point*/ + 0, /*Reserved,*/ + 0, /*Max Lat, Min Gnt, interrupt pin, interrupt line*/ +}; + +static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) +{ + if (devfn == 0) { + memcpy(val, ((u8 *)pci_space) + where, size); + return PCIBIOS_SUCCESSFUL; + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) +{ + return 0; +} + +static struct pci_ops vpci_ops = { + .read = pci_read, + .write = pci_write, +}; + +static int vpci_scan_bus(void *sysdata) +{ + struct pci_bus *vpci_bus; + struct epf_ntb *ndev = sysdata; + + vpci_bus = pci_scan_bus(ndev->vbus_number, &vpci_ops, sysdata); + if (vpci_bus) + pr_err("create pci bus\n"); + + pci_bus_add_devices(vpci_bus); + + return 0; +} + +/*==================== Virtual PCIe NTB driver ==========================*/ + +static int vntb_epf_mw_count(struct ntb_dev *ntb, int pidx) +{ + struct epf_ntb *ndev = ntb_ndev(ntb); + + return ndev->num_mws; +} + +static int vntb_epf_spad_count(struct ntb_dev *ntb) +{ + return ntb_ndev(ntb)->spad_count; +} + +static int vntb_epf_peer_mw_count(struct ntb_dev *ntb) +{ + return ntb_ndev(ntb)->num_mws; +} + +static u64 vntb_epf_db_valid_mask(struct ntb_dev *ntb) +{ + return BIT_ULL(ntb_ndev(ntb)->db_count) - 1; +} + +static int vntb_epf_db_set_mask(struct ntb_dev *ntb, u64 db_bits) +{ + return 0; +} + +static int vntb_epf_mw_set_trans(struct ntb_dev *ndev, int pidx, int idx, + dma_addr_t addr, resource_size_t size) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + struct pci_epf_bar *epf_bar; + enum pci_barno barno; + int ret; + struct device *dev; + + dev = &ntb->ntb.dev; + barno = ntb->epf_ntb_bar[BAR_MW0 + idx]; + epf_bar = &ntb->epf->bar[barno]; + epf_bar->phys_addr = addr; + epf_bar->barno = barno; + epf_bar->size = size; + + ret = pci_epc_set_bar(ntb->epf->epc, 0, 0, epf_bar); + if (ret) { + dev_err(dev, "failure set mw trans\n"); + return ret; + } + return 0; +} + +static int vntb_epf_mw_clear_trans(struct ntb_dev *ntb, int pidx, int idx) +{ + return 0; +} + +static int vntb_epf_peer_mw_get_addr(struct ntb_dev *ndev, int idx, + phys_addr_t *base, resource_size_t *size) +{ + + struct epf_ntb *ntb = ntb_ndev(ndev); + + if (base) + *base = ntb->vpci_mw_phy[idx]; + + if (size) + *size = ntb->mws_size[idx]; + + return 0; +} + +static int vntb_epf_link_enable(struct ntb_dev *ntb, + enum ntb_speed max_speed, + enum ntb_width max_width) +{ + return 0; +} + +static u32 vntb_epf_spad_read(struct ntb_dev *ndev, int idx) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + int off = ntb->reg->spad_offset, ct = ntb->reg->spad_count * 4; + u32 val; + void __iomem *base = ntb->reg; + + val = readl(base + off + ct + idx * 4); + return val; +} + +static int vntb_epf_spad_write(struct ntb_dev *ndev, int idx, u32 val) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + struct epf_ntb_ctrl *ctrl = ntb->reg; + int off = ctrl->spad_offset, ct = ctrl->spad_count * 4; + void __iomem *base = ntb->reg; + + writel(val, base + off + ct + idx * 4); + return 0; +} + +static u32 vntb_epf_peer_spad_read(struct ntb_dev *ndev, int pidx, int idx) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + struct epf_ntb_ctrl *ctrl = ntb->reg; + int off = ctrl->spad_offset; + void __iomem *base = ntb->reg; + u32 val; + + val = readl(base + off + idx * 4); + return val; +} + +static int vntb_epf_peer_spad_write(struct ntb_dev *ndev, int pidx, int idx, u32 val) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + struct epf_ntb_ctrl *ctrl = ntb->reg; + int off = ctrl->spad_offset; + void __iomem *base = ntb->reg; + + writel(val, base + off + idx * 4); + return 0; +} + +static int vntb_epf_peer_db_set(struct ntb_dev *ndev, u64 db_bits) +{ + u32 interrupt_num = ffs(db_bits) + 1; + struct epf_ntb *ntb = ntb_ndev(ndev); + u8 func_no, vfunc_no; + int ret; + + func_no = ntb->epf->func_no; + vfunc_no = ntb->epf->vfunc_no; + + ret = pci_epc_raise_irq(ntb->epf->epc, + func_no, + vfunc_no, + PCI_EPC_IRQ_MSI, + interrupt_num + 1); + if (ret) + dev_err(&ntb->ntb.dev, "Failed to raise IRQ\n"); + + return ret; +} + +static u64 vntb_epf_db_read(struct ntb_dev *ndev) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + + return ntb->db; +} + +static int vntb_epf_mw_get_align(struct ntb_dev *ndev, int pidx, int idx, + resource_size_t *addr_align, + resource_size_t *size_align, + resource_size_t *size_max) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + + if (addr_align) + *addr_align = SZ_4K; + + if (size_align) + *size_align = 1; + + if (size_max) + *size_max = ntb->mws_size[idx]; + + return 0; +} + +static u64 vntb_epf_link_is_up(struct ntb_dev *ndev, + enum ntb_speed *speed, + enum ntb_width *width) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + + return ntb->reg->link_status; +} + +static int vntb_epf_db_clear_mask(struct ntb_dev *ndev, u64 db_bits) +{ + return 0; +} + +static int vntb_epf_db_clear(struct ntb_dev *ndev, u64 db_bits) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + + ntb->db &= ~db_bits; + return 0; +} + +static int vntb_epf_link_disable(struct ntb_dev *ntb) +{ + return 0; +} + +static const struct ntb_dev_ops vntb_epf_ops = { + .mw_count = vntb_epf_mw_count, + .spad_count = vntb_epf_spad_count, + .peer_mw_count = vntb_epf_peer_mw_count, + .db_valid_mask = vntb_epf_db_valid_mask, + .db_set_mask = vntb_epf_db_set_mask, + .mw_set_trans = vntb_epf_mw_set_trans, + .mw_clear_trans = vntb_epf_mw_clear_trans, + .peer_mw_get_addr = vntb_epf_peer_mw_get_addr, + .link_enable = vntb_epf_link_enable, + .spad_read = vntb_epf_spad_read, + .spad_write = vntb_epf_spad_write, + .peer_spad_read = vntb_epf_peer_spad_read, + .peer_spad_write = vntb_epf_peer_spad_write, + .peer_db_set = vntb_epf_peer_db_set, + .db_read = vntb_epf_db_read, + .mw_get_align = vntb_epf_mw_get_align, + .link_is_up = vntb_epf_link_is_up, + .db_clear_mask = vntb_epf_db_clear_mask, + .db_clear = vntb_epf_db_clear, + .link_disable = vntb_epf_link_disable, +}; + +static int pci_vntb_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int ret; + struct epf_ntb *ndev = (struct epf_ntb *)pdev->sysdata; + struct device *dev = &pdev->dev; + + ndev->ntb.pdev = pdev; + ndev->ntb.topo = NTB_TOPO_NONE; + ndev->ntb.ops = &vntb_epf_ops; + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(dev, "Cannot set DMA mask\n"); + return -EINVAL; + } + + ret = ntb_register_device(&ndev->ntb); + if (ret) { + dev_err(dev, "Failed to register NTB device\n"); + goto err_register_dev; + } + + dev_dbg(dev, "PCI Virtual NTB driver loaded\n"); + return 0; + +err_register_dev: + return -EINVAL; +} + +static struct pci_device_id pci_vntb_table[] = { + { + PCI_DEVICE(0xffff, 0xffff), + }, + {}, +}; + +static struct pci_driver vntb_pci_driver = { + .name = "pci-vntb", + .id_table = pci_vntb_table, + .probe = pci_vntb_probe, +}; + +/* ============ PCIe EPF Driver Bind ====================*/ + +/** + * epf_ntb_bind() - Initialize endpoint controller to provide NTB functionality + * @epf: NTB endpoint function device + * + * Initialize both the endpoint controllers associated with NTB function device. + * Invoked when a primary interface or secondary interface is bound to EPC + * device. This function will succeed only when EPC is bound to both the + * interfaces. + */ +static int epf_ntb_bind(struct pci_epf *epf) +{ + struct epf_ntb *ntb = epf_get_drvdata(epf); + struct device *dev = &epf->dev; + int ret; + + if (!epf->epc) { + dev_dbg(dev, "PRIMARY EPC interface not yet bound\n"); + return 0; + } + + ret = epf_ntb_init_epc_bar(ntb); + if (ret) { + dev_err(dev, "Failed to create NTB EPC\n"); + goto err_bar_init; + } + + ret = epf_ntb_config_spad_bar_alloc(ntb); + if (ret) { + dev_err(dev, "Failed to allocate BAR memory\n"); + goto err_bar_alloc; + } + + ret = epf_ntb_epc_init(ntb); + if (ret) { + dev_err(dev, "Failed to initialize EPC\n"); + goto err_bar_alloc; + } + + epf_set_drvdata(epf, ntb); + + pci_space[0] = (ntb->vntb_pid << 16) | ntb->vntb_vid; + pci_vntb_table[0].vendor = ntb->vntb_vid; + pci_vntb_table[0].device = ntb->vntb_pid; + + ret = pci_register_driver(&vntb_pci_driver); + if (ret) { + dev_err(dev, "failure register vntb pci driver\n"); + goto err_bar_alloc; + } + + vpci_scan_bus(ntb); + + return 0; + +err_bar_alloc: + epf_ntb_config_spad_bar_free(ntb); + +err_bar_init: + epf_ntb_epc_destroy(ntb); + + return ret; +} + +/** + * epf_ntb_unbind() - Cleanup the initialization from epf_ntb_bind() + * @epf: NTB endpoint function device + * + * Cleanup the initialization from epf_ntb_bind() + */ +static void epf_ntb_unbind(struct pci_epf *epf) +{ + struct epf_ntb *ntb = epf_get_drvdata(epf); + + epf_ntb_epc_cleanup(ntb); + epf_ntb_config_spad_bar_free(ntb); + epf_ntb_epc_destroy(ntb); + + pci_unregister_driver(&vntb_pci_driver); +} + +// EPF driver probe +static struct pci_epf_ops epf_ntb_ops = { + .bind = epf_ntb_bind, + .unbind = epf_ntb_unbind, + .add_cfs = epf_ntb_add_cfs, +}; + +/** + * epf_ntb_probe() - Probe NTB function driver + * @epf: NTB endpoint function device + * + * Probe NTB function driver when endpoint function bus detects a NTB + * endpoint function. + */ +static int epf_ntb_probe(struct pci_epf *epf) +{ + struct epf_ntb *ntb; + struct device *dev; + + dev = &epf->dev; + + ntb = devm_kzalloc(dev, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; + + epf->header = &epf_ntb_header; + ntb->epf = epf; + ntb->vbus_number = 0xff; + epf_set_drvdata(epf, ntb); + + dev_info(dev, "pci-ep epf driver loaded\n"); + return 0; +} + +static const struct pci_epf_device_id epf_ntb_ids[] = { + { + .name = "pci_epf_vntb", + }, + {}, +}; + +static struct pci_epf_driver epf_ntb_driver = { + .driver.name = "pci_epf_vntb", + .probe = epf_ntb_probe, + .id_table = epf_ntb_ids, + .ops = &epf_ntb_ops, + .owner = THIS_MODULE, +}; + +static int __init epf_ntb_init(void) +{ + int ret; + + kpcintb_workqueue = alloc_workqueue("kpcintb", WQ_MEM_RECLAIM | + WQ_HIGHPRI, 0); + ret = pci_epf_register_driver(&epf_ntb_driver); + if (ret) { + destroy_workqueue(kpcintb_workqueue); + pr_err("Failed to register pci epf ntb driver --> %d\n", ret); + return ret; + } + + return 0; +} +module_init(epf_ntb_init); + +static void __exit epf_ntb_exit(void) +{ + pci_epf_unregister_driver(&epf_ntb_driver); + destroy_workqueue(kpcintb_workqueue); +} +module_exit(epf_ntb_exit); + +MODULE_DESCRIPTION("PCI EPF NTB DRIVER"); +MODULE_AUTHOR("Frank Li <Frank.li@nxp.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/perf/riscv_pmu.c b/drivers/perf/riscv_pmu.c index 2c961839903d..ebca5eab9c9b 100644 --- a/drivers/perf/riscv_pmu.c +++ b/drivers/perf/riscv_pmu.c @@ -170,7 +170,6 @@ int riscv_pmu_event_set_period(struct perf_event *event) left = (max_period >> 1); local64_set(&hwc->prev_count, (u64)-left); - perf_event_update_userpage(event); return overflow; } diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c index 79a3de515ece..6f6681bbfd36 100644 --- a/drivers/perf/riscv_pmu_sbi.c +++ b/drivers/perf/riscv_pmu_sbi.c @@ -41,20 +41,6 @@ static const struct attribute_group *riscv_pmu_attr_groups[] = { NULL, }; -union sbi_pmu_ctr_info { - unsigned long value; - struct { - unsigned long csr:12; - unsigned long width:6; -#if __riscv_xlen == 32 - unsigned long reserved:13; -#else - unsigned long reserved:45; -#endif - unsigned long type:1; - }; -}; - /* * RISC-V doesn't have hetergenous harts yet. This need to be part of * per_cpu in case of harts with different pmu counters @@ -294,8 +280,13 @@ static int pmu_sbi_ctr_get_idx(struct perf_event *event) cflags |= SBI_PMU_CFG_FLAG_SET_UINH; /* retrieve the available counter index */ +#if defined(CONFIG_32BIT) + ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase, cmask, + cflags, hwc->event_base, hwc->config, hwc->config >> 32); +#else ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase, cmask, cflags, hwc->event_base, hwc->config, 0); +#endif if (ret.error) { pr_debug("Not able to find a counter for event %lx config %llx\n", hwc->event_base, hwc->config); @@ -437,8 +428,13 @@ static void pmu_sbi_ctr_start(struct perf_event *event, u64 ival) struct hw_perf_event *hwc = &event->hw; unsigned long flag = SBI_PMU_START_FLAG_SET_INIT_VALUE; +#if defined(CONFIG_32BIT) ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, hwc->idx, 1, flag, ival, ival >> 32, 0); +#else + ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, hwc->idx, + 1, flag, ival, 0, 0); +#endif if (ret.error && (ret.error != SBI_ERR_ALREADY_STARTED)) pr_err("Starting counter idx %d failed with error %d\n", hwc->idx, sbi_err_map_linux_errno(ret.error)); @@ -545,8 +541,14 @@ static inline void pmu_sbi_start_overflow_mask(struct riscv_pmu *pmu, hwc = &event->hw; max_period = riscv_pmu_ctr_get_width_mask(event); init_val = local64_read(&hwc->prev_count) & max_period; +#if defined(CONFIG_32BIT) + sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, idx, 1, + flag, init_val, init_val >> 32, 0); +#else sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, idx, 1, flag, init_val, 0, 0); +#endif + perf_event_update_userpage(event); } ctr_ovf_mask = ctr_ovf_mask >> 1; idx++; diff --git a/drivers/platform/mellanox/mlxbf-tmfifo.c b/drivers/platform/mellanox/mlxbf-tmfifo.c index 38800e86ed8a..8be13d416f48 100644 --- a/drivers/platform/mellanox/mlxbf-tmfifo.c +++ b/drivers/platform/mellanox/mlxbf-tmfifo.c @@ -928,6 +928,7 @@ static int mlxbf_tmfifo_virtio_find_vqs(struct virtio_device *vdev, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char * const names[], + u32 sizes[], const bool *ctx, struct irq_affinity *desc) { @@ -959,6 +960,8 @@ static int mlxbf_tmfifo_virtio_find_vqs(struct virtio_device *vdev, goto error; } + vq->num_max = vring->num; + vqs[i] = vq; vring->vq = vq; vq->priv = vring; diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 4b563db3ab3e..a8c46ba5878f 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -297,4 +297,10 @@ config NVMEM_REBOOT_MODE then the bootloader can read it and take different action according to the mode. +config POWER_MLXBF + tristate "Mellanox BlueField power handling driver" + depends on (GPIO_MLXBF2 && ACPI) + help + This driver supports reset or low power mode handling for Mellanox BlueField. + endif diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index f606a2f60539..0a39424fc558 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -35,3 +35,4 @@ obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o obj-$(CONFIG_POWER_RESET_SC27XX) += sc27xx-poweroff.o obj-$(CONFIG_NVMEM_REBOOT_MODE) += nvmem-reboot-mode.o +obj-$(CONFIG_POWER_MLXBF) += pwr-mlxbf.o diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index 64def79d557a..741e44a017c3 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -17,10 +17,13 @@ #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/reboot.h> +#include <linux/reset-controller.h> #include <soc/at91/at91sam9_ddrsdr.h> #include <soc/at91/at91sam9_sdramc.h> +#include <dt-bindings/reset/sama7g5-reset.h> + #define AT91_RSTC_CR 0x00 /* Reset Controller Control Register */ #define AT91_RSTC_PROCRST BIT(0) /* Processor Reset */ #define AT91_RSTC_PERRST BIT(2) /* Peripheral Reset */ @@ -39,6 +42,17 @@ #define AT91_RSTC_URSTIEN BIT(4) /* User Reset Interrupt Enable */ #define AT91_RSTC_ERSTL GENMASK(11, 8) /* External Reset Length */ +/** + * enum reset_type - reset types + * @RESET_TYPE_GENERAL: first power-up reset + * @RESET_TYPE_WAKEUP: return from backup mode + * @RESET_TYPE_WATCHDOG: watchdog fault + * @RESET_TYPE_SOFTWARE: processor reset required by software + * @RESET_TYPE_USER: NRST pin detected low + * @RESET_TYPE_CPU_FAIL: CPU clock failure detection + * @RESET_TYPE_XTAL_FAIL: 32KHz crystal failure dectection fault + * @RESET_TYPE_ULP2: ULP2 reset + */ enum reset_type { RESET_TYPE_GENERAL = 0, RESET_TYPE_WAKEUP = 1, @@ -50,15 +64,48 @@ enum reset_type { RESET_TYPE_ULP2 = 8, }; +/** + * struct at91_reset - AT91 reset specific data structure + * @rstc_base: base address for system reset + * @ramc_base: array with base addresses of RAM controllers + * @dev_base: base address for devices reset + * @sclk: slow clock + * @data: platform specific reset data + * @rcdev: reset controller device + * @lock: lock for devices reset register access + * @nb: reset notifier block + * @args: SoC specific system reset arguments + * @ramc_lpr: SDRAM Controller Low Power Register + */ struct at91_reset { void __iomem *rstc_base; void __iomem *ramc_base[2]; + void __iomem *dev_base; struct clk *sclk; + const struct at91_reset_data *data; + struct reset_controller_dev rcdev; + spinlock_t lock; struct notifier_block nb; u32 args; u32 ramc_lpr; }; +#define to_at91_reset(r) container_of(r, struct at91_reset, rcdev) + +/** + * struct at91_reset_data - AT91 reset data + * @reset_args: SoC specific system reset arguments + * @n_device_reset: number of device resets + * @device_reset_min_id: min id for device reset + * @device_reset_max_id: max id for device reset + */ +struct at91_reset_data { + u32 reset_args; + u32 n_device_reset; + u8 device_reset_min_id; + u8 device_reset_max_id; +}; + /* * unless the SDRAM is cleanly shutdown before we hit the * reset register it can be left driving the data bus and @@ -95,7 +142,7 @@ static int at91_reset(struct notifier_block *this, unsigned long mode, "r" (reset->rstc_base), "r" (1), "r" cpu_to_le32(AT91_DDRSDRC_LPCB_POWER_DOWN), - "r" (reset->args), + "r" (reset->data->reset_args), "r" (reset->ramc_lpr) : "r4"); @@ -153,34 +200,133 @@ static const struct of_device_id at91_ramc_of_match[] = { { /* sentinel */ } }; +static const struct at91_reset_data sam9260 = { + .reset_args = AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST, +}; + +static const struct at91_reset_data samx7 = { + .reset_args = AT91_RSTC_KEY | AT91_RSTC_PROCRST, +}; + +static const struct at91_reset_data sama7g5 = { + .reset_args = AT91_RSTC_KEY | AT91_RSTC_PROCRST, + .n_device_reset = 3, + .device_reset_min_id = SAMA7G5_RESET_USB_PHY1, + .device_reset_max_id = SAMA7G5_RESET_USB_PHY3, +}; + static const struct of_device_id at91_reset_of_match[] = { { .compatible = "atmel,at91sam9260-rstc", - .data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PERRST | - AT91_RSTC_PROCRST), + .data = &sam9260, }, { .compatible = "atmel,at91sam9g45-rstc", - .data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PERRST | - AT91_RSTC_PROCRST) + .data = &sam9260, }, { .compatible = "atmel,sama5d3-rstc", - .data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PERRST | - AT91_RSTC_PROCRST) + .data = &sam9260, }, { .compatible = "atmel,samx7-rstc", - .data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PROCRST) + .data = &samx7, }, { .compatible = "microchip,sam9x60-rstc", - .data = (void *)(AT91_RSTC_KEY | AT91_RSTC_PROCRST) + .data = &samx7, + }, + { + .compatible = "microchip,sama7g5-rstc", + .data = &sama7g5, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, at91_reset_of_match); +static int at91_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct at91_reset *reset = to_at91_reset(rcdev); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&reset->lock, flags); + val = readl_relaxed(reset->dev_base); + if (assert) + val |= BIT(id); + else + val &= ~BIT(id); + writel_relaxed(val, reset->dev_base); + spin_unlock_irqrestore(&reset->lock, flags); + + return 0; +} + +static int at91_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return at91_reset_update(rcdev, id, true); +} + +static int at91_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return at91_reset_update(rcdev, id, false); +} + +static int at91_reset_dev_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct at91_reset *reset = to_at91_reset(rcdev); + u32 val; + + val = readl_relaxed(reset->dev_base); + + return !!(val & BIT(id)); +} + +static const struct reset_control_ops at91_reset_ops = { + .assert = at91_reset_assert, + .deassert = at91_reset_deassert, + .status = at91_reset_dev_status, +}; + +static int at91_reset_of_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + struct at91_reset *reset = to_at91_reset(rcdev); + + if (!reset->data->n_device_reset || + (reset_spec->args[0] < reset->data->device_reset_min_id || + reset_spec->args[0] > reset->data->device_reset_max_id)) + return -EINVAL; + + return reset_spec->args[0]; +} + +static int at91_rcdev_init(struct at91_reset *reset, + struct platform_device *pdev) +{ + if (!reset->data->n_device_reset) + return 0; + + reset->dev_base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 1, + NULL); + if (IS_ERR(reset->dev_base)) + return -ENODEV; + + spin_lock_init(&reset->lock); + reset->rcdev.ops = &at91_reset_ops; + reset->rcdev.owner = THIS_MODULE; + reset->rcdev.of_node = pdev->dev.of_node; + reset->rcdev.nr_resets = reset->data->n_device_reset; + reset->rcdev.of_reset_n_cells = 1; + reset->rcdev.of_xlate = at91_reset_of_xlate; + + return devm_reset_controller_register(&pdev->dev, &reset->rcdev); +} + static int __init at91_reset_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -212,10 +358,12 @@ static int __init at91_reset_probe(struct platform_device *pdev) } } - match = of_match_node(at91_reset_of_match, pdev->dev.of_node); + reset->data = device_get_match_data(&pdev->dev); + if (!reset->data) + return -ENODEV; + reset->nb.notifier_call = at91_reset; reset->nb.priority = 192; - reset->args = (u32)match->data; reset->sclk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(reset->sclk)) @@ -229,6 +377,10 @@ static int __init at91_reset_probe(struct platform_device *pdev) platform_set_drvdata(pdev, reset); + ret = at91_rcdev_init(reset, pdev); + if (ret) + goto disable_clk; + if (of_device_is_compatible(pdev->dev.of_node, "microchip,sam9x60-rstc")) { u32 val = readl(reset->rstc_base + AT91_RSTC_MR); @@ -237,14 +389,16 @@ static int __init at91_reset_probe(struct platform_device *pdev) } ret = register_restart_handler(&reset->nb); - if (ret) { - clk_disable_unprepare(reset->sclk); - return ret; - } + if (ret) + goto disable_clk; at91_reset_status(pdev, reset->rstc_base); return 0; + +disable_clk: + clk_disable_unprepare(reset->sclk); + return ret; } static int __exit at91_reset_remove(struct platform_device *pdev) diff --git a/drivers/power/reset/pwr-mlxbf.c b/drivers/power/reset/pwr-mlxbf.c new file mode 100644 index 000000000000..12dedf841a44 --- /dev/null +++ b/drivers/power/reset/pwr-mlxbf.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-only or BSD-3-Clause + +/* + * Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. + */ + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/devm-helpers.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/reboot.h> +#include <linux/types.h> + +struct pwr_mlxbf { + struct work_struct send_work; + const char *hid; +}; + +static void pwr_mlxbf_send_work(struct work_struct *work) +{ + acpi_bus_generate_netlink_event("button/power.*", "Power Button", 0x80, 1); +} + +static irqreturn_t pwr_mlxbf_irq(int irq, void *ptr) +{ + const char *rst_pwr_hid = "MLNXBF24"; + const char *low_pwr_hid = "MLNXBF29"; + struct pwr_mlxbf *priv = ptr; + + if (!strncmp(priv->hid, rst_pwr_hid, 8)) + emergency_restart(); + + if (!strncmp(priv->hid, low_pwr_hid, 8)) + schedule_work(&priv->send_work); + + return IRQ_HANDLED; +} + +static int pwr_mlxbf_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct acpi_device *adev; + struct pwr_mlxbf *priv; + const char *hid; + int irq, err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + adev = ACPI_COMPANION(dev); + if (!adev) + return -ENXIO; + + hid = acpi_device_hid(adev); + priv->hid = hid; + + irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0); + if (irq < 0) + return dev_err_probe(dev, irq, "Error getting %s irq.\n", priv->hid); + + err = devm_work_autocancel(dev, &priv->send_work, pwr_mlxbf_send_work); + if (err) + return err; + + err = devm_request_irq(dev, irq, pwr_mlxbf_irq, 0, hid, priv); + if (err) + dev_err(dev, "Failed request of %s irq\n", priv->hid); + + return err; +} + +static const struct acpi_device_id __maybe_unused pwr_mlxbf_acpi_match[] = { + { "MLNXBF24", 0 }, + { "MLNXBF29", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, pwr_mlxbf_acpi_match); + +static struct platform_driver pwr_mlxbf_driver = { + .driver = { + .name = "pwr_mlxbf", + .acpi_match_table = pwr_mlxbf_acpi_match, + }, + .probe = pwr_mlxbf_probe, +}; + +module_platform_driver(pwr_mlxbf_driver); + +MODULE_DESCRIPTION("Mellanox BlueField power driver"); +MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/power/supply/ab8500-chargalg.h b/drivers/power/supply/ab8500-chargalg.h index f47a0061c36a..8534d067ba95 100644 --- a/drivers/power/supply/ab8500-chargalg.h +++ b/drivers/power/supply/ab8500-chargalg.h @@ -34,7 +34,6 @@ struct ux500_charger_ops { * @max_out_volt_uv maximum output charger voltage in uV * @max_out_curr_ua maximum output charger current in uA * @enabled indicates if this charger is used or not - * @external external charger unit (pm2xxx) */ struct ux500_charger { struct power_supply *psy; @@ -43,9 +42,6 @@ struct ux500_charger { int max_out_curr_ua; int wdt_refresh; bool enabled; - bool external; }; -extern struct blocking_notifier_head charger_notifier_list; - #endif /* _AB8500_CHARGALG_H_ */ diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index b7e842dff567..863fabe05bdc 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -697,7 +697,6 @@ static void ab8500_btemp_unbind(struct device *dev, struct device *master, /* Delete the work queue */ destroy_workqueue(di->btemp_wq); - flush_scheduled_work(); } static const struct component_ops ab8500_btemp_component_ops = { diff --git a/drivers/power/supply/ab8500_chargalg.c b/drivers/power/supply/ab8500_chargalg.c index 431bbc352d1b..ae4be553f424 100644 --- a/drivers/power/supply/ab8500_chargalg.c +++ b/drivers/power/supply/ab8500_chargalg.c @@ -246,9 +246,6 @@ struct ab8500_chargalg { struct kobject chargalg_kobject; }; -/*External charger prepare notifier*/ -BLOCKING_NOTIFIER_HEAD(charger_notifier_list); - /* Main battery properties */ static enum power_supply_property ab8500_chargalg_props[] = { POWER_SUPPLY_PROP_STATUS, @@ -343,8 +340,7 @@ static int ab8500_chargalg_check_charger_enable(struct ab8500_chargalg *di) return di->usb_chg->ops.check_enable(di->usb_chg, bi->constant_charge_voltage_max_uv, bi->constant_charge_current_max_ua); - } else if ((di->chg_info.charger_type & AC_CHG) && - !(di->ac_chg->external)) { + } else if (di->chg_info.charger_type & AC_CHG) { return di->ac_chg->ops.check_enable(di->ac_chg, bi->constant_charge_voltage_max_uv, bi->constant_charge_current_max_ua); @@ -473,15 +469,6 @@ static int ab8500_chargalg_kick_watchdog(struct ab8500_chargalg *di) /* Check if charger exists and kick watchdog if charging */ if (di->ac_chg && di->ac_chg->ops.kick_wd && di->chg_info.online_chg & AC_CHG) { - /* - * If AB charger watchdog expired, pm2xxx charging - * gets disabled. To be safe, kick both AB charger watchdog - * and pm2xxx watchdog. - */ - if (di->ac_chg->external && - di->usb_chg && di->usb_chg->ops.kick_wd) - di->usb_chg->ops.kick_wd(di->usb_chg); - return di->ac_chg->ops.kick_wd(di->ac_chg); } else if (di->usb_chg && di->usb_chg->ops.kick_wd && di->chg_info.online_chg & USB_CHG) @@ -517,14 +504,6 @@ static int ab8500_chargalg_ac_en(struct ab8500_chargalg *di, int enable, di->chg_info.ac_iset_ua = iset_ua; di->chg_info.ac_vset_uv = vset_uv; - /* Enable external charger */ - if (enable && di->ac_chg->external && - !ab8500_chargalg_ex_ac_enable_toggle) { - blocking_notifier_call_chain(&charger_notifier_list, - 0, di->dev); - ab8500_chargalg_ex_ac_enable_toggle++; - } - return di->ac_chg->ops.enable(di->ac_chg, enable, vset_uv, iset_ua); } @@ -1217,6 +1196,34 @@ static void ab8500_chargalg_external_power_changed(struct power_supply *psy) } /** + * ab8500_chargalg_time_to_restart() - time to restart CC/CV charging? + * @di: charging algorithm state + * + * This checks if the voltage or capacity of the battery has fallen so + * low that we need to restart the CC/CV charge cycle. + */ +static bool ab8500_chargalg_time_to_restart(struct ab8500_chargalg *di) +{ + struct power_supply_battery_info *bi = di->bm->bi; + + /* Sanity check - these need to have some reasonable values */ + if (!di->batt_data.volt_uv || !di->batt_data.percent) + return false; + + /* Some batteries tell us at which voltage we should restart charging */ + if (bi->charge_restart_voltage_uv > 0) { + if (di->batt_data.volt_uv <= bi->charge_restart_voltage_uv) + return true; + /* Else we restart as we reach a certain capacity */ + } else { + if (di->batt_data.percent <= AB8500_RECHARGE_CAP) + return true; + } + + return false; +} + +/** * ab8500_chargalg_algorithm() - Main function for the algorithm * @di: pointer to the ab8500_chargalg structure * @@ -1459,7 +1466,7 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di) fallthrough; case STATE_WAIT_FOR_RECHARGE: - if (di->batt_data.percent <= AB8500_RECHARGE_CAP) + if (ab8500_chargalg_time_to_restart(di)) ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); break; @@ -1486,6 +1493,14 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di) ab8500_chargalg_stop_maintenance_timer(di); ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT); } + /* + * This happens if the voltage drops too quickly during + * maintenance charging, especially in older batteries. + */ + if (ab8500_chargalg_time_to_restart(di)) { + ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); + dev_info(di->dev, "restarted charging from maintenance state A - battery getting old?\n"); + } break; case STATE_MAINTENANCE_B_INIT: @@ -1510,6 +1525,14 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di) ab8500_chargalg_stop_maintenance_timer(di); ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); } + /* + * This happens if the voltage drops too quickly during + * maintenance charging, especially in older batteries. + */ + if (ab8500_chargalg_time_to_restart(di)) { + ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); + dev_info(di->dev, "restarted charging from maintenance state B - battery getting old?\n"); + } break; case STATE_TEMP_LOWHIGH_INIT: @@ -1746,7 +1769,6 @@ static void ab8500_chargalg_unbind(struct device *dev, struct device *master, /* Delete the work queue */ destroy_workqueue(di->chargalg_wq); - flush_scheduled_work(); } static const struct component_ops ab8500_chargalg_component_ops = { diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index d04d087caa50..c19c50442761 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -1716,29 +1716,6 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger, return ret; } -static int ab8500_external_charger_prepare(struct notifier_block *charger_nb, - unsigned long event, void *data) -{ - int ret; - struct device *dev = data; - /*Toggle External charger control pin*/ - ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK, - AB8500_SYS_CHARGER_CONTROL_REG, - EXTERNAL_CHARGER_DISABLE_REG_VAL); - if (ret < 0) { - dev_err(dev, "write reg failed %d\n", ret); - goto out; - } - ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK, - AB8500_SYS_CHARGER_CONTROL_REG, - EXTERNAL_CHARGER_ENABLE_REG_VAL); - if (ret < 0) - dev_err(dev, "Write reg failed %d\n", ret); - -out: - return ret; -} - /** * ab8500_charger_usb_check_enable() - enable usb charging * @charger: pointer to the ux500_charger structure @@ -3316,10 +3293,6 @@ static int __maybe_unused ab8500_charger_suspend(struct device *dev) return 0; } -static struct notifier_block charger_nb = { - .notifier_call = ab8500_external_charger_prepare, -}; - static char *supply_interface[] = { "ab8500_chargalg", "ab8500_fg", @@ -3378,6 +3351,7 @@ static int ab8500_charger_bind(struct device *dev) ret = component_bind_all(dev, di); if (ret) { dev_err(dev, "can't bind component devices\n"); + destroy_workqueue(di->charger_wq); return ret; } @@ -3404,8 +3378,6 @@ static void ab8500_charger_unbind(struct device *dev) /* Delete the work queue */ destroy_workqueue(di->charger_wq); - flush_scheduled_work(); - /* Unbind fg, btemp, algorithm */ component_unbind_all(dev, di); } @@ -3540,7 +3512,6 @@ static int ab8500_charger_probe(struct platform_device *pdev) */ if (!is_ab8505(di->parent)) di->ac_chg.enabled = true; - di->ac_chg.external = false; /* USB supply */ /* ux500_charger sub-class */ @@ -3553,7 +3524,6 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->usb_chg.max_out_curr_ua = ab8500_charge_output_curr_map[ARRAY_SIZE(ab8500_charge_output_curr_map) - 1]; di->usb_chg.wdt_refresh = CHG_WD_INTERVAL; - di->usb_chg.external = false; di->usb_state.usb_current_ua = -1; mutex_init(&di->charger_attached_mutex); @@ -3677,17 +3647,11 @@ static int ab8500_charger_probe(struct platform_device *pdev) goto remove_ab8500_bm; } - /* Notifier for external charger enabling */ - if (!di->ac_chg.enabled) - blocking_notifier_chain_register( - &charger_notifier_list, &charger_nb); - - di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); if (IS_ERR_OR_NULL(di->usb_phy)) { dev_err(dev, "failed to get usb transceiver\n"); ret = -EINVAL; - goto out_charger_notifier; + goto remove_ab8500_bm; } di->nb.notifier_call = ab8500_charger_usb_notifier_call; ret = usb_register_notifier(di->usb_phy, &di->nb); @@ -3696,7 +3660,6 @@ static int ab8500_charger_probe(struct platform_device *pdev) goto put_usb_phy; } - ret = component_master_add_with_match(&pdev->dev, &ab8500_charger_comp_ops, match); @@ -3711,10 +3674,6 @@ free_notifier: usb_unregister_notifier(di->usb_phy, &di->nb); put_usb_phy: usb_put_phy(di->usb_phy); -out_charger_notifier: - if (!di->ac_chg.enabled) - blocking_notifier_chain_unregister( - &charger_notifier_list, &charger_nb); remove_ab8500_bm: ab8500_bm_of_remove(di->usb_chg.psy, di->bm); return ret; @@ -3729,9 +3688,6 @@ static int ab8500_charger_remove(struct platform_device *pdev) usb_unregister_notifier(di->usb_phy, &di->nb); ab8500_bm_of_remove(di->usb_chg.psy, di->bm); usb_put_phy(di->usb_phy); - if (!di->ac_chg.enabled) - blocking_notifier_chain_unregister( - &charger_notifier_list, &charger_nb); return 0; } diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index 4339fa9ff009..c6c9804280db 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -412,7 +412,7 @@ static int ab8500_fg_add_cap_sample(struct ab8500_fg *di, int sample) * ab8500_fg_clear_cap_samples() - Clear average filter * @di: pointer to the ab8500_fg structure * - * The capacity filter is is reset to zero. + * The capacity filter is reset to zero. */ static void ab8500_fg_clear_cap_samples(struct ab8500_fg *di) { @@ -3234,7 +3234,6 @@ static int ab8500_fg_remove(struct platform_device *pdev) struct ab8500_fg *di = platform_get_drvdata(pdev); destroy_workqueue(di->fg_wq); - flush_scheduled_work(); component_del(&pdev->dev, &ab8500_fg_component_ops); list_del(&di->node); ab8500_fg_sysfs_exit(di); diff --git a/drivers/power/supply/bq24257_charger.c b/drivers/power/supply/bq24257_charger.c index 96cb3290bcaa..ecba9ab86faf 100644 --- a/drivers/power/supply/bq24257_charger.c +++ b/drivers/power/supply/bq24257_charger.c @@ -287,7 +287,7 @@ static int bq24257_set_input_current_limit(struct bq24257_device *bq, { /* * Address the case where the user manually sets an input current limit - * while the charger auto-detection mechanism is is active. In this + * while the charger auto-detection mechanism is active. In this * case we want to abort and go straight to the user-specified value. */ if (bq->iilimit_autoset_enable) diff --git a/drivers/power/supply/cros_peripheral_charger.c b/drivers/power/supply/cros_peripheral_charger.c index 9fe6d826148d..1379afd9698d 100644 --- a/drivers/power/supply/cros_peripheral_charger.c +++ b/drivers/power/supply/cros_peripheral_charger.c @@ -63,7 +63,7 @@ static int cros_pchg_ec_command(const struct charger_data *charger, struct cros_ec_command *msg; int ret; - msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL); + msg = kzalloc(struct_size(msg, data, max(outsize, insize)), GFP_KERNEL); if (!msg) return -ENOMEM; diff --git a/drivers/power/supply/goldfish_battery.c b/drivers/power/supply/goldfish_battery.c index bf1754355c9f..a58d713d75ce 100644 --- a/drivers/power/supply/goldfish_battery.c +++ b/drivers/power/supply/goldfish_battery.c @@ -221,10 +221,8 @@ static int goldfish_battery_probe(struct platform_device *pdev) } data->irq = platform_get_irq(pdev, 0); - if (data->irq < 0) { - dev_err(&pdev->dev, "platform_get_irq failed\n"); + if (data->irq < 0) return -ENODEV; - } ret = devm_request_irq(&pdev->dev, data->irq, goldfish_battery_interrupt, diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c index 397e5a03b7d9..56c57529c228 100644 --- a/drivers/power/supply/lp8788-charger.c +++ b/drivers/power/supply/lp8788-charger.c @@ -376,7 +376,7 @@ static int lp8788_update_charger_params(struct platform_device *pdev, return 0; } - /* settting charging parameters */ + /* setting charging parameters */ for (i = 0; i < pdata->num_chg_params; i++) { param = pdata->chg_params + i; diff --git a/drivers/power/supply/max77976_charger.c b/drivers/power/supply/max77976_charger.c index 8b6c8cfa7503..4fed74511931 100644 --- a/drivers/power/supply/max77976_charger.c +++ b/drivers/power/supply/max77976_charger.c @@ -3,7 +3,7 @@ * max77976_charger.c - Driver for the Maxim MAX77976 battery charger * * Copyright (C) 2021 Luca Ceresoli - * Author: Luca Ceresoli <luca@lucaceresoli.net> + * Author: Luca Ceresoli <luca.ceresoli@bootlin.com> */ #include <linux/i2c.h> @@ -504,6 +504,6 @@ static struct i2c_driver max77976_driver = { }; module_i2c_driver(max77976_driver); -MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>"); +MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>"); MODULE_DESCRIPTION("Maxim MAX77976 charger driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/supply/olpc_battery.c b/drivers/power/supply/olpc_battery.c index e0476ec06601..a5da20ffd685 100644 --- a/drivers/power/supply/olpc_battery.c +++ b/drivers/power/supply/olpc_battery.c @@ -635,6 +635,7 @@ static int olpc_battery_probe(struct platform_device *pdev) struct power_supply_config bat_psy_cfg = {}; struct power_supply_config ac_psy_cfg = {}; struct olpc_battery_data *data; + struct device_node *np; uint8_t status; uint8_t ecver; int ret; @@ -649,7 +650,9 @@ static int olpc_battery_probe(struct platform_device *pdev) if (ret) return ret; - if (of_find_compatible_node(NULL, NULL, "olpc,xo1.75-ec")) { + np = of_find_compatible_node(NULL, NULL, "olpc,xo1.75-ec"); + if (np) { + of_node_put(np); /* XO 1.75 */ data->new_proto = true; data->little_endian = true; diff --git a/drivers/power/supply/pm2301_charger.h b/drivers/power/supply/pm2301_charger.h deleted file mode 100644 index 74397e377982..000000000000 --- a/drivers/power/supply/pm2301_charger.h +++ /dev/null @@ -1,492 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson SA 2012 - * - * PM2301 power supply interface - */ - -#ifndef PM2301_CHARGER_H -#define PM2301_CHARGER_H - -/* Watchdog timeout constant */ -#define WD_TIMER 0x30 /* 4min */ -#define WD_KICK_INTERVAL (30 * HZ) - -#define PM2XXX_NUM_INT_REG 0x6 - -/* Constant voltage/current */ -#define PM2XXX_CONST_CURR 0x0 -#define PM2XXX_CONST_VOLT 0x1 - -/* Lowest charger voltage is 3.39V -> 0x4E */ -#define LOW_VOLT_REG 0x4E - -#define PM2XXX_BATT_CTRL_REG1 0x00 -#define PM2XXX_BATT_CTRL_REG2 0x01 -#define PM2XXX_BATT_CTRL_REG3 0x02 -#define PM2XXX_BATT_CTRL_REG4 0x03 -#define PM2XXX_BATT_CTRL_REG5 0x04 -#define PM2XXX_BATT_CTRL_REG6 0x05 -#define PM2XXX_BATT_CTRL_REG7 0x06 -#define PM2XXX_BATT_CTRL_REG8 0x07 -#define PM2XXX_NTC_CTRL_REG1 0x08 -#define PM2XXX_NTC_CTRL_REG2 0x09 -#define PM2XXX_BATT_CTRL_REG9 0x0A -#define PM2XXX_BATT_STAT_REG1 0x0B -#define PM2XXX_INP_VOLT_VPWR2 0x11 -#define PM2XXX_INP_DROP_VPWR2 0x13 -#define PM2XXX_INP_VOLT_VPWR1 0x15 -#define PM2XXX_INP_DROP_VPWR1 0x17 -#define PM2XXX_INP_MODE_VPWR 0x18 -#define PM2XXX_BATT_WD_KICK 0x70 -#define PM2XXX_DEV_VER_STAT 0x0C -#define PM2XXX_THERM_WARN_CTRL_REG 0x20 -#define PM2XXX_BATT_DISC_REG 0x21 -#define PM2XXX_BATT_LOW_LEV_COMP_REG 0x22 -#define PM2XXX_BATT_LOW_LEV_VAL_REG 0x23 -#define PM2XXX_I2C_PAD_CTRL_REG 0x24 -#define PM2XXX_SW_CTRL_REG 0x26 -#define PM2XXX_LED_CTRL_REG 0x28 - -#define PM2XXX_REG_INT1 0x40 -#define PM2XXX_MASK_REG_INT1 0x50 -#define PM2XXX_SRCE_REG_INT1 0x60 -#define PM2XXX_REG_INT2 0x41 -#define PM2XXX_MASK_REG_INT2 0x51 -#define PM2XXX_SRCE_REG_INT2 0x61 -#define PM2XXX_REG_INT3 0x42 -#define PM2XXX_MASK_REG_INT3 0x52 -#define PM2XXX_SRCE_REG_INT3 0x62 -#define PM2XXX_REG_INT4 0x43 -#define PM2XXX_MASK_REG_INT4 0x53 -#define PM2XXX_SRCE_REG_INT4 0x63 -#define PM2XXX_REG_INT5 0x44 -#define PM2XXX_MASK_REG_INT5 0x54 -#define PM2XXX_SRCE_REG_INT5 0x64 -#define PM2XXX_REG_INT6 0x45 -#define PM2XXX_MASK_REG_INT6 0x55 -#define PM2XXX_SRCE_REG_INT6 0x65 - -#define VPWR_OVV 0x0 -#define VSYSTEM_OVV 0x1 - -/* control Reg 1 */ -#define PM2XXX_CH_RESUME_EN 0x1 -#define PM2XXX_CH_RESUME_DIS 0x0 - -/* control Reg 2 */ -#define PM2XXX_CH_AUTO_RESUME_EN 0X2 -#define PM2XXX_CH_AUTO_RESUME_DIS 0X0 -#define PM2XXX_CHARGER_ENA 0x4 -#define PM2XXX_CHARGER_DIS 0x0 - -/* control Reg 3 */ -#define PM2XXX_CH_WD_CC_PHASE_OFF 0x0 -#define PM2XXX_CH_WD_CC_PHASE_5MIN 0x1 -#define PM2XXX_CH_WD_CC_PHASE_10MIN 0x2 -#define PM2XXX_CH_WD_CC_PHASE_30MIN 0x3 -#define PM2XXX_CH_WD_CC_PHASE_60MIN 0x4 -#define PM2XXX_CH_WD_CC_PHASE_120MIN 0x5 -#define PM2XXX_CH_WD_CC_PHASE_240MIN 0x6 -#define PM2XXX_CH_WD_CC_PHASE_360MIN 0x7 - -#define PM2XXX_CH_WD_CV_PHASE_OFF (0x0<<3) -#define PM2XXX_CH_WD_CV_PHASE_5MIN (0x1<<3) -#define PM2XXX_CH_WD_CV_PHASE_10MIN (0x2<<3) -#define PM2XXX_CH_WD_CV_PHASE_30MIN (0x3<<3) -#define PM2XXX_CH_WD_CV_PHASE_60MIN (0x4<<3) -#define PM2XXX_CH_WD_CV_PHASE_120MIN (0x5<<3) -#define PM2XXX_CH_WD_CV_PHASE_240MIN (0x6<<3) -#define PM2XXX_CH_WD_CV_PHASE_360MIN (0x7<<3) - -/* control Reg 4 */ -#define PM2XXX_CH_WD_PRECH_PHASE_OFF 0x0 -#define PM2XXX_CH_WD_PRECH_PHASE_1MIN 0x1 -#define PM2XXX_CH_WD_PRECH_PHASE_5MIN 0x2 -#define PM2XXX_CH_WD_PRECH_PHASE_10MIN 0x3 -#define PM2XXX_CH_WD_PRECH_PHASE_30MIN 0x4 -#define PM2XXX_CH_WD_PRECH_PHASE_60MIN 0x5 -#define PM2XXX_CH_WD_PRECH_PHASE_120MIN 0x6 -#define PM2XXX_CH_WD_PRECH_PHASE_240MIN 0x7 - -/* control Reg 5 */ -#define PM2XXX_CH_WD_AUTO_TIMEOUT_NONE 0x0 -#define PM2XXX_CH_WD_AUTO_TIMEOUT_20MIN 0x1 - -/* control Reg 6 */ -#define PM2XXX_DIR_CH_CC_CURRENT_MASK 0x0F -#define PM2XXX_DIR_CH_CC_CURRENT_200MA 0x0 -#define PM2XXX_DIR_CH_CC_CURRENT_400MA 0x2 -#define PM2XXX_DIR_CH_CC_CURRENT_600MA 0x3 -#define PM2XXX_DIR_CH_CC_CURRENT_800MA 0x4 -#define PM2XXX_DIR_CH_CC_CURRENT_1000MA 0x5 -#define PM2XXX_DIR_CH_CC_CURRENT_1200MA 0x6 -#define PM2XXX_DIR_CH_CC_CURRENT_1400MA 0x7 -#define PM2XXX_DIR_CH_CC_CURRENT_1600MA 0x8 -#define PM2XXX_DIR_CH_CC_CURRENT_1800MA 0x9 -#define PM2XXX_DIR_CH_CC_CURRENT_2000MA 0xA -#define PM2XXX_DIR_CH_CC_CURRENT_2200MA 0xB -#define PM2XXX_DIR_CH_CC_CURRENT_2400MA 0xC -#define PM2XXX_DIR_CH_CC_CURRENT_2600MA 0xD -#define PM2XXX_DIR_CH_CC_CURRENT_2800MA 0xE -#define PM2XXX_DIR_CH_CC_CURRENT_3000MA 0xF - -#define PM2XXX_CH_PRECH_CURRENT_MASK 0x30 -#define PM2XXX_CH_PRECH_CURRENT_25MA (0x0<<4) -#define PM2XXX_CH_PRECH_CURRENT_50MA (0x1<<4) -#define PM2XXX_CH_PRECH_CURRENT_75MA (0x2<<4) -#define PM2XXX_CH_PRECH_CURRENT_100MA (0x3<<4) - -#define PM2XXX_CH_EOC_CURRENT_MASK 0xC0 -#define PM2XXX_CH_EOC_CURRENT_100MA (0x0<<6) -#define PM2XXX_CH_EOC_CURRENT_150MA (0x1<<6) -#define PM2XXX_CH_EOC_CURRENT_300MA (0x2<<6) -#define PM2XXX_CH_EOC_CURRENT_400MA (0x3<<6) - -/* control Reg 7 */ -#define PM2XXX_CH_PRECH_VOL_2_5 0x0 -#define PM2XXX_CH_PRECH_VOL_2_7 0x1 -#define PM2XXX_CH_PRECH_VOL_2_9 0x2 -#define PM2XXX_CH_PRECH_VOL_3_1 0x3 - -#define PM2XXX_CH_VRESUME_VOL_3_2 (0x0<<2) -#define PM2XXX_CH_VRESUME_VOL_3_4 (0x1<<2) -#define PM2XXX_CH_VRESUME_VOL_3_6 (0x2<<2) -#define PM2XXX_CH_VRESUME_VOL_3_8 (0x3<<2) - -/* control Reg 8 */ -#define PM2XXX_CH_VOLT_MASK 0x3F -#define PM2XXX_CH_VOLT_3_5 0x0 -#define PM2XXX_CH_VOLT_3_5225 0x1 -#define PM2XXX_CH_VOLT_3_6 0x4 -#define PM2XXX_CH_VOLT_3_7 0x8 -#define PM2XXX_CH_VOLT_4_0 0x14 -#define PM2XXX_CH_VOLT_4_175 0x1B -#define PM2XXX_CH_VOLT_4_2 0x1C -#define PM2XXX_CH_VOLT_4_275 0x1F -#define PM2XXX_CH_VOLT_4_3 0x20 - -/*NTC control register 1*/ -#define PM2XXX_BTEMP_HIGH_TH_45 0x0 -#define PM2XXX_BTEMP_HIGH_TH_50 0x1 -#define PM2XXX_BTEMP_HIGH_TH_55 0x2 -#define PM2XXX_BTEMP_HIGH_TH_60 0x3 -#define PM2XXX_BTEMP_HIGH_TH_65 0x4 - -#define PM2XXX_BTEMP_LOW_TH_N5 (0x0<<3) -#define PM2XXX_BTEMP_LOW_TH_0 (0x1<<3) -#define PM2XXX_BTEMP_LOW_TH_5 (0x2<<3) -#define PM2XXX_BTEMP_LOW_TH_10 (0x3<<3) - -/*NTC control register 2*/ -#define PM2XXX_NTC_BETA_COEFF_3477 0x0 -#define PM2XXX_NTC_BETA_COEFF_3964 0x1 - -#define PM2XXX_NTC_RES_10K (0x0<<2) -#define PM2XXX_NTC_RES_47K (0x1<<2) -#define PM2XXX_NTC_RES_100K (0x2<<2) -#define PM2XXX_NTC_RES_NO_NTC (0x3<<2) - -/* control Reg 9 */ -#define PM2XXX_CH_CC_MODEDROP_EN 1 -#define PM2XXX_CH_CC_MODEDROP_DIS 0 - -#define PM2XXX_CH_CC_REDUCED_CURRENT_100MA (0x0<<1) -#define PM2XXX_CH_CC_REDUCED_CURRENT_200MA (0x1<<1) -#define PM2XXX_CH_CC_REDUCED_CURRENT_400MA (0x2<<1) -#define PM2XXX_CH_CC_REDUCED_CURRENT_IDENT (0x3<<1) - -#define PM2XXX_CHARCHING_INFO_DIS (0<<3) -#define PM2XXX_CHARCHING_INFO_EN (1<<3) - -#define PM2XXX_CH_150MV_DROP_300MV (0<<4) -#define PM2XXX_CH_150MV_DROP_150MV (1<<4) - - -/* charger status register */ -#define PM2XXX_CHG_STATUS_OFF 0x0 -#define PM2XXX_CHG_STATUS_ON 0x1 -#define PM2XXX_CHG_STATUS_FULL 0x2 -#define PM2XXX_CHG_STATUS_ERR 0x3 -#define PM2XXX_CHG_STATUS_WAIT 0x4 -#define PM2XXX_CHG_STATUS_NOBAT 0x5 - -/* Input charger voltage VPWR2 */ -#define PM2XXX_VPWR2_OVV_6_0 0x0 -#define PM2XXX_VPWR2_OVV_6_3 0x1 -#define PM2XXX_VPWR2_OVV_10 0x2 -#define PM2XXX_VPWR2_OVV_NONE 0x3 - -/* Input charger drop VPWR2 */ -#define PM2XXX_VPWR2_HW_OPT_EN (0x1<<4) -#define PM2XXX_VPWR2_HW_OPT_DIS (0x0<<4) - -#define PM2XXX_VPWR2_VALID_EN (0x1<<3) -#define PM2XXX_VPWR2_VALID_DIS (0x0<<3) - -#define PM2XXX_VPWR2_DROP_EN (0x1<<2) -#define PM2XXX_VPWR2_DROP_DIS (0x0<<2) - -/* Input charger voltage VPWR1 */ -#define PM2XXX_VPWR1_OVV_6_0 0x0 -#define PM2XXX_VPWR1_OVV_6_3 0x1 -#define PM2XXX_VPWR1_OVV_10 0x2 -#define PM2XXX_VPWR1_OVV_NONE 0x3 - -/* Input charger drop VPWR1 */ -#define PM2XXX_VPWR1_HW_OPT_EN (0x1<<4) -#define PM2XXX_VPWR1_HW_OPT_DIS (0x0<<4) - -#define PM2XXX_VPWR1_VALID_EN (0x1<<3) -#define PM2XXX_VPWR1_VALID_DIS (0x0<<3) - -#define PM2XXX_VPWR1_DROP_EN (0x1<<2) -#define PM2XXX_VPWR1_DROP_DIS (0x0<<2) - -/* Battery low level comparator control register */ -#define PM2XXX_VBAT_LOW_MONITORING_DIS 0x0 -#define PM2XXX_VBAT_LOW_MONITORING_ENA 0x1 - -/* Battery low level value control register */ -#define PM2XXX_VBAT_LOW_LEVEL_2_3 0x0 -#define PM2XXX_VBAT_LOW_LEVEL_2_4 0x1 -#define PM2XXX_VBAT_LOW_LEVEL_2_5 0x2 -#define PM2XXX_VBAT_LOW_LEVEL_2_6 0x3 -#define PM2XXX_VBAT_LOW_LEVEL_2_7 0x4 -#define PM2XXX_VBAT_LOW_LEVEL_2_8 0x5 -#define PM2XXX_VBAT_LOW_LEVEL_2_9 0x6 -#define PM2XXX_VBAT_LOW_LEVEL_3_0 0x7 -#define PM2XXX_VBAT_LOW_LEVEL_3_1 0x8 -#define PM2XXX_VBAT_LOW_LEVEL_3_2 0x9 -#define PM2XXX_VBAT_LOW_LEVEL_3_3 0xA -#define PM2XXX_VBAT_LOW_LEVEL_3_4 0xB -#define PM2XXX_VBAT_LOW_LEVEL_3_5 0xC -#define PM2XXX_VBAT_LOW_LEVEL_3_6 0xD -#define PM2XXX_VBAT_LOW_LEVEL_3_7 0xE -#define PM2XXX_VBAT_LOW_LEVEL_3_8 0xF -#define PM2XXX_VBAT_LOW_LEVEL_3_9 0x10 -#define PM2XXX_VBAT_LOW_LEVEL_4_0 0x11 -#define PM2XXX_VBAT_LOW_LEVEL_4_1 0x12 -#define PM2XXX_VBAT_LOW_LEVEL_4_2 0x13 - -/* SW CTRL */ -#define PM2XXX_SWCTRL_HW 0x0 -#define PM2XXX_SWCTRL_SW 0x1 - - -/* LED Driver Control */ -#define PM2XXX_LED_CURRENT_MASK 0x0C -#define PM2XXX_LED_CURRENT_2_5MA (0X0<<2) -#define PM2XXX_LED_CURRENT_1MA (0X1<<2) -#define PM2XXX_LED_CURRENT_5MA (0X2<<2) -#define PM2XXX_LED_CURRENT_10MA (0X3<<2) - -#define PM2XXX_LED_SELECT_MASK 0x02 -#define PM2XXX_LED_SELECT_EN (0X0<<1) -#define PM2XXX_LED_SELECT_DIS (0X1<<1) - -#define PM2XXX_ANTI_OVERSHOOT_MASK 0x01 -#define PM2XXX_ANTI_OVERSHOOT_DIS 0X0 -#define PM2XXX_ANTI_OVERSHOOT_EN 0X1 - -enum pm2xxx_reg_int1 { - PM2XXX_INT1_ITVBATDISCONNECT = 0x02, - PM2XXX_INT1_ITVBATLOWR = 0x04, - PM2XXX_INT1_ITVBATLOWF = 0x08, -}; - -enum pm2xxx_mask_reg_int1 { - PM2XXX_INT1_M_ITVBATDISCONNECT = 0x02, - PM2XXX_INT1_M_ITVBATLOWR = 0x04, - PM2XXX_INT1_M_ITVBATLOWF = 0x08, -}; - -enum pm2xxx_source_reg_int1 { - PM2XXX_INT1_S_ITVBATDISCONNECT = 0x02, - PM2XXX_INT1_S_ITVBATLOWR = 0x04, - PM2XXX_INT1_S_ITVBATLOWF = 0x08, -}; - -enum pm2xxx_reg_int2 { - PM2XXX_INT2_ITVPWR2PLUG = 0x01, - PM2XXX_INT2_ITVPWR2UNPLUG = 0x02, - PM2XXX_INT2_ITVPWR1PLUG = 0x04, - PM2XXX_INT2_ITVPWR1UNPLUG = 0x08, -}; - -enum pm2xxx_mask_reg_int2 { - PM2XXX_INT2_M_ITVPWR2PLUG = 0x01, - PM2XXX_INT2_M_ITVPWR2UNPLUG = 0x02, - PM2XXX_INT2_M_ITVPWR1PLUG = 0x04, - PM2XXX_INT2_M_ITVPWR1UNPLUG = 0x08, -}; - -enum pm2xxx_source_reg_int2 { - PM2XXX_INT2_S_ITVPWR2PLUG = 0x03, - PM2XXX_INT2_S_ITVPWR1PLUG = 0x0c, -}; - -enum pm2xxx_reg_int3 { - PM2XXX_INT3_ITCHPRECHARGEWD = 0x01, - PM2XXX_INT3_ITCHCCWD = 0x02, - PM2XXX_INT3_ITCHCVWD = 0x04, - PM2XXX_INT3_ITAUTOTIMEOUTWD = 0x08, -}; - -enum pm2xxx_mask_reg_int3 { - PM2XXX_INT3_M_ITCHPRECHARGEWD = 0x01, - PM2XXX_INT3_M_ITCHCCWD = 0x02, - PM2XXX_INT3_M_ITCHCVWD = 0x04, - PM2XXX_INT3_M_ITAUTOTIMEOUTWD = 0x08, -}; - -enum pm2xxx_source_reg_int3 { - PM2XXX_INT3_S_ITCHPRECHARGEWD = 0x01, - PM2XXX_INT3_S_ITCHCCWD = 0x02, - PM2XXX_INT3_S_ITCHCVWD = 0x04, - PM2XXX_INT3_S_ITAUTOTIMEOUTWD = 0x08, -}; - -enum pm2xxx_reg_int4 { - PM2XXX_INT4_ITBATTEMPCOLD = 0x01, - PM2XXX_INT4_ITBATTEMPHOT = 0x02, - PM2XXX_INT4_ITVPWR2OVV = 0x04, - PM2XXX_INT4_ITVPWR1OVV = 0x08, - PM2XXX_INT4_ITCHARGINGON = 0x10, - PM2XXX_INT4_ITVRESUME = 0x20, - PM2XXX_INT4_ITBATTFULL = 0x40, - PM2XXX_INT4_ITCVPHASE = 0x80, -}; - -enum pm2xxx_mask_reg_int4 { - PM2XXX_INT4_M_ITBATTEMPCOLD = 0x01, - PM2XXX_INT4_M_ITBATTEMPHOT = 0x02, - PM2XXX_INT4_M_ITVPWR2OVV = 0x04, - PM2XXX_INT4_M_ITVPWR1OVV = 0x08, - PM2XXX_INT4_M_ITCHARGINGON = 0x10, - PM2XXX_INT4_M_ITVRESUME = 0x20, - PM2XXX_INT4_M_ITBATTFULL = 0x40, - PM2XXX_INT4_M_ITCVPHASE = 0x80, -}; - -enum pm2xxx_source_reg_int4 { - PM2XXX_INT4_S_ITBATTEMPCOLD = 0x01, - PM2XXX_INT4_S_ITBATTEMPHOT = 0x02, - PM2XXX_INT4_S_ITVPWR2OVV = 0x04, - PM2XXX_INT4_S_ITVPWR1OVV = 0x08, - PM2XXX_INT4_S_ITCHARGINGON = 0x10, - PM2XXX_INT4_S_ITVRESUME = 0x20, - PM2XXX_INT4_S_ITBATTFULL = 0x40, - PM2XXX_INT4_S_ITCVPHASE = 0x80, -}; - -enum pm2xxx_reg_int5 { - PM2XXX_INT5_ITTHERMALSHUTDOWNRISE = 0x01, - PM2XXX_INT5_ITTHERMALSHUTDOWNFALL = 0x02, - PM2XXX_INT5_ITTHERMALWARNINGRISE = 0x04, - PM2XXX_INT5_ITTHERMALWARNINGFALL = 0x08, - PM2XXX_INT5_ITVSYSTEMOVV = 0x10, -}; - -enum pm2xxx_mask_reg_int5 { - PM2XXX_INT5_M_ITTHERMALSHUTDOWNRISE = 0x01, - PM2XXX_INT5_M_ITTHERMALSHUTDOWNFALL = 0x02, - PM2XXX_INT5_M_ITTHERMALWARNINGRISE = 0x04, - PM2XXX_INT5_M_ITTHERMALWARNINGFALL = 0x08, - PM2XXX_INT5_M_ITVSYSTEMOVV = 0x10, -}; - -enum pm2xxx_source_reg_int5 { - PM2XXX_INT5_S_ITTHERMALSHUTDOWNRISE = 0x01, - PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL = 0x02, - PM2XXX_INT5_S_ITTHERMALWARNINGRISE = 0x04, - PM2XXX_INT5_S_ITTHERMALWARNINGFALL = 0x08, - PM2XXX_INT5_S_ITVSYSTEMOVV = 0x10, -}; - -enum pm2xxx_reg_int6 { - PM2XXX_INT6_ITVPWR2DROP = 0x01, - PM2XXX_INT6_ITVPWR1DROP = 0x02, - PM2XXX_INT6_ITVPWR2VALIDRISE = 0x04, - PM2XXX_INT6_ITVPWR2VALIDFALL = 0x08, - PM2XXX_INT6_ITVPWR1VALIDRISE = 0x10, - PM2XXX_INT6_ITVPWR1VALIDFALL = 0x20, -}; - -enum pm2xxx_mask_reg_int6 { - PM2XXX_INT6_M_ITVPWR2DROP = 0x01, - PM2XXX_INT6_M_ITVPWR1DROP = 0x02, - PM2XXX_INT6_M_ITVPWR2VALIDRISE = 0x04, - PM2XXX_INT6_M_ITVPWR2VALIDFALL = 0x08, - PM2XXX_INT6_M_ITVPWR1VALIDRISE = 0x10, - PM2XXX_INT6_M_ITVPWR1VALIDFALL = 0x20, -}; - -enum pm2xxx_source_reg_int6 { - PM2XXX_INT6_S_ITVPWR2DROP = 0x01, - PM2XXX_INT6_S_ITVPWR1DROP = 0x02, - PM2XXX_INT6_S_ITVPWR2VALIDRISE = 0x04, - PM2XXX_INT6_S_ITVPWR2VALIDFALL = 0x08, - PM2XXX_INT6_S_ITVPWR1VALIDRISE = 0x10, - PM2XXX_INT6_S_ITVPWR1VALIDFALL = 0x20, -}; - -struct pm2xxx_charger_info { - int charger_connected; - int charger_online; - int cv_active; - bool wd_expired; -}; - -struct pm2xxx_charger_event_flags { - bool mainextchnotok; - bool main_thermal_prot; - bool ovv; - bool chgwdexp; -}; - -struct pm2xxx_interrupts { - u8 reg[PM2XXX_NUM_INT_REG]; - int (*handler[PM2XXX_NUM_INT_REG])(void *, int); -}; - -struct pm2xxx_config { - struct i2c_client *pm2xxx_i2c; - struct i2c_device_id *pm2xxx_id; -}; - -struct pm2xxx_irq { - char *name; - irqreturn_t (*isr)(int irq, void *data); -}; - -struct pm2xxx_charger { - struct device *dev; - u8 chip_id; - bool vddadc_en_ac; - struct pm2xxx_config config; - bool ac_conn; - unsigned int gpio_irq; - int vbat; - int old_vbat; - int failure_case; - int failure_input_ovv; - unsigned int lpn_pin; - struct pm2xxx_interrupts *pm2_int; - struct regulator *regu; - struct pm2xxx_bm_data *bat; - struct mutex lock; - struct ab8500 *parent; - struct pm2xxx_charger_info ac; - struct pm2xxx_charger_platform_data *pdata; - struct workqueue_struct *charger_wq; - struct delayed_work check_vbat_work; - struct work_struct ac_work; - struct work_struct check_main_thermal_prot_work; - struct delayed_work check_hw_failure_work; - struct ux500_charger ac_chg; - struct power_supply_desc ac_chg_desc; - struct pm2xxx_charger_event_flags flags; -}; - -#endif /* PM2301_CHARGER_H */ diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 470253c337c7..4b5fb172fa99 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -263,13 +263,13 @@ static int power_supply_check_supplies(struct power_supply *psy) return 0; /* All supplies found, allocate char ** array for filling */ - psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(psy->supplied_from), + psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(*psy->supplied_from), GFP_KERNEL); if (!psy->supplied_from) return -ENOMEM; *psy->supplied_from = devm_kcalloc(&psy->dev, - cnt - 1, sizeof(char *), + cnt - 1, sizeof(**psy->supplied_from), GFP_KERNEL); if (!*psy->supplied_from) return -ENOMEM; diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 89832399e028..e5279ed9a8d7 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -335,7 +335,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) size_t size; /* actual size of vring (in bytes) */ - size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); + size = PAGE_ALIGN(vring_size(rvring->num, rvring->align)); rsc = (void *)rproc->table_ptr + rvdev->rsc_offset; @@ -402,7 +402,7 @@ rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) return -EINVAL; } - rvring->len = vring->num; + rvring->num = vring->num; rvring->align = vring->align; rvring->rvdev = rvdev; diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index 70ab496d0431..81c4f5776109 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -87,7 +87,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, struct fw_rsc_vdev *rsc; struct virtqueue *vq; void *addr; - int len, size; + int num, size; /* we're temporarily limited to two virtqueues per rvdev */ if (id >= ARRAY_SIZE(rvdev->vring)) @@ -104,20 +104,20 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, rvring = &rvdev->vring[id]; addr = mem->va; - len = rvring->len; + num = rvring->num; /* zero vring */ - size = vring_size(len, rvring->align); + size = vring_size(num, rvring->align); memset(addr, 0, size); dev_dbg(dev, "vring%d: va %pK qsz %d notifyid %d\n", - id, addr, len, rvring->notifyid); + id, addr, num, rvring->notifyid); /* * Create the new vq, and tell virtio we're not interested in * the 'weak' smp barriers, since we're talking with a real device. */ - vq = vring_new_virtqueue(id, len, rvring->align, vdev, false, ctx, + vq = vring_new_virtqueue(id, num, rvring->align, vdev, false, ctx, addr, rproc_virtio_notify, callback, name); if (!vq) { dev_err(dev, "vring_new_virtqueue %s failed\n", name); @@ -125,6 +125,8 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, return ERR_PTR(-ENOMEM); } + vq->num_max = num; + rvring->vq = vq; vq->priv = rvring; @@ -156,6 +158,7 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char * const names[], + u32 sizes[], const bool * ctx, struct irq_affinity *desc) { diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a00f901b5c1d..b8de25118ad0 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -383,6 +383,16 @@ config RTC_DRV_MAX77686 This driver can also be built as a module. If so, the module will be called rtc-max77686. +config RTC_DRV_NCT3018Y + tristate "Nuvoton NCT3018Y" + depends on OF + help + If you say yes here you get support for the Nuvoton NCT3018Y I2C RTC + chip. + + This driver can also be built as a module, if so, the module will be + called "rtc-nct3018y". + config RTC_DRV_RK808 tristate "Rockchip RK805/RK808/RK809/RK817/RK818 RTC" depends on MFD_RK808 @@ -1478,16 +1488,6 @@ config RTC_DRV_SUNPLUS This driver can also be built as a module. If so, the module will be called rtc-sunplus. -config RTC_DRV_VR41XX - tristate "NEC VR41XX" - depends on CPU_VR41XX || COMPILE_TEST - help - If you say Y here you will get access to the real time clock - built into your NEC VR41XX CPU. - - To compile this driver as a module, choose M here: the - module will be called rtc-vr41xx. - config RTC_DRV_PL030 tristate "ARM AMBA PL030 RTC" depends on ARM_AMBA @@ -1929,6 +1929,17 @@ config RTC_DRV_ASPEED This driver can also be built as a module, if so, the module will be called "rtc-aspeed". +config RTC_DRV_TI_K3 + tristate "TI K3 RTC" + depends on ARCH_K3 || COMPILE_TEST + select REGMAP_MMIO + help + If you say yes here you get support for the Texas Instruments's + Real Time Clock for K3 architecture. + + This driver can also be built as a module, if so, the module + will be called "rtc-ti-k3". + comment "HID Sensor RTC drivers" config RTC_DRV_HID_SENSOR_TIME @@ -1973,4 +1984,14 @@ config RTC_DRV_MSC313 This driver can also be built as a module, if so, the module will be called "rtc-msc313". +config RTC_DRV_POLARFIRE_SOC + tristate "Microchip PolarFire SoC built-in RTC" + depends on SOC_MICROCHIP_POLARFIRE + help + If you say yes here you will get support for the + built-in RTC on Polarfire SoC. + + This driver can also be built as a module, if so, the module + will be called "rtc-mpfs". + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index fb04467b652d..aab22bc63432 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -112,6 +112,7 @@ obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o obj-$(CONFIG_RTC_DRV_GAMECUBE) += rtc-gamecube.o +obj-$(CONFIG_RTC_DRV_NCT3018Y) += rtc-nct3018y.o obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o @@ -130,6 +131,7 @@ obj-$(CONFIG_RTC_DRV_PIC32) += rtc-pic32.o obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o obj-$(CONFIG_RTC_DRV_PM8XXX) += rtc-pm8xxx.o +obj-$(CONFIG_RTC_DRV_POLARFIRE_SOC) += rtc-mpfs.o obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o obj-$(CONFIG_RTC_DRV_R7301) += rtc-r7301.o @@ -172,11 +174,11 @@ obj-$(CONFIG_RTC_DRV_SUNPLUS) += rtc-sunplus.o obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o +obj-$(CONFIG_RTC_DRV_TI_K3) += rtc-ti-k3.o obj-$(CONFIG_RTC_DRV_TPS6586X) += rtc-tps6586x.o obj-$(CONFIG_RTC_DRV_TPS65910) += rtc-tps65910.o obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o -obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o obj-$(CONFIG_RTC_DRV_WILCO_EC) += rtc-wilco-ec.o obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 3c8eec2218df..e48223c00c67 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -36,7 +36,7 @@ static void rtc_device_release(struct device *dev) cancel_work_sync(&rtc->irqwork); - ida_simple_remove(&rtc_ida, rtc->id); + ida_free(&rtc_ida, rtc->id); mutex_destroy(&rtc->ops_lock); kfree(rtc); } @@ -262,7 +262,7 @@ static int rtc_device_get_id(struct device *dev) } if (id < 0) - id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL); + id = ida_alloc(&rtc_ida, GFP_KERNEL); return id; } @@ -368,7 +368,7 @@ struct rtc_device *devm_rtc_allocate_device(struct device *dev) rtc = rtc_allocate_device(); if (!rtc) { - ida_simple_remove(&rtc_ida, id); + ida_free(&rtc_ida, id); return ERR_PTR(-ENOMEM); } diff --git a/drivers/rtc/dev.c b/drivers/rtc/dev.c index 69325aeede1a..4aad9bb99868 100644 --- a/drivers/rtc/dev.c +++ b/drivers/rtc/dev.c @@ -96,7 +96,7 @@ static int clear_uie(struct rtc_device *rtc) } if (rtc->uie_task_active) { spin_unlock_irq(&rtc->irq_lock); - flush_scheduled_work(); + flush_work(&rtc->uie_task); spin_lock_irq(&rtc->irq_lock); } rtc->uie_irq_active = 0; @@ -566,9 +566,3 @@ void __init rtc_dev_init(void) if (err < 0) pr_err("failed to allocate char dev region\n"); } - -void __exit rtc_dev_exit(void) -{ - if (rtc_devt) - unregister_chrdev_region(rtc_devt, RTC_DEV_MAX); -} diff --git a/drivers/rtc/rtc-ab-b5ze-s3.c b/drivers/rtc/rtc-ab-b5ze-s3.c index 6e3e320dc727..f2b0971d2c65 100644 --- a/drivers/rtc/rtc-ab-b5ze-s3.c +++ b/drivers/rtc/rtc-ab-b5ze-s3.c @@ -817,8 +817,7 @@ static const struct regmap_config abb5zes3_rtc_regmap_config = { .val_bits = 8, }; -static int abb5zes3_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int abb5zes3_probe(struct i2c_client *client) { struct abb5zes3_rtc_data *data = NULL; struct device *dev = &client->dev; @@ -945,7 +944,7 @@ static struct i2c_driver abb5zes3_driver = { .pm = &abb5zes3_rtc_pm_ops, .of_match_table = of_match_ptr(abb5zes3_dt_match), }, - .probe = abb5zes3_probe, + .probe_new = abb5zes3_probe, .id_table = abb5zes3_id, }; module_i2c_driver(abb5zes3_driver); diff --git a/drivers/rtc/rtc-ab-eoz9.c b/drivers/rtc/rtc-ab-eoz9.c index e188ab517f1e..2f8deb8c4cd3 100644 --- a/drivers/rtc/rtc-ab-eoz9.c +++ b/drivers/rtc/rtc-ab-eoz9.c @@ -495,8 +495,7 @@ static void abeoz9_hwmon_register(struct device *dev, #endif -static int abeoz9_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int abeoz9_probe(struct i2c_client *client) { struct abeoz9_rtc_data *data = NULL; struct device *dev = &client->dev; @@ -580,7 +579,7 @@ static struct i2c_driver abeoz9_driver = { .name = "rtc-ab-eoz9", .of_match_table = of_match_ptr(abeoz9_dt_match), }, - .probe = abeoz9_probe, + .probe_new = abeoz9_probe, .id_table = abeoz9_id, }; diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c index 2235c968842d..e0bbb11d912e 100644 --- a/drivers/rtc/rtc-bq32k.c +++ b/drivers/rtc/rtc-bq32k.c @@ -249,8 +249,7 @@ static void bq32k_sysfs_unregister(struct device *dev) device_remove_file(dev, &dev_attr_trickle_charge_bypass); } -static int bq32k_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int bq32k_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct rtc_device *rtc; @@ -322,7 +321,7 @@ static struct i2c_driver bq32k_driver = { .name = "bq32k", .of_match_table = of_match_ptr(bq32k_of_match), }, - .probe = bq32k_probe, + .probe_new = bq32k_probe, .remove = bq32k_remove, .id_table = bq32k_id, }; diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 7c006c2b125f..bdb1df843c78 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -1260,9 +1260,6 @@ static void use_acpi_alarm_quirks(void) if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) return; - if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)) - return; - if (!is_hpet_enabled()) return; diff --git a/drivers/rtc/rtc-core.h b/drivers/rtc/rtc-core.h index 0abf98983e13..4b10a1b8f370 100644 --- a/drivers/rtc/rtc-core.h +++ b/drivers/rtc/rtc-core.h @@ -2,7 +2,6 @@ #ifdef CONFIG_RTC_INTF_DEV extern void __init rtc_dev_init(void); -extern void __exit rtc_dev_exit(void); extern void rtc_dev_prepare(struct rtc_device *rtc); #else @@ -11,10 +10,6 @@ static inline void rtc_dev_init(void) { } -static inline void rtc_dev_exit(void) -{ -} - static inline void rtc_dev_prepare(struct rtc_device *rtc) { } diff --git a/drivers/rtc/rtc-cros-ec.c b/drivers/rtc/rtc-cros-ec.c index 70626793ca69..887f5193e253 100644 --- a/drivers/rtc/rtc-cros-ec.c +++ b/drivers/rtc/rtc-cros-ec.c @@ -375,10 +375,8 @@ static int cros_ec_rtc_remove(struct platform_device *pdev) ret = blocking_notifier_chain_unregister( &cros_ec_rtc->cros_ec->event_notifier, &cros_ec_rtc->notifier); - if (ret) { + if (ret) dev_err(dev, "failed to unregister notifier\n"); - return ret; - } return 0; } diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c index 8db5a631bca8..b19de5100b1a 100644 --- a/drivers/rtc/rtc-ds1374.c +++ b/drivers/rtc/rtc-ds1374.c @@ -467,8 +467,7 @@ static const struct watchdog_ops ds1374_wdt_ops = { * ***************************************************************************** */ -static int ds1374_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ds1374_probe(struct i2c_client *client) { struct ds1374 *ds1374; int ret; @@ -575,7 +574,7 @@ static struct i2c_driver ds1374_driver = { .of_match_table = of_match_ptr(ds1374_of_match), .pm = &ds1374_pm, }, - .probe = ds1374_probe, + .probe_new = ds1374_probe, .remove = ds1374_remove, .id_table = ds1374_id, }; diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index 4cd8efbef6cf..a3bb2cd9c881 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -106,8 +106,7 @@ static const struct rtc_class_ops ds1672_rtc_ops = { .set_time = ds1672_set_time, }; -static int ds1672_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ds1672_probe(struct i2c_client *client) { int err = 0; struct rtc_device *rtc; @@ -150,7 +149,7 @@ static struct i2c_driver ds1672_driver = { .name = "rtc-ds1672", .of_match_table = of_match_ptr(ds1672_of_match), }, - .probe = &ds1672_probe, + .probe_new = ds1672_probe, .id_table = ds1672_id, }; diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index 168bc27f1f5a..dd31a60c1fc6 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c @@ -566,8 +566,7 @@ static const struct dev_pm_ops ds3232_pm_ops = { #if IS_ENABLED(CONFIG_I2C) -static int ds3232_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ds3232_i2c_probe(struct i2c_client *client) { struct regmap *regmap; static const struct regmap_config config = { @@ -604,7 +603,7 @@ static struct i2c_driver ds3232_driver = { .of_match_table = of_match_ptr(ds3232_of_match), .pm = &ds3232_pm_ops, }, - .probe = ds3232_i2c_probe, + .probe_new = ds3232_i2c_probe, .id_table = ds3232_id, }; diff --git a/drivers/rtc/rtc-em3027.c b/drivers/rtc/rtc-em3027.c index 9f176bce48ba..53f9f9391a5f 100644 --- a/drivers/rtc/rtc-em3027.c +++ b/drivers/rtc/rtc-em3027.c @@ -111,8 +111,7 @@ static const struct rtc_class_ops em3027_rtc_ops = { .set_time = em3027_set_time, }; -static int em3027_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int em3027_probe(struct i2c_client *client) { struct rtc_device *rtc; @@ -148,7 +147,7 @@ static struct i2c_driver em3027_driver = { .name = "rtc-em3027", .of_match_table = of_match_ptr(em3027_of_match), }, - .probe = &em3027_probe, + .probe_new = em3027_probe, .id_table = em3027_id, }; diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c index 677ec2da13d8..f59bb81f23c0 100644 --- a/drivers/rtc/rtc-fm3130.c +++ b/drivers/rtc/rtc-fm3130.c @@ -340,8 +340,7 @@ static const struct rtc_class_ops fm3130_rtc_ops = { static struct i2c_driver fm3130_driver; -static int fm3130_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int fm3130_probe(struct i2c_client *client) { struct fm3130 *fm3130; int err = -ENODEV; @@ -518,7 +517,7 @@ static struct i2c_driver fm3130_driver = { .driver = { .name = "rtc-fm3130", }, - .probe = fm3130_probe, + .probe_new = fm3130_probe, .id_table = fm3130_id, }; diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c index 90e602e99d03..cc710d682121 100644 --- a/drivers/rtc/rtc-hym8563.c +++ b/drivers/rtc/rtc-hym8563.c @@ -495,8 +495,7 @@ static int hym8563_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(hym8563_pm_ops, hym8563_suspend, hym8563_resume); -static int hym8563_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int hym8563_probe(struct i2c_client *client) { struct hym8563 *hym8563; int ret; @@ -572,7 +571,7 @@ static struct i2c_driver hym8563_driver = { .pm = &hym8563_pm_ops, .of_match_table = hym8563_dt_idtable, }, - .probe = hym8563_probe, + .probe_new = hym8563_probe, .id_table = hym8563_id, }; diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c index 961bd5d1d109..79461ded1a48 100644 --- a/drivers/rtc/rtc-isl12022.c +++ b/drivers/rtc/rtc-isl12022.c @@ -232,8 +232,7 @@ static const struct rtc_class_ops isl12022_rtc_ops = { .set_time = isl12022_rtc_set_time, }; -static int isl12022_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int isl12022_probe(struct i2c_client *client) { struct isl12022 *isl12022; @@ -275,7 +274,7 @@ static struct i2c_driver isl12022_driver = { .of_match_table = of_match_ptr(isl12022_dt_match), #endif }, - .probe = isl12022_probe, + .probe_new = isl12022_probe, .id_table = isl12022_id, }; diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index 182dfa605515..f448a525333e 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -880,10 +880,14 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) if (rc) return rc; - if (client->irq > 0) + if (client->irq > 0) { rc = isl1208_setup_irq(client, client->irq); - if (rc) - return rc; + if (rc) + return rc; + + } else { + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, isl1208->rtc->features); + } if (evdet_irq > 0 && evdet_irq != client->irq) rc = isl1208_setup_irq(client, evdet_irq); diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c index 4beadfa41644..0a33851cc51f 100644 --- a/drivers/rtc/rtc-max6900.c +++ b/drivers/rtc/rtc-max6900.c @@ -197,8 +197,7 @@ static const struct rtc_class_ops max6900_rtc_ops = { .set_time = max6900_rtc_set_time, }; -static int -max6900_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int max6900_probe(struct i2c_client *client) { struct rtc_device *rtc; @@ -225,7 +224,7 @@ static struct i2c_driver max6900_driver = { .driver = { .name = "rtc-max6900", }, - .probe = max6900_probe, + .probe_new = max6900_probe, .id_table = max6900_id, }; diff --git a/drivers/rtc/rtc-mc146818-lib.c b/drivers/rtc/rtc-mc146818-lib.c index 522449b25921..f1c09f1db044 100644 --- a/drivers/rtc/rtc-mc146818-lib.c +++ b/drivers/rtc/rtc-mc146818-lib.c @@ -21,13 +21,13 @@ bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param), unsigned long flags; unsigned char seconds; - for (i = 0; i < 10; i++) { + for (i = 0; i < 100; i++) { spin_lock_irqsave(&rtc_lock, flags); /* * Check whether there is an update in progress during which the * readout is unspecified. The maximum update time is ~2ms. Poll - * every msec for completion. + * every 100 usec for completion. * * Store the second value before checking UIP so a long lasting * NMI which happens to hit after the UIP check cannot make @@ -37,7 +37,7 @@ bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param), if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) { spin_unlock_irqrestore(&rtc_lock, flags); - mdelay(1); + udelay(100); continue; } @@ -56,7 +56,7 @@ bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param), */ if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) { spin_unlock_irqrestore(&rtc_lock, flags); - mdelay(1); + udelay(100); continue; } diff --git a/drivers/rtc/rtc-mpfs.c b/drivers/rtc/rtc-mpfs.c new file mode 100644 index 000000000000..f14d1925e0c9 --- /dev/null +++ b/drivers/rtc/rtc-mpfs.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip MPFS RTC driver + * + * Copyright (c) 2021-2022 Microchip Corporation. All rights reserved. + * + * Author: Daire McNamara <daire.mcnamara@microchip.com> + * & Conor Dooley <conor.dooley@microchip.com> + */ +#include "linux/bits.h" +#include "linux/iopoll.h" +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_wakeirq.h> +#include <linux/slab.h> +#include <linux/rtc.h> + +#define CONTROL_REG 0x00 +#define MODE_REG 0x04 +#define PRESCALER_REG 0x08 +#define ALARM_LOWER_REG 0x0c +#define ALARM_UPPER_REG 0x10 +#define COMPARE_LOWER_REG 0x14 +#define COMPARE_UPPER_REG 0x18 +#define DATETIME_LOWER_REG 0x20 +#define DATETIME_UPPER_REG 0x24 + +#define CONTROL_RUNNING_BIT BIT(0) +#define CONTROL_START_BIT BIT(0) +#define CONTROL_STOP_BIT BIT(1) +#define CONTROL_ALARM_ON_BIT BIT(2) +#define CONTROL_ALARM_OFF_BIT BIT(3) +#define CONTROL_RESET_BIT BIT(4) +#define CONTROL_UPLOAD_BIT BIT(5) +#define CONTROL_DOWNLOAD_BIT BIT(6) +#define CONTROL_MATCH_BIT BIT(7) +#define CONTROL_WAKEUP_CLR_BIT BIT(8) +#define CONTROL_WAKEUP_SET_BIT BIT(9) +#define CONTROL_UPDATED_BIT BIT(10) + +#define MODE_CLOCK_CALENDAR BIT(0) +#define MODE_WAKE_EN BIT(1) +#define MODE_WAKE_RESET BIT(2) +#define MODE_WAKE_CONTINUE BIT(3) + +#define MAX_PRESCALER_COUNT GENMASK(25, 0) +#define DATETIME_UPPER_MASK GENMASK(29, 0) +#define ALARM_UPPER_MASK GENMASK(10, 0) + +#define UPLOAD_TIMEOUT_US 50 + +struct mpfs_rtc_dev { + struct rtc_device *rtc; + void __iomem *base; +}; + +static void mpfs_rtc_start(struct mpfs_rtc_dev *rtcdev) +{ + u32 ctrl; + + ctrl = readl(rtcdev->base + CONTROL_REG); + ctrl &= ~CONTROL_STOP_BIT; + ctrl |= CONTROL_START_BIT; + writel(ctrl, rtcdev->base + CONTROL_REG); +} + +static void mpfs_rtc_clear_irq(struct mpfs_rtc_dev *rtcdev) +{ + u32 val = readl(rtcdev->base + CONTROL_REG); + + val &= ~(CONTROL_ALARM_ON_BIT | CONTROL_STOP_BIT); + val |= CONTROL_ALARM_OFF_BIT; + writel(val, rtcdev->base + CONTROL_REG); + /* + * Ensure that the posted write to the CONTROL_REG register completed before + * returning from this function. Not doing this may result in the interrupt + * only being cleared some time after this function returns. + */ + (void)readl(rtcdev->base + CONTROL_REG); +} + +static int mpfs_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev); + u64 time; + + time = readl(rtcdev->base + DATETIME_LOWER_REG); + time |= ((u64)readl(rtcdev->base + DATETIME_UPPER_REG) & DATETIME_UPPER_MASK) << 32; + rtc_time64_to_tm(time, tm); + + return 0; +} + +static int mpfs_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev); + u32 ctrl, prog; + u64 time; + int ret; + + time = rtc_tm_to_time64(tm); + + writel((u32)time, rtcdev->base + DATETIME_LOWER_REG); + writel((u32)(time >> 32) & DATETIME_UPPER_MASK, rtcdev->base + DATETIME_UPPER_REG); + + ctrl = readl(rtcdev->base + CONTROL_REG); + ctrl &= ~CONTROL_STOP_BIT; + ctrl |= CONTROL_UPLOAD_BIT; + writel(ctrl, rtcdev->base + CONTROL_REG); + + ret = read_poll_timeout(readl, prog, prog & CONTROL_UPLOAD_BIT, 0, UPLOAD_TIMEOUT_US, + false, rtcdev->base + CONTROL_REG); + if (ret) { + dev_err(dev, "timed out uploading time to rtc"); + return ret; + } + mpfs_rtc_start(rtcdev); + + return 0; +} + +static int mpfs_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev); + u32 mode = readl(rtcdev->base + MODE_REG); + u64 time; + + alrm->enabled = mode & MODE_WAKE_EN; + + time = (u64)readl(rtcdev->base + ALARM_LOWER_REG) << 32; + time |= (readl(rtcdev->base + ALARM_UPPER_REG) & ALARM_UPPER_MASK); + rtc_time64_to_tm(time, &alrm->time); + + return 0; +} + +static int mpfs_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev); + u32 mode, ctrl; + u64 time; + + /* Disable the alarm before updating */ + ctrl = readl(rtcdev->base + CONTROL_REG); + ctrl |= CONTROL_ALARM_OFF_BIT; + writel(ctrl, rtcdev->base + CONTROL_REG); + + time = rtc_tm_to_time64(&alrm->time); + + writel((u32)time, rtcdev->base + ALARM_LOWER_REG); + writel((u32)(time >> 32) & ALARM_UPPER_MASK, rtcdev->base + ALARM_UPPER_REG); + + /* Bypass compare register in alarm mode */ + writel(GENMASK(31, 0), rtcdev->base + COMPARE_LOWER_REG); + writel(GENMASK(29, 0), rtcdev->base + COMPARE_UPPER_REG); + + /* Configure the RTC to enable the alarm. */ + ctrl = readl(rtcdev->base + CONTROL_REG); + mode = readl(rtcdev->base + MODE_REG); + if (alrm->enabled) { + mode = MODE_WAKE_EN | MODE_WAKE_CONTINUE; + /* Enable the alarm */ + ctrl &= ~CONTROL_ALARM_OFF_BIT; + ctrl |= CONTROL_ALARM_ON_BIT; + } + ctrl &= ~CONTROL_STOP_BIT; + ctrl |= CONTROL_START_BIT; + writel(ctrl, rtcdev->base + CONTROL_REG); + writel(mode, rtcdev->base + MODE_REG); + + return 0; +} + +static int mpfs_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev); + u32 ctrl; + + ctrl = readl(rtcdev->base + CONTROL_REG); + ctrl &= ~(CONTROL_ALARM_ON_BIT | CONTROL_ALARM_OFF_BIT | CONTROL_STOP_BIT); + + if (enabled) + ctrl |= CONTROL_ALARM_ON_BIT; + else + ctrl |= CONTROL_ALARM_OFF_BIT; + + writel(ctrl, rtcdev->base + CONTROL_REG); + + return 0; +} + +static inline struct clk *mpfs_rtc_init_clk(struct device *dev) +{ + struct clk *clk; + int ret; + + clk = devm_clk_get(dev, "rtc"); + if (IS_ERR(clk)) + return clk; + + ret = clk_prepare_enable(clk); + if (ret) + return ERR_PTR(ret); + + devm_add_action_or_reset(dev, (void (*) (void *))clk_disable_unprepare, clk); + return clk; +} + +static irqreturn_t mpfs_rtc_wakeup_irq_handler(int irq, void *dev) +{ + struct mpfs_rtc_dev *rtcdev = dev; + + mpfs_rtc_clear_irq(rtcdev); + + rtc_update_irq(rtcdev->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops mpfs_rtc_ops = { + .read_time = mpfs_rtc_readtime, + .set_time = mpfs_rtc_settime, + .read_alarm = mpfs_rtc_readalarm, + .set_alarm = mpfs_rtc_setalarm, + .alarm_irq_enable = mpfs_rtc_alarm_irq_enable, +}; + +static int mpfs_rtc_probe(struct platform_device *pdev) +{ + struct mpfs_rtc_dev *rtcdev; + struct clk *clk; + u32 prescaler; + int wakeup_irq, ret; + + rtcdev = devm_kzalloc(&pdev->dev, sizeof(struct mpfs_rtc_dev), GFP_KERNEL); + if (!rtcdev) + return -ENOMEM; + + platform_set_drvdata(pdev, rtcdev); + + rtcdev->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtcdev->rtc)) + return PTR_ERR(rtcdev->rtc); + + rtcdev->rtc->ops = &mpfs_rtc_ops; + + /* range is capped by alarm max, lower reg is 31:0 & upper is 10:0 */ + rtcdev->rtc->range_max = GENMASK_ULL(42, 0); + + clk = mpfs_rtc_init_clk(&pdev->dev); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + rtcdev->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtcdev->base)) { + dev_dbg(&pdev->dev, "invalid ioremap resources\n"); + return PTR_ERR(rtcdev->base); + } + + wakeup_irq = platform_get_irq(pdev, 0); + if (wakeup_irq <= 0) { + dev_dbg(&pdev->dev, "could not get wakeup irq\n"); + return wakeup_irq; + } + ret = devm_request_irq(&pdev->dev, wakeup_irq, mpfs_rtc_wakeup_irq_handler, 0, + dev_name(&pdev->dev), rtcdev); + if (ret) { + dev_dbg(&pdev->dev, "could not request wakeup irq\n"); + return ret; + } + + /* prescaler hardware adds 1 to reg value */ + prescaler = clk_get_rate(devm_clk_get(&pdev->dev, "rtcref")) - 1; + + if (prescaler > MAX_PRESCALER_COUNT) { + dev_dbg(&pdev->dev, "invalid prescaler %d\n", prescaler); + return -EINVAL; + } + + writel(prescaler, rtcdev->base + PRESCALER_REG); + dev_info(&pdev->dev, "prescaler set to: 0x%X \r\n", prescaler); + + device_init_wakeup(&pdev->dev, true); + ret = dev_pm_set_wake_irq(&pdev->dev, wakeup_irq); + if (ret) + dev_err(&pdev->dev, "failed to enable irq wake\n"); + + return devm_rtc_register_device(rtcdev->rtc); +} + +static int mpfs_rtc_remove(struct platform_device *pdev) +{ + dev_pm_clear_wake_irq(&pdev->dev); + + return 0; +} + +static const struct of_device_id mpfs_rtc_of_match[] = { + { .compatible = "microchip,mpfs-rtc" }, + { } +}; + +MODULE_DEVICE_TABLE(of, mpfs_rtc_of_match); + +static struct platform_driver mpfs_rtc_driver = { + .probe = mpfs_rtc_probe, + .remove = mpfs_rtc_remove, + .driver = { + .name = "mpfs_rtc", + .of_match_table = mpfs_rtc_of_match, + }, +}; + +module_platform_driver(mpfs_rtc_driver); + +MODULE_DESCRIPTION("Real time clock for Microchip Polarfire SoC"); +MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>"); +MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-nct3018y.c b/drivers/rtc/rtc-nct3018y.c new file mode 100644 index 000000000000..d43acd3920ed --- /dev/null +++ b/drivers/rtc/rtc-nct3018y.c @@ -0,0 +1,553 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022 Nuvoton Technology Corporation + +#include <linux/bcd.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/rtc.h> +#include <linux/slab.h> + +#define NCT3018Y_REG_SC 0x00 /* seconds */ +#define NCT3018Y_REG_SCA 0x01 /* alarm */ +#define NCT3018Y_REG_MN 0x02 +#define NCT3018Y_REG_MNA 0x03 /* alarm */ +#define NCT3018Y_REG_HR 0x04 +#define NCT3018Y_REG_HRA 0x05 /* alarm */ +#define NCT3018Y_REG_DW 0x06 +#define NCT3018Y_REG_DM 0x07 +#define NCT3018Y_REG_MO 0x08 +#define NCT3018Y_REG_YR 0x09 +#define NCT3018Y_REG_CTRL 0x0A /* timer control */ +#define NCT3018Y_REG_ST 0x0B /* status */ +#define NCT3018Y_REG_CLKO 0x0C /* clock out */ + +#define NCT3018Y_BIT_AF BIT(7) +#define NCT3018Y_BIT_ST BIT(7) +#define NCT3018Y_BIT_DM BIT(6) +#define NCT3018Y_BIT_HF BIT(5) +#define NCT3018Y_BIT_DSM BIT(4) +#define NCT3018Y_BIT_AIE BIT(3) +#define NCT3018Y_BIT_OFIE BIT(2) +#define NCT3018Y_BIT_CIE BIT(1) +#define NCT3018Y_BIT_TWO BIT(0) + +#define NCT3018Y_REG_BAT_MASK 0x07 +#define NCT3018Y_REG_CLKO_F_MASK 0x03 /* frequenc mask */ +#define NCT3018Y_REG_CLKO_CKE 0x80 /* clock out enabled */ + +struct nct3018y { + struct rtc_device *rtc; + struct i2c_client *client; +#ifdef CONFIG_COMMON_CLK + struct clk_hw clkout_hw; +#endif +}; + +static int nct3018y_set_alarm_mode(struct i2c_client *client, bool on) +{ + int err, flags; + + dev_dbg(&client->dev, "%s:on:%d\n", __func__, on); + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CTRL); + if (flags < 0) { + dev_dbg(&client->dev, + "Failed to read NCT3018Y_REG_CTRL\n"); + return flags; + } + + if (on) + flags |= NCT3018Y_BIT_AIE; + else + flags &= ~NCT3018Y_BIT_AIE; + + flags |= NCT3018Y_BIT_CIE; + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_CTRL, flags); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_CTRL\n"); + return err; + } + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_ST); + if (flags < 0) { + dev_dbg(&client->dev, + "Failed to read NCT3018Y_REG_ST\n"); + return flags; + } + + flags &= ~(NCT3018Y_BIT_AF); + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_ST, flags); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_ST\n"); + return err; + } + + return 0; +} + +static int nct3018y_get_alarm_mode(struct i2c_client *client, unsigned char *alarm_enable, + unsigned char *alarm_flag) +{ + int flags; + + if (alarm_enable) { + dev_dbg(&client->dev, "%s:NCT3018Y_REG_CTRL\n", __func__); + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CTRL); + if (flags < 0) + return flags; + *alarm_enable = flags & NCT3018Y_BIT_AIE; + } + + if (alarm_flag) { + dev_dbg(&client->dev, "%s:NCT3018Y_REG_ST\n", __func__); + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_ST); + if (flags < 0) + return flags; + *alarm_flag = flags & NCT3018Y_BIT_AF; + } + + dev_dbg(&client->dev, "%s:alarm_enable:%x alarm_flag:%x\n", + __func__, *alarm_enable, *alarm_flag); + + return 0; +} + +static irqreturn_t nct3018y_irq(int irq, void *dev_id) +{ + struct nct3018y *nct3018y = i2c_get_clientdata(dev_id); + struct i2c_client *client = nct3018y->client; + int err; + unsigned char alarm_flag; + unsigned char alarm_enable; + + dev_dbg(&client->dev, "%s:irq:%d\n", __func__, irq); + err = nct3018y_get_alarm_mode(nct3018y->client, &alarm_enable, &alarm_flag); + if (err) + return IRQ_NONE; + + if (alarm_flag) { + dev_dbg(&client->dev, "%s:alarm flag:%x\n", + __func__, alarm_flag); + rtc_update_irq(nct3018y->rtc, 1, RTC_IRQF | RTC_AF); + nct3018y_set_alarm_mode(nct3018y->client, 0); + dev_dbg(&client->dev, "%s:IRQ_HANDLED\n", __func__); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +/* + * In the routines that deal directly with the nct3018y hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int nct3018y_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[10]; + int err; + + err = i2c_smbus_read_i2c_block_data(client, NCT3018Y_REG_ST, 1, buf); + if (err < 0) + return err; + + if (!buf[0]) { + dev_dbg(&client->dev, " voltage <=1.7, date/time is not reliable.\n"); + return -EINVAL; + } + + err = i2c_smbus_read_i2c_block_data(client, NCT3018Y_REG_SC, sizeof(buf), buf); + if (err < 0) + return err; + + tm->tm_sec = bcd2bin(buf[0] & 0x7F); + tm->tm_min = bcd2bin(buf[2] & 0x7F); + tm->tm_hour = bcd2bin(buf[4] & 0x3F); + tm->tm_wday = buf[6] & 0x07; + tm->tm_mday = bcd2bin(buf[7] & 0x3F); + tm->tm_mon = bcd2bin(buf[8] & 0x1F) - 1; + tm->tm_year = bcd2bin(buf[9]) + 100; + + return 0; +} + +static int nct3018y_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[4] = {0}; + int err; + + buf[0] = bin2bcd(tm->tm_sec); + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_SC, buf[0]); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_SC\n"); + return err; + } + + buf[0] = bin2bcd(tm->tm_min); + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_MN, buf[0]); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_MN\n"); + return err; + } + + buf[0] = bin2bcd(tm->tm_hour); + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_HR, buf[0]); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_HR\n"); + return err; + } + + buf[0] = tm->tm_wday & 0x07; + buf[1] = bin2bcd(tm->tm_mday); + buf[2] = bin2bcd(tm->tm_mon + 1); + buf[3] = bin2bcd(tm->tm_year - 100); + err = i2c_smbus_write_i2c_block_data(client, NCT3018Y_REG_DW, + sizeof(buf), buf); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write for day and mon and year\n"); + return -EIO; + } + + return err; +} + +static int nct3018y_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[5]; + int err; + + err = i2c_smbus_read_i2c_block_data(client, NCT3018Y_REG_SCA, + sizeof(buf), buf); + if (err < 0) { + dev_dbg(&client->dev, "Unable to read date\n"); + return -EIO; + } + + dev_dbg(&client->dev, "%s: raw data is sec=%02x, min=%02x hr=%02x\n", + __func__, buf[0], buf[2], buf[4]); + + tm->time.tm_sec = bcd2bin(buf[0] & 0x7F); + tm->time.tm_min = bcd2bin(buf[2] & 0x7F); + tm->time.tm_hour = bcd2bin(buf[4] & 0x3F); + + err = nct3018y_get_alarm_mode(client, &tm->enabled, &tm->pending); + if (err < 0) + return err; + + dev_dbg(&client->dev, "%s:s=%d m=%d, hr=%d, enabled=%d, pending=%d\n", + __func__, tm->time.tm_sec, tm->time.tm_min, + tm->time.tm_hour, tm->enabled, tm->pending); + + return 0; +} + +static int nct3018y_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + int err; + + dev_dbg(dev, "%s, sec=%d, min=%d hour=%d tm->enabled:%d\n", + __func__, tm->time.tm_sec, tm->time.tm_min, tm->time.tm_hour, + tm->enabled); + + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_SCA, bin2bcd(tm->time.tm_sec)); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_SCA\n"); + return err; + } + + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_MNA, bin2bcd(tm->time.tm_min)); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_MNA\n"); + return err; + } + + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_HRA, bin2bcd(tm->time.tm_hour)); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_HRA\n"); + return err; + } + + return nct3018y_set_alarm_mode(client, tm->enabled); +} + +static int nct3018y_irq_enable(struct device *dev, unsigned int enabled) +{ + dev_dbg(dev, "%s: alarm enable=%d\n", __func__, enabled); + + return nct3018y_set_alarm_mode(to_i2c_client(dev), enabled); +} + +static int nct3018y_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = to_i2c_client(dev); + int status, flags = 0; + + switch (cmd) { + case RTC_VL_READ: + status = i2c_smbus_read_byte_data(client, NCT3018Y_REG_ST); + if (status < 0) + return status; + + if (!(status & NCT3018Y_REG_BAT_MASK)) + flags |= RTC_VL_DATA_INVALID; + + return put_user(flags, (unsigned int __user *)arg); + + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMMON_CLK +/* + * Handling of the clkout + */ + +#define clkout_hw_to_nct3018y(_hw) container_of(_hw, struct nct3018y, clkout_hw) + +static const int clkout_rates[] = { + 32768, + 1024, + 32, + 1, +}; + +static unsigned long nct3018y_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct nct3018y *nct3018y = clkout_hw_to_nct3018y(hw); + struct i2c_client *client = nct3018y->client; + int flags; + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CLKO); + if (flags < 0) + return 0; + + flags &= NCT3018Y_REG_CLKO_F_MASK; + return clkout_rates[flags]; +} + +static long nct3018y_clkout_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] <= rate) + return clkout_rates[i]; + + return 0; +} + +static int nct3018y_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct nct3018y *nct3018y = clkout_hw_to_nct3018y(hw); + struct i2c_client *client = nct3018y->client; + int i, flags; + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CLKO); + if (flags < 0) + return flags; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] == rate) { + flags &= ~NCT3018Y_REG_CLKO_F_MASK; + flags |= i; + return i2c_smbus_write_byte_data(client, NCT3018Y_REG_CLKO, flags); + } + + return -EINVAL; +} + +static int nct3018y_clkout_control(struct clk_hw *hw, bool enable) +{ + struct nct3018y *nct3018y = clkout_hw_to_nct3018y(hw); + struct i2c_client *client = nct3018y->client; + int flags; + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CLKO); + if (flags < 0) + return flags; + + if (enable) + flags |= NCT3018Y_REG_CLKO_CKE; + else + flags &= ~NCT3018Y_REG_CLKO_CKE; + + return i2c_smbus_write_byte_data(client, NCT3018Y_REG_CLKO, flags); +} + +static int nct3018y_clkout_prepare(struct clk_hw *hw) +{ + return nct3018y_clkout_control(hw, 1); +} + +static void nct3018y_clkout_unprepare(struct clk_hw *hw) +{ + nct3018y_clkout_control(hw, 0); +} + +static int nct3018y_clkout_is_prepared(struct clk_hw *hw) +{ + struct nct3018y *nct3018y = clkout_hw_to_nct3018y(hw); + struct i2c_client *client = nct3018y->client; + int flags; + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CLKO); + if (flags < 0) + return flags; + + return flags & NCT3018Y_REG_CLKO_CKE; +} + +static const struct clk_ops nct3018y_clkout_ops = { + .prepare = nct3018y_clkout_prepare, + .unprepare = nct3018y_clkout_unprepare, + .is_prepared = nct3018y_clkout_is_prepared, + .recalc_rate = nct3018y_clkout_recalc_rate, + .round_rate = nct3018y_clkout_round_rate, + .set_rate = nct3018y_clkout_set_rate, +}; + +static struct clk *nct3018y_clkout_register_clk(struct nct3018y *nct3018y) +{ + struct i2c_client *client = nct3018y->client; + struct device_node *node = client->dev.of_node; + struct clk *clk; + struct clk_init_data init; + + init.name = "nct3018y-clkout"; + init.ops = &nct3018y_clkout_ops; + init.flags = 0; + init.parent_names = NULL; + init.num_parents = 0; + nct3018y->clkout_hw.init = &init; + + /* optional override of the clockname */ + of_property_read_string(node, "clock-output-names", &init.name); + + /* register the clock */ + clk = devm_clk_register(&client->dev, &nct3018y->clkout_hw); + + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return clk; +} +#endif + +static const struct rtc_class_ops nct3018y_rtc_ops = { + .read_time = nct3018y_rtc_read_time, + .set_time = nct3018y_rtc_set_time, + .read_alarm = nct3018y_rtc_read_alarm, + .set_alarm = nct3018y_rtc_set_alarm, + .alarm_irq_enable = nct3018y_irq_enable, + .ioctl = nct3018y_ioctl, +}; + +static int nct3018y_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct nct3018y *nct3018y; + int err, flags; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BLOCK_DATA)) + return -ENODEV; + + nct3018y = devm_kzalloc(&client->dev, sizeof(struct nct3018y), + GFP_KERNEL); + if (!nct3018y) + return -ENOMEM; + + i2c_set_clientdata(client, nct3018y); + nct3018y->client = client; + device_set_wakeup_capable(&client->dev, 1); + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CTRL); + if (flags < 0) { + dev_dbg(&client->dev, "%s: read error\n", __func__); + return flags; + } else if (flags & NCT3018Y_BIT_TWO) { + dev_dbg(&client->dev, "%s: NCT3018Y_BIT_TWO is set\n", __func__); + } + + flags = NCT3018Y_BIT_TWO; + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_CTRL, flags); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_CTRL\n"); + return err; + } + + flags = 0; + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_ST, flags); + if (err < 0) { + dev_dbg(&client->dev, "%s: write error\n", __func__); + return err; + } + + nct3018y->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(nct3018y->rtc)) + return PTR_ERR(nct3018y->rtc); + + nct3018y->rtc->ops = &nct3018y_rtc_ops; + nct3018y->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + nct3018y->rtc->range_max = RTC_TIMESTAMP_END_2099; + + if (client->irq > 0) { + err = devm_request_threaded_irq(&client->dev, client->irq, + NULL, nct3018y_irq, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING, + "nct3018y", client); + if (err) { + dev_dbg(&client->dev, "unable to request IRQ %d\n", client->irq); + return err; + } + } else { + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, nct3018y->rtc->features); + clear_bit(RTC_FEATURE_ALARM, nct3018y->rtc->features); + } + +#ifdef CONFIG_COMMON_CLK + /* register clk in common clk framework */ + nct3018y_clkout_register_clk(nct3018y); +#endif + + return devm_rtc_register_device(nct3018y->rtc); +} + +static const struct i2c_device_id nct3018y_id[] = { + { "nct3018y", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nct3018y_id); + +static const struct of_device_id nct3018y_of_match[] = { + { .compatible = "nuvoton,nct3018y" }, + {} +}; +MODULE_DEVICE_TABLE(of, nct3018y_of_match); + +static struct i2c_driver nct3018y_driver = { + .driver = { + .name = "rtc-nct3018y", + .of_match_table = of_match_ptr(nct3018y_of_match), + }, + .probe = nct3018y_probe, + .id_table = nct3018y_id, +}; + +module_i2c_driver(nct3018y_driver); + +MODULE_AUTHOR("Medad CChien <ctcchien@nuvoton.com>"); +MODULE_AUTHOR("Mia Lin <mimi05633@gmail.com>"); +MODULE_DESCRIPTION("Nuvoton NCT3018Y RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index b1b1943de844..6174b3fd4b98 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -390,8 +390,7 @@ static const struct regmap_config regmap_config = { .max_register = 0x13, }; -static int pcf8523_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pcf8523_probe(struct i2c_client *client) { struct pcf8523 *pcf8523; struct rtc_device *rtc; @@ -485,7 +484,7 @@ static struct i2c_driver pcf8523_driver = { .name = "rtc-pcf8523", .of_match_table = pcf8523_of_match, }, - .probe = pcf8523_probe, + .probe_new = pcf8523_probe, .id_table = pcf8523_id, }; module_i2c_driver(pcf8523_driver); diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c index bb3e9ba75f6c..c05b722f0060 100644 --- a/drivers/rtc/rtc-pcf85363.c +++ b/drivers/rtc/rtc-pcf85363.c @@ -350,8 +350,7 @@ static const struct pcf85x63_config pcf_85363_config = { .num_nvram = 2 }; -static int pcf85363_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pcf85363_probe(struct i2c_client *client) { struct pcf85363 *pcf85363; const struct pcf85x63_config *config = &pcf_85363_config; @@ -436,7 +435,7 @@ static struct i2c_driver pcf85363_driver = { .name = "pcf85363", .of_match_table = of_match_ptr(dev_ids), }, - .probe = pcf85363_probe, + .probe_new = pcf85363_probe, }; module_i2c_driver(pcf85363_driver); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 9d06813e2e6d..11fa9788558b 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -509,8 +509,7 @@ static const struct rtc_class_ops pcf8563_rtc_ops = { .alarm_irq_enable = pcf8563_irq_enable, }; -static int pcf8563_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pcf8563_probe(struct i2c_client *client) { struct pcf8563 *pcf8563; int err; @@ -606,7 +605,7 @@ static struct i2c_driver pcf8563_driver = { .name = "rtc-pcf8563", .of_match_table = of_match_ptr(pcf8563_of_match), }, - .probe = pcf8563_probe, + .probe_new = pcf8563_probe, .id_table = pcf8563_id, }; diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c index c80ca20e5d8d..87074d178274 100644 --- a/drivers/rtc/rtc-pcf8583.c +++ b/drivers/rtc/rtc-pcf8583.c @@ -275,8 +275,7 @@ static const struct rtc_class_ops pcf8583_rtc_ops = { .set_time = pcf8583_rtc_set_time, }; -static int pcf8583_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pcf8583_probe(struct i2c_client *client) { struct pcf8583 *pcf8583; @@ -307,7 +306,7 @@ static struct i2c_driver pcf8583_driver = { .driver = { .name = "pcf8583", }, - .probe = pcf8583_probe, + .probe_new = pcf8583_probe, .id_table = pcf8583_id, }; diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c index 8cb84c9595fc..eb483a30bd92 100644 --- a/drivers/rtc/rtc-rv3029c2.c +++ b/drivers/rtc/rtc-rv3029c2.c @@ -784,8 +784,7 @@ static const struct regmap_config config = { #if IS_ENABLED(CONFIG_I2C) -static int rv3029_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int rv3029_i2c_probe(struct i2c_client *client) { struct regmap *regmap; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK | @@ -819,7 +818,7 @@ static struct i2c_driver rv3029_driver = { .name = "rv3029", .of_match_table = of_match_ptr(rv3029_of_match), }, - .probe = rv3029_i2c_probe, + .probe_new = rv3029_i2c_probe, .id_table = rv3029_id, }; diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c index f69e0b1137cd..3527a0521e9b 100644 --- a/drivers/rtc/rtc-rv8803.c +++ b/drivers/rtc/rtc-rv8803.c @@ -9,6 +9,7 @@ #include <linux/bcd.h> #include <linux/bitops.h> +#include <linux/bitfield.h> #include <linux/log2.h> #include <linux/i2c.h> #include <linux/interrupt.h> @@ -33,6 +34,7 @@ #define RV8803_EXT 0x0D #define RV8803_FLAG 0x0E #define RV8803_CTRL 0x0F +#define RV8803_OSC_OFFSET 0x2C #define RV8803_EXT_WADA BIT(6) @@ -49,12 +51,15 @@ #define RV8803_CTRL_TIE BIT(4) #define RV8803_CTRL_UIE BIT(5) +#define RX8803_CTRL_CSEL GENMASK(7, 6) + #define RX8900_BACKUP_CTRL 0x18 #define RX8900_FLAG_SWOFF BIT(2) #define RX8900_FLAG_VDETOFF BIT(3) enum rv8803_type { rv_8803, + rx_8803, rx_8804, rx_8900 }; @@ -64,6 +69,7 @@ struct rv8803_data { struct rtc_device *rtc; struct mutex flags_lock; u8 ctrl; + u8 backup; enum rv8803_type type; }; @@ -136,6 +142,44 @@ static int rv8803_write_regs(const struct i2c_client *client, return ret; } +static int rv8803_regs_init(struct rv8803_data *rv8803) +{ + int ret; + + ret = rv8803_write_reg(rv8803->client, RV8803_OSC_OFFSET, 0x00); + if (ret) + return ret; + + ret = rv8803_write_reg(rv8803->client, RV8803_CTRL, + FIELD_PREP(RX8803_CTRL_CSEL, 1)); /* 2s */ + if (ret) + return ret; + + ret = rv8803_write_regs(rv8803->client, RV8803_ALARM_MIN, 3, + (u8[]){ 0, 0, 0 }); + if (ret) + return ret; + + return rv8803_write_reg(rv8803->client, RV8803_RAM, 0x00); +} + +static int rv8803_regs_configure(struct rv8803_data *rv8803); + +static int rv8803_regs_reset(struct rv8803_data *rv8803) +{ + /* + * The RV-8803 resets all registers to POR defaults after voltage-loss, + * the Epson RTCs don't, so we manually reset the remainder here. + */ + if (rv8803->type == rx_8803 || rv8803->type == rx_8900) { + int ret = rv8803_regs_init(rv8803); + if (ret) + return ret; + } + + return rv8803_regs_configure(rv8803); +} + static irqreturn_t rv8803_handle_irq(int irq, void *dev_id) { struct i2c_client *client = dev_id; @@ -269,6 +313,14 @@ static int rv8803_set_time(struct device *dev, struct rtc_time *tm) return flags; } + if (flags & RV8803_FLAG_V2F) { + ret = rv8803_regs_reset(rv8803); + if (ret) { + mutex_unlock(&rv8803->flags_lock); + return ret; + } + } + ret = rv8803_write_reg(rv8803->client, RV8803_FLAG, flags & ~(RV8803_FLAG_V1F | RV8803_FLAG_V2F)); @@ -498,18 +550,32 @@ static int rx8900_trickle_charger_init(struct rv8803_data *rv8803) if (err < 0) return err; - flags = ~(RX8900_FLAG_VDETOFF | RX8900_FLAG_SWOFF) & (u8)err; - - if (of_property_read_bool(node, "epson,vdet-disable")) - flags |= RX8900_FLAG_VDETOFF; - - if (of_property_read_bool(node, "trickle-diode-disable")) - flags |= RX8900_FLAG_SWOFF; + flags = (u8)err; + flags &= ~(RX8900_FLAG_VDETOFF | RX8900_FLAG_SWOFF); + flags |= rv8803->backup; return i2c_smbus_write_byte_data(rv8803->client, RX8900_BACKUP_CTRL, flags); } +/* configure registers with values different than the Power-On reset defaults */ +static int rv8803_regs_configure(struct rv8803_data *rv8803) +{ + int err; + + err = rv8803_write_reg(rv8803->client, RV8803_EXT, RV8803_EXT_WADA); + if (err) + return err; + + err = rx8900_trickle_charger_init(rv8803); + if (err) { + dev_err(&rv8803->client->dev, "failed to init charger\n"); + return err; + } + + return 0; +} + static int rv8803_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -576,15 +642,15 @@ static int rv8803_probe(struct i2c_client *client, if (!client->irq) clear_bit(RTC_FEATURE_ALARM, rv8803->rtc->features); - err = rv8803_write_reg(rv8803->client, RV8803_EXT, RV8803_EXT_WADA); - if (err) - return err; + if (of_property_read_bool(client->dev.of_node, "epson,vdet-disable")) + rv8803->backup |= RX8900_FLAG_VDETOFF; - err = rx8900_trickle_charger_init(rv8803); - if (err) { - dev_err(&client->dev, "failed to init charger\n"); + if (of_property_read_bool(client->dev.of_node, "trickle-diode-disable")) + rv8803->backup |= RX8900_FLAG_SWOFF; + + err = rv8803_regs_configure(rv8803); + if (err) return err; - } rv8803->rtc->ops = &rv8803_rtc_ops; rv8803->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; @@ -603,7 +669,7 @@ static int rv8803_probe(struct i2c_client *client, static const struct i2c_device_id rv8803_id[] = { { "rv8803", rv_8803 }, { "rv8804", rx_8804 }, - { "rx8803", rv_8803 }, + { "rx8803", rx_8803 }, { "rx8900", rx_8900 }, { } }; @@ -616,7 +682,7 @@ static const __maybe_unused struct of_device_id rv8803_of_match[] = { }, { .compatible = "epson,rx8803", - .data = (void *)rv_8803 + .data = (void *)rx_8803 }, { .compatible = "epson,rx8804", diff --git a/drivers/rtc/rtc-rx6110.c b/drivers/rtc/rtc-rx6110.c index 758fd6e11a15..cc634558b928 100644 --- a/drivers/rtc/rtc-rx6110.c +++ b/drivers/rtc/rtc-rx6110.c @@ -419,8 +419,7 @@ static struct regmap_config regmap_i2c_config = { .read_flag_mask = 0x80, }; -static int rx6110_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int rx6110_i2c_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct rx6110_data *rx6110; @@ -464,7 +463,7 @@ static struct i2c_driver rx6110_i2c_driver = { .name = RX6110_DRIVER_NAME, .acpi_match_table = rx6110_i2c_acpi_match, }, - .probe = rx6110_i2c_probe, + .probe_new = rx6110_i2c_probe, .id_table = rx6110_i2c_id, }; diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c index b32117ccd74b..dde86f3e2a4b 100644 --- a/drivers/rtc/rtc-rx8025.c +++ b/drivers/rtc/rtc-rx8025.c @@ -55,6 +55,8 @@ #define RX8025_BIT_CTRL2_XST BIT(5) #define RX8025_BIT_CTRL2_VDET BIT(6) +#define RX8035_BIT_HOUR_1224 BIT(7) + /* Clock precision adjustment */ #define RX8025_ADJ_RESOLUTION 3050 /* in ppb */ #define RX8025_ADJ_DATA_MAX 62 @@ -78,6 +80,7 @@ struct rx8025_data { struct rtc_device *rtc; enum rx_model model; u8 ctrl1; + int is_24; }; static s32 rx8025_read_reg(const struct i2c_client *client, u8 number) @@ -226,7 +229,7 @@ static int rx8025_get_time(struct device *dev, struct rtc_time *dt) dt->tm_sec = bcd2bin(date[RX8025_REG_SEC] & 0x7f); dt->tm_min = bcd2bin(date[RX8025_REG_MIN] & 0x7f); - if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224) + if (rx8025->is_24) dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x3f); else dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x1f) % 12 @@ -254,7 +257,7 @@ static int rx8025_set_time(struct device *dev, struct rtc_time *dt) */ date[RX8025_REG_SEC] = bin2bcd(dt->tm_sec); date[RX8025_REG_MIN] = bin2bcd(dt->tm_min); - if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224) + if (rx8025->is_24) date[RX8025_REG_HOUR] = bin2bcd(dt->tm_hour); else date[RX8025_REG_HOUR] = (dt->tm_hour >= 12 ? 0x20 : 0) @@ -279,6 +282,7 @@ static int rx8025_init_client(struct i2c_client *client) struct rx8025_data *rx8025 = i2c_get_clientdata(client); u8 ctrl[2], ctrl2; int need_clear = 0; + int hour_reg; int err; err = rx8025_read_regs(client, RX8025_REG_CTRL1, 2, ctrl); @@ -303,6 +307,16 @@ static int rx8025_init_client(struct i2c_client *client) err = rx8025_write_reg(client, RX8025_REG_CTRL2, ctrl2); } + + if (rx8025->model == model_rx_8035) { + /* In RX-8035, 12/24 flag is in the hour register */ + hour_reg = rx8025_read_reg(client, RX8025_REG_HOUR); + if (hour_reg < 0) + return hour_reg; + rx8025->is_24 = (hour_reg & RX8035_BIT_HOUR_1224); + } else { + rx8025->is_24 = (ctrl[1] & RX8025_BIT_CTRL1_1224); + } out: return err; } @@ -329,7 +343,7 @@ static int rx8025_read_alarm(struct device *dev, struct rtc_wkalrm *t) /* Hardware alarms precision is 1 minute! */ t->time.tm_sec = 0; t->time.tm_min = bcd2bin(ald[0] & 0x7f); - if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224) + if (rx8025->is_24) t->time.tm_hour = bcd2bin(ald[1] & 0x3f); else t->time.tm_hour = bcd2bin(ald[1] & 0x1f) % 12 @@ -350,7 +364,7 @@ static int rx8025_set_alarm(struct device *dev, struct rtc_wkalrm *t) int err; ald[0] = bin2bcd(t->time.tm_min); - if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224) + if (rx8025->is_24) ald[1] = bin2bcd(t->time.tm_hour); else ald[1] = (t->time.tm_hour >= 12 ? 0x20 : 0) diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c index aed4898a0ff4..14edb7534c97 100644 --- a/drivers/rtc/rtc-rx8581.c +++ b/drivers/rtc/rtc-rx8581.c @@ -248,8 +248,7 @@ static const struct rx85x1_config rx8571_config = { .num_nvram = 2 }; -static int rx8581_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int rx8581_probe(struct i2c_client *client) { struct rx8581 *rx8581; const struct rx85x1_config *config = &rx8581_config; @@ -326,7 +325,7 @@ static struct i2c_driver rx8581_driver = { .name = "rtc-rx8581", .of_match_table = of_match_ptr(rx8581_of_match), }, - .probe = rx8581_probe, + .probe_new = rx8581_probe, .id_table = rx8581_id, }; diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c index 26278c770731..81d97b1d3159 100644 --- a/drivers/rtc/rtc-s35390a.c +++ b/drivers/rtc/rtc-s35390a.c @@ -420,8 +420,7 @@ static const struct rtc_class_ops s35390a_rtc_ops = { .ioctl = s35390a_rtc_ioctl, }; -static int s35390a_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int s35390a_probe(struct i2c_client *client) { int err, err_read; unsigned int i; @@ -502,7 +501,7 @@ static struct i2c_driver s35390a_driver = { .name = "rtc-s35390a", .of_match_table = of_match_ptr(s35390a_of_match), }, - .probe = s35390a_probe, + .probe_new = s35390a_probe, .id_table = s35390a_id, }; diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c index 24e8528e23ec..e2f90d768ca8 100644 --- a/drivers/rtc/rtc-sd3078.c +++ b/drivers/rtc/rtc-sd3078.c @@ -163,8 +163,7 @@ static const struct regmap_config regmap_config = { .max_register = 0x11, }; -static int sd3078_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sd3078_probe(struct i2c_client *client) { int ret; struct sd3078 *sd3078; @@ -218,7 +217,7 @@ static struct i2c_driver sd3078_driver = { .name = "sd3078", .of_match_table = of_match_ptr(rtc_dt_match), }, - .probe = sd3078_probe, + .probe_new = sd3078_probe, .id_table = sd3078_id, }; diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c index d4777b01ab22..736fe535cd45 100644 --- a/drivers/rtc/rtc-spear.c +++ b/drivers/rtc/rtc-spear.c @@ -388,7 +388,7 @@ static int spear_rtc_probe(struct platform_device *pdev) config->rtc->ops = &spear_rtc_ops; config->rtc->range_min = RTC_TIMESTAMP_BEGIN_0000; - config->rtc->range_min = RTC_TIMESTAMP_END_9999; + config->rtc->range_max = RTC_TIMESTAMP_END_9999; status = devm_rtc_register_device(config->rtc); if (status) diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c index 57540727ce1c..ed5516089e9a 100644 --- a/drivers/rtc/rtc-sun6i.c +++ b/drivers/rtc/rtc-sun6i.c @@ -875,6 +875,8 @@ static const struct of_device_id sun6i_rtc_dt_ids[] = { { .compatible = "allwinner,sun50i-h6-rtc" }, { .compatible = "allwinner,sun50i-h616-rtc", .data = (void *)RTC_LINEAR_DAY }, + { .compatible = "allwinner,sun50i-r329-rtc", + .data = (void *)RTC_LINEAR_DAY }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids); diff --git a/drivers/rtc/rtc-ti-k3.c b/drivers/rtc/rtc-ti-k3.c new file mode 100644 index 000000000000..7a0f181d3fef --- /dev/null +++ b/drivers/rtc/rtc-ti-k3.c @@ -0,0 +1,680 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments K3 RTC driver + * + * Copyright (C) 2021-2022 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/rtc.h> + +/* Registers */ +#define REG_K3RTC_S_CNT_LSW 0x08 +#define REG_K3RTC_S_CNT_MSW 0x0c +#define REG_K3RTC_COMP 0x10 +#define REG_K3RTC_ON_OFF_S_CNT_LSW 0x20 +#define REG_K3RTC_ON_OFF_S_CNT_MSW 0x24 +#define REG_K3RTC_SCRATCH0 0x30 +#define REG_K3RTC_SCRATCH7 0x4c +#define REG_K3RTC_GENERAL_CTL 0x50 +#define REG_K3RTC_IRQSTATUS_RAW_SYS 0x54 +#define REG_K3RTC_IRQSTATUS_SYS 0x58 +#define REG_K3RTC_IRQENABLE_SET_SYS 0x5c +#define REG_K3RTC_IRQENABLE_CLR_SYS 0x60 +#define REG_K3RTC_SYNCPEND 0x68 +#define REG_K3RTC_KICK0 0x70 +#define REG_K3RTC_KICK1 0x74 + +/* Freeze when lsw is read and unfreeze when msw is read */ +#define K3RTC_CNT_FMODE_S_CNT_VALUE (0x2 << 24) + +/* Magic values for lock/unlock */ +#define K3RTC_KICK0_UNLOCK_VALUE 0x83e70b13 +#define K3RTC_KICK1_UNLOCK_VALUE 0x95a4f1e0 + +/* Multiplier for ppb conversions */ +#define K3RTC_PPB_MULT (1000000000LL) +/* Min and max values supported with 'offset' interface (swapped sign) */ +#define K3RTC_MIN_OFFSET (-277761) +#define K3RTC_MAX_OFFSET (277778) + +/** + * struct ti_k3_rtc_soc_data - Private of compatible data for ti-k3-rtc + * @unlock_irq_erratum: Has erratum for unlock infinite IRQs (erratum i2327) + */ +struct ti_k3_rtc_soc_data { + const bool unlock_irq_erratum; +}; + +static const struct regmap_config ti_k3_rtc_regmap_config = { + .name = "peripheral-registers", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = REG_K3RTC_KICK1, +}; + +enum ti_k3_rtc_fields { + K3RTC_KICK0, + K3RTC_KICK1, + K3RTC_S_CNT_LSW, + K3RTC_S_CNT_MSW, + K3RTC_O32K_OSC_DEP_EN, + K3RTC_UNLOCK, + K3RTC_CNT_FMODE, + K3RTC_PEND, + K3RTC_RELOAD_FROM_BBD, + K3RTC_COMP, + + K3RTC_ALM_S_CNT_LSW, + K3RTC_ALM_S_CNT_MSW, + K3RTC_IRQ_STATUS_RAW, + K3RTC_IRQ_STATUS, + K3RTC_IRQ_ENABLE_SET, + K3RTC_IRQ_ENABLE_CLR, + + K3RTC_IRQ_STATUS_ALT, + K3RTC_IRQ_ENABLE_CLR_ALT, + + K3_RTC_MAX_FIELDS +}; + +static const struct reg_field ti_rtc_reg_fields[] = { + [K3RTC_KICK0] = REG_FIELD(REG_K3RTC_KICK0, 0, 31), + [K3RTC_KICK1] = REG_FIELD(REG_K3RTC_KICK1, 0, 31), + [K3RTC_S_CNT_LSW] = REG_FIELD(REG_K3RTC_S_CNT_LSW, 0, 31), + [K3RTC_S_CNT_MSW] = REG_FIELD(REG_K3RTC_S_CNT_MSW, 0, 15), + [K3RTC_O32K_OSC_DEP_EN] = REG_FIELD(REG_K3RTC_GENERAL_CTL, 21, 21), + [K3RTC_UNLOCK] = REG_FIELD(REG_K3RTC_GENERAL_CTL, 23, 23), + [K3RTC_CNT_FMODE] = REG_FIELD(REG_K3RTC_GENERAL_CTL, 24, 25), + [K3RTC_PEND] = REG_FIELD(REG_K3RTC_SYNCPEND, 0, 1), + [K3RTC_RELOAD_FROM_BBD] = REG_FIELD(REG_K3RTC_SYNCPEND, 31, 31), + [K3RTC_COMP] = REG_FIELD(REG_K3RTC_COMP, 0, 31), + + /* We use on to off as alarm trigger */ + [K3RTC_ALM_S_CNT_LSW] = REG_FIELD(REG_K3RTC_ON_OFF_S_CNT_LSW, 0, 31), + [K3RTC_ALM_S_CNT_MSW] = REG_FIELD(REG_K3RTC_ON_OFF_S_CNT_MSW, 0, 15), + [K3RTC_IRQ_STATUS_RAW] = REG_FIELD(REG_K3RTC_IRQSTATUS_RAW_SYS, 0, 0), + [K3RTC_IRQ_STATUS] = REG_FIELD(REG_K3RTC_IRQSTATUS_SYS, 0, 0), + [K3RTC_IRQ_ENABLE_SET] = REG_FIELD(REG_K3RTC_IRQENABLE_SET_SYS, 0, 0), + [K3RTC_IRQ_ENABLE_CLR] = REG_FIELD(REG_K3RTC_IRQENABLE_CLR_SYS, 0, 0), + /* Off to on is alternate */ + [K3RTC_IRQ_STATUS_ALT] = REG_FIELD(REG_K3RTC_IRQSTATUS_SYS, 1, 1), + [K3RTC_IRQ_ENABLE_CLR_ALT] = REG_FIELD(REG_K3RTC_IRQENABLE_CLR_SYS, 1, 1), +}; + +/** + * struct ti_k3_rtc - Private data for ti-k3-rtc + * @irq: IRQ + * @sync_timeout_us: data sync timeout period in uSec + * @rate_32k: 32k clock rate in Hz + * @rtc_dev: rtc device + * @regmap: rtc mmio regmap + * @r_fields: rtc register fields + * @soc: SoC compatible match data + */ +struct ti_k3_rtc { + unsigned int irq; + u32 sync_timeout_us; + unsigned long rate_32k; + struct rtc_device *rtc_dev; + struct regmap *regmap; + struct regmap_field *r_fields[K3_RTC_MAX_FIELDS]; + const struct ti_k3_rtc_soc_data *soc; +}; + +static int k3rtc_field_read(struct ti_k3_rtc *priv, enum ti_k3_rtc_fields f) +{ + int ret; + int val; + + ret = regmap_field_read(priv->r_fields[f], &val); + /* + * We shouldn't be seeing regmap fail on us for mmio reads + * This is possible if clock context fails, but that isn't the case for us + */ + if (WARN_ON_ONCE(ret)) + return ret; + return val; +} + +static void k3rtc_field_write(struct ti_k3_rtc *priv, enum ti_k3_rtc_fields f, u32 val) +{ + regmap_field_write(priv->r_fields[f], val); +} + +/** + * k3rtc_fence - Ensure a register sync took place between the two domains + * @priv: pointer to priv data + * + * Return: 0 if the sync took place, else returns -ETIMEDOUT + */ +static int k3rtc_fence(struct ti_k3_rtc *priv) +{ + int ret; + + ret = regmap_field_read_poll_timeout(priv->r_fields[K3RTC_PEND], ret, + !ret, 2, priv->sync_timeout_us); + + return ret; +} + +static inline int k3rtc_check_unlocked(struct ti_k3_rtc *priv) +{ + int ret; + + ret = k3rtc_field_read(priv, K3RTC_UNLOCK); + if (ret < 0) + return ret; + + return (ret) ? 0 : 1; +} + +static int k3rtc_unlock_rtc(struct ti_k3_rtc *priv) +{ + int ret; + + ret = k3rtc_check_unlocked(priv); + if (!ret) + return ret; + + k3rtc_field_write(priv, K3RTC_KICK0, K3RTC_KICK0_UNLOCK_VALUE); + k3rtc_field_write(priv, K3RTC_KICK1, K3RTC_KICK1_UNLOCK_VALUE); + + /* Skip fence since we are going to check the unlock bit as fence */ + ret = regmap_field_read_poll_timeout(priv->r_fields[K3RTC_UNLOCK], ret, + !ret, 2, priv->sync_timeout_us); + + return ret; +} + +static int k3rtc_configure(struct device *dev) +{ + int ret; + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + + /* + * HWBUG: The compare state machine is broken if the RTC module + * is NOT unlocked in under one second of boot - which is pretty long + * time from the perspective of Linux driver (module load, u-boot + * shell all can take much longer than this. + * + * In such occurrence, it is assumed that the RTC module is unusable + */ + if (priv->soc->unlock_irq_erratum) { + ret = k3rtc_check_unlocked(priv); + /* If there is an error OR if we are locked, return error */ + if (ret) { + dev_err(dev, + HW_ERR "Erratum i2327 unlock QUIRK! Cannot operate!!\n"); + return -EFAULT; + } + } else { + /* May need to explicitly unlock first time */ + ret = k3rtc_unlock_rtc(priv); + if (ret) { + dev_err(dev, "Failed to unlock(%d)!\n", ret); + return ret; + } + } + + /* Enable Shadow register sync on 32k clock boundary */ + k3rtc_field_write(priv, K3RTC_O32K_OSC_DEP_EN, 0x1); + + /* + * Wait at least clock sync time before proceeding further programming. + * This ensures that the 32k based sync is active. + */ + usleep_range(priv->sync_timeout_us, priv->sync_timeout_us + 5); + + /* We need to ensure fence here to make sure sync here */ + ret = k3rtc_fence(priv); + if (ret) { + dev_err(dev, + "Failed fence osc_dep enable(%d) - is 32k clk working?!\n", ret); + return ret; + } + + /* + * FMODE setting: Reading lower seconds will freeze value on higher + * seconds. This also implies that we must *ALWAYS* read lower seconds + * prior to reading higher seconds + */ + k3rtc_field_write(priv, K3RTC_CNT_FMODE, K3RTC_CNT_FMODE_S_CNT_VALUE); + + /* Clear any spurious IRQ sources if any */ + k3rtc_field_write(priv, K3RTC_IRQ_STATUS_ALT, 0x1); + k3rtc_field_write(priv, K3RTC_IRQ_STATUS, 0x1); + /* Disable all IRQs */ + k3rtc_field_write(priv, K3RTC_IRQ_ENABLE_CLR_ALT, 0x1); + k3rtc_field_write(priv, K3RTC_IRQ_ENABLE_CLR, 0x1); + + /* And.. Let us Sync the writes in */ + return k3rtc_fence(priv); +} + +static int ti_k3_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 seconds_lo, seconds_hi; + + seconds_lo = k3rtc_field_read(priv, K3RTC_S_CNT_LSW); + seconds_hi = k3rtc_field_read(priv, K3RTC_S_CNT_MSW); + + rtc_time64_to_tm((((time64_t)seconds_hi) << 32) | (time64_t)seconds_lo, tm); + + return 0; +} + +static int ti_k3_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + time64_t seconds; + + seconds = rtc_tm_to_time64(tm); + + /* + * Read operation on LSW will freeze the RTC, so to update + * the time, we cannot use field operations. Just write since the + * reserved bits are ignored. + */ + regmap_write(priv->regmap, REG_K3RTC_S_CNT_LSW, seconds); + regmap_write(priv->regmap, REG_K3RTC_S_CNT_MSW, seconds >> 32); + + return k3rtc_fence(priv); +} + +static int ti_k3_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 reg; + u32 offset = enabled ? K3RTC_IRQ_ENABLE_SET : K3RTC_IRQ_ENABLE_CLR; + + reg = k3rtc_field_read(priv, K3RTC_IRQ_ENABLE_SET); + if ((enabled && reg) || (!enabled && !reg)) + return 0; + + k3rtc_field_write(priv, offset, 0x1); + + /* + * Ensure the write sync is through - NOTE: it should be OK to have + * ISR to fire as we are checking sync (which should be done in a 32k + * cycle or so). + */ + return k3rtc_fence(priv); +} + +static int ti_k3_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 seconds_lo, seconds_hi; + + seconds_lo = k3rtc_field_read(priv, K3RTC_ALM_S_CNT_LSW); + seconds_hi = k3rtc_field_read(priv, K3RTC_ALM_S_CNT_MSW); + + rtc_time64_to_tm((((time64_t)seconds_hi) << 32) | (time64_t)seconds_lo, &alarm->time); + + alarm->enabled = k3rtc_field_read(priv, K3RTC_IRQ_ENABLE_SET); + + return 0; +} + +static int ti_k3_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + time64_t seconds; + int ret; + + seconds = rtc_tm_to_time64(&alarm->time); + + k3rtc_field_write(priv, K3RTC_ALM_S_CNT_LSW, seconds); + k3rtc_field_write(priv, K3RTC_ALM_S_CNT_MSW, (seconds >> 32)); + + /* Make sure the alarm time is synced in */ + ret = k3rtc_fence(priv); + if (ret) { + dev_err(dev, "Failed to fence(%d)! Potential config issue?\n", ret); + return ret; + } + + /* Alarm IRQ enable will do a sync */ + return ti_k3_rtc_alarm_irq_enable(dev, alarm->enabled); +} + +static int ti_k3_rtc_read_offset(struct device *dev, long *offset) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 ticks_per_hr = priv->rate_32k * 3600; + int comp; + s64 tmp; + + comp = k3rtc_field_read(priv, K3RTC_COMP); + + /* Convert from RTC calibration register format to ppb format */ + tmp = comp * (s64)K3RTC_PPB_MULT; + if (tmp < 0) + tmp -= ticks_per_hr / 2LL; + else + tmp += ticks_per_hr / 2LL; + tmp = div_s64(tmp, ticks_per_hr); + + /* Offset value operates in negative way, so swap sign */ + *offset = (long)-tmp; + + return 0; +} + +static int ti_k3_rtc_set_offset(struct device *dev, long offset) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 ticks_per_hr = priv->rate_32k * 3600; + int comp; + s64 tmp; + + /* Make sure offset value is within supported range */ + if (offset < K3RTC_MIN_OFFSET || offset > K3RTC_MAX_OFFSET) + return -ERANGE; + + /* Convert from ppb format to RTC calibration register format */ + tmp = offset * (s64)ticks_per_hr; + if (tmp < 0) + tmp -= K3RTC_PPB_MULT / 2LL; + else + tmp += K3RTC_PPB_MULT / 2LL; + tmp = div_s64(tmp, K3RTC_PPB_MULT); + + /* Offset value operates in negative way, so swap sign */ + comp = (int)-tmp; + + k3rtc_field_write(priv, K3RTC_COMP, comp); + + return k3rtc_fence(priv); +} + +static irqreturn_t ti_k3_rtc_interrupt(s32 irq, void *dev_id) +{ + struct device *dev = dev_id; + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 reg; + int ret; + + /* + * IRQ assertion can be very fast, however, the IRQ Status clear + * de-assert depends on 32k clock edge in the 32k domain + * If we clear the status prior to the first 32k clock edge, + * the status bit is cleared, but the IRQ stays re-asserted. + * + * To prevent this condition, we need to wait for clock sync time. + * We can either do that by polling the 32k observability signal for + * a toggle OR we could just sleep and let the processor do other + * stuff. + */ + usleep_range(priv->sync_timeout_us, priv->sync_timeout_us + 2); + + /* Lets make sure that this is a valid interrupt */ + reg = k3rtc_field_read(priv, K3RTC_IRQ_STATUS); + + if (!reg) { + u32 raw = k3rtc_field_read(priv, K3RTC_IRQ_STATUS_RAW); + + dev_err(dev, + HW_ERR + "Erratum i2327/IRQ trig: status: 0x%08x / 0x%08x\n", reg, raw); + return IRQ_NONE; + } + + /* + * Write 1 to clear status reg + * We cannot use a field operation here due to a potential race between + * 32k domain and vbus domain. + */ + regmap_write(priv->regmap, REG_K3RTC_IRQSTATUS_SYS, 0x1); + + /* Sync the write in */ + ret = k3rtc_fence(priv); + if (ret) { + dev_err(dev, "Failed to fence irq status clr(%d)!\n", ret); + return IRQ_NONE; + } + + /* + * Force the 32k status to be reloaded back in to ensure status is + * reflected back correctly. + */ + k3rtc_field_write(priv, K3RTC_RELOAD_FROM_BBD, 0x1); + + /* Ensure the write sync is through */ + ret = k3rtc_fence(priv); + if (ret) { + dev_err(dev, "Failed to fence reload from bbd(%d)!\n", ret); + return IRQ_NONE; + } + + /* Now we ensure that the status bit is cleared */ + ret = regmap_field_read_poll_timeout(priv->r_fields[K3RTC_IRQ_STATUS], + ret, !ret, 2, priv->sync_timeout_us); + if (ret) { + dev_err(dev, "Time out waiting for status clear\n"); + return IRQ_NONE; + } + + /* Notify RTC core on event */ + rtc_update_irq(priv->rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops ti_k3_rtc_ops = { + .read_time = ti_k3_rtc_read_time, + .set_time = ti_k3_rtc_set_time, + .read_alarm = ti_k3_rtc_read_alarm, + .set_alarm = ti_k3_rtc_set_alarm, + .read_offset = ti_k3_rtc_read_offset, + .set_offset = ti_k3_rtc_set_offset, + .alarm_irq_enable = ti_k3_rtc_alarm_irq_enable, +}; + +static int ti_k3_rtc_scratch_read(void *priv_data, unsigned int offset, + void *val, size_t bytes) +{ + struct ti_k3_rtc *priv = (struct ti_k3_rtc *)priv_data; + + return regmap_bulk_read(priv->regmap, REG_K3RTC_SCRATCH0 + offset, val, bytes / 4); +} + +static int ti_k3_rtc_scratch_write(void *priv_data, unsigned int offset, + void *val, size_t bytes) +{ + struct ti_k3_rtc *priv = (struct ti_k3_rtc *)priv_data; + int ret; + + ret = regmap_bulk_write(priv->regmap, REG_K3RTC_SCRATCH0 + offset, val, bytes / 4); + if (ret) + return ret; + + return k3rtc_fence(priv); +} + +static struct nvmem_config ti_k3_rtc_nvmem_config = { + .name = "ti_k3_rtc_scratch", + .word_size = 4, + .stride = 4, + .size = REG_K3RTC_SCRATCH7 - REG_K3RTC_SCRATCH0 + 4, + .reg_read = ti_k3_rtc_scratch_read, + .reg_write = ti_k3_rtc_scratch_write, +}; + +static int k3rtc_get_32kclk(struct device *dev, struct ti_k3_rtc *priv) +{ + int ret; + struct clk *clk; + + clk = devm_clk_get(dev, "osc32k"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ret = clk_prepare_enable(clk); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, (void (*)(void *))clk_disable_unprepare, clk); + if (ret) + return ret; + + priv->rate_32k = clk_get_rate(clk); + + /* Make sure we are exact 32k clock. Else, try to compensate delay */ + if (priv->rate_32k != 32768) + dev_warn(dev, "Clock rate %ld is not 32768! Could misbehave!\n", + priv->rate_32k); + + /* + * Sync timeout should be two 32k clk sync cycles = ~61uS. We double + * it to comprehend intermediate bus segment and cpu frequency + * deltas + */ + priv->sync_timeout_us = (u32)(DIV_ROUND_UP_ULL(1000000, priv->rate_32k) * 4); + + return ret; +} + +static int k3rtc_get_vbusclk(struct device *dev, struct ti_k3_rtc *priv) +{ + int ret; + struct clk *clk; + + /* Note: VBUS isn't a context clock, it is needed for hardware operation */ + clk = devm_clk_get(dev, "vbus"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ret = clk_prepare_enable(clk); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, (void (*)(void *))clk_disable_unprepare, clk); +} + +static int ti_k3_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ti_k3_rtc *priv; + void __iomem *rtc_base; + int ret; + + priv = devm_kzalloc(dev, sizeof(struct ti_k3_rtc), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + rtc_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc_base)) + return PTR_ERR(rtc_base); + + priv->regmap = devm_regmap_init_mmio(dev, rtc_base, &ti_k3_rtc_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + ret = devm_regmap_field_bulk_alloc(dev, priv->regmap, priv->r_fields, + ti_rtc_reg_fields, K3_RTC_MAX_FIELDS); + if (ret) + return ret; + + ret = k3rtc_get_32kclk(dev, priv); + if (ret) + return ret; + ret = k3rtc_get_vbusclk(dev, priv); + if (ret) + return ret; + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + priv->irq = (unsigned int)ret; + + priv->rtc_dev = devm_rtc_allocate_device(dev); + if (IS_ERR(priv->rtc_dev)) + return PTR_ERR(priv->rtc_dev); + + priv->soc = of_device_get_match_data(dev); + + priv->rtc_dev->ops = &ti_k3_rtc_ops; + priv->rtc_dev->range_max = (1ULL << 48) - 1; /* 48Bit seconds */ + ti_k3_rtc_nvmem_config.priv = priv; + + ret = devm_request_threaded_irq(dev, priv->irq, NULL, + ti_k3_rtc_interrupt, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + dev_name(dev), dev); + if (ret) { + dev_err(dev, "Could not request IRQ: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, priv); + + ret = k3rtc_configure(dev); + if (ret) + return ret; + + if (device_property_present(dev, "wakeup-source")) + device_init_wakeup(dev, true); + else + device_set_wakeup_capable(dev, true); + + ret = devm_rtc_register_device(priv->rtc_dev); + if (ret) + return ret; + + return devm_rtc_nvmem_register(priv->rtc_dev, &ti_k3_rtc_nvmem_config); +} + +static const struct ti_k3_rtc_soc_data ti_k3_am62_data = { + .unlock_irq_erratum = true, +}; + +static const struct of_device_id ti_k3_rtc_of_match_table[] = { + {.compatible = "ti,am62-rtc", .data = &ti_k3_am62_data}, + {} +}; +MODULE_DEVICE_TABLE(of, ti_k3_rtc_of_match_table); + +static int __maybe_unused ti_k3_rtc_suspend(struct device *dev) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(priv->irq); + return 0; +} + +static int __maybe_unused ti_k3_rtc_resume(struct device *dev) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(priv->irq); + return 0; +} + +static SIMPLE_DEV_PM_OPS(ti_k3_rtc_pm_ops, ti_k3_rtc_suspend, ti_k3_rtc_resume); + +static struct platform_driver ti_k3_rtc_driver = { + .probe = ti_k3_rtc_probe, + .driver = { + .name = "rtc-ti-k3", + .of_match_table = ti_k3_rtc_of_match_table, + .pm = &ti_k3_rtc_pm_ops, + }, +}; +module_platform_driver(ti_k3_rtc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TI K3 RTC driver"); +MODULE_AUTHOR("Nishanth Menon"); diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c deleted file mode 100644 index 5a9f9ad86d32..000000000000 --- a/drivers/rtc/rtc-vr41xx.c +++ /dev/null @@ -1,363 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Driver for NEC VR4100 series Real Time Clock unit. - * - * Copyright (C) 2003-2008 Yoichi Yuasa <yuasa@linux-mips.org> - */ -#include <linux/compat.h> -#include <linux/err.h> -#include <linux/fs.h> -#include <linux/init.h> -#include <linux/io.h> -#include <linux/ioport.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/rtc.h> -#include <linux/spinlock.h> -#include <linux/types.h> -#include <linux/uaccess.h> -#include <linux/log2.h> - -#include <asm/div64.h> - -MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>"); -MODULE_DESCRIPTION("NEC VR4100 series RTC driver"); -MODULE_LICENSE("GPL v2"); - -/* RTC 1 registers */ -#define ETIMELREG 0x00 -#define ETIMEMREG 0x02 -#define ETIMEHREG 0x04 -/* RFU */ -#define ECMPLREG 0x08 -#define ECMPMREG 0x0a -#define ECMPHREG 0x0c -/* RFU */ -#define RTCL1LREG 0x10 -#define RTCL1HREG 0x12 -#define RTCL1CNTLREG 0x14 -#define RTCL1CNTHREG 0x16 -#define RTCL2LREG 0x18 -#define RTCL2HREG 0x1a -#define RTCL2CNTLREG 0x1c -#define RTCL2CNTHREG 0x1e - -/* RTC 2 registers */ -#define TCLKLREG 0x00 -#define TCLKHREG 0x02 -#define TCLKCNTLREG 0x04 -#define TCLKCNTHREG 0x06 -/* RFU */ -#define RTCINTREG 0x1e - #define TCLOCK_INT 0x08 - #define RTCLONG2_INT 0x04 - #define RTCLONG1_INT 0x02 - #define ELAPSEDTIME_INT 0x01 - -#define RTC_FREQUENCY 32768 -#define MAX_PERIODIC_RATE 6553 - -static void __iomem *rtc1_base; -static void __iomem *rtc2_base; - -#define rtc1_read(offset) readw(rtc1_base + (offset)) -#define rtc1_write(offset, value) writew((value), rtc1_base + (offset)) - -#define rtc2_read(offset) readw(rtc2_base + (offset)) -#define rtc2_write(offset, value) writew((value), rtc2_base + (offset)) - -/* 32-bit compat for ioctls that nobody else uses */ -#define RTC_EPOCH_READ32 _IOR('p', 0x0d, __u32) - -static unsigned long epoch = 1970; /* Jan 1 1970 00:00:00 */ - -static DEFINE_SPINLOCK(rtc_lock); -static char rtc_name[] = "RTC"; -static unsigned long periodic_count; -static unsigned int alarm_enabled; -static int aie_irq; -static int pie_irq; - -static inline time64_t read_elapsed_second(void) -{ - - unsigned long first_low, first_mid, first_high; - - unsigned long second_low, second_mid, second_high; - - do { - first_low = rtc1_read(ETIMELREG); - first_mid = rtc1_read(ETIMEMREG); - first_high = rtc1_read(ETIMEHREG); - second_low = rtc1_read(ETIMELREG); - second_mid = rtc1_read(ETIMEMREG); - second_high = rtc1_read(ETIMEHREG); - } while (first_low != second_low || first_mid != second_mid || - first_high != second_high); - - return ((u64)first_high << 17) | (first_mid << 1) | (first_low >> 15); -} - -static inline void write_elapsed_second(time64_t sec) -{ - spin_lock_irq(&rtc_lock); - - rtc1_write(ETIMELREG, (uint16_t)(sec << 15)); - rtc1_write(ETIMEMREG, (uint16_t)(sec >> 1)); - rtc1_write(ETIMEHREG, (uint16_t)(sec >> 17)); - - spin_unlock_irq(&rtc_lock); -} - -static int vr41xx_rtc_read_time(struct device *dev, struct rtc_time *time) -{ - time64_t epoch_sec, elapsed_sec; - - epoch_sec = mktime64(epoch, 1, 1, 0, 0, 0); - elapsed_sec = read_elapsed_second(); - - rtc_time64_to_tm(epoch_sec + elapsed_sec, time); - - return 0; -} - -static int vr41xx_rtc_set_time(struct device *dev, struct rtc_time *time) -{ - time64_t epoch_sec, current_sec; - - epoch_sec = mktime64(epoch, 1, 1, 0, 0, 0); - current_sec = rtc_tm_to_time64(time); - - write_elapsed_second(current_sec - epoch_sec); - - return 0; -} - -static int vr41xx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) -{ - unsigned long low, mid, high; - struct rtc_time *time = &wkalrm->time; - - spin_lock_irq(&rtc_lock); - - low = rtc1_read(ECMPLREG); - mid = rtc1_read(ECMPMREG); - high = rtc1_read(ECMPHREG); - wkalrm->enabled = alarm_enabled; - - spin_unlock_irq(&rtc_lock); - - rtc_time64_to_tm((high << 17) | (mid << 1) | (low >> 15), time); - - return 0; -} - -static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) -{ - time64_t alarm_sec; - - alarm_sec = rtc_tm_to_time64(&wkalrm->time); - - spin_lock_irq(&rtc_lock); - - if (alarm_enabled) - disable_irq(aie_irq); - - rtc1_write(ECMPLREG, (uint16_t)(alarm_sec << 15)); - rtc1_write(ECMPMREG, (uint16_t)(alarm_sec >> 1)); - rtc1_write(ECMPHREG, (uint16_t)(alarm_sec >> 17)); - - if (wkalrm->enabled) - enable_irq(aie_irq); - - alarm_enabled = wkalrm->enabled; - - spin_unlock_irq(&rtc_lock); - - return 0; -} - -static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case RTC_EPOCH_READ: - return put_user(epoch, (unsigned long __user *)arg); -#ifdef CONFIG_64BIT - case RTC_EPOCH_READ32: - return put_user(epoch, (unsigned int __user *)arg); -#endif - case RTC_EPOCH_SET: - /* Doesn't support before 1900 */ - if (arg < 1900) - return -EINVAL; - epoch = arg; - break; - default: - return -ENOIOCTLCMD; - } - - return 0; -} - -static int vr41xx_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) -{ - spin_lock_irq(&rtc_lock); - if (enabled) { - if (!alarm_enabled) { - enable_irq(aie_irq); - alarm_enabled = 1; - } - } else { - if (alarm_enabled) { - disable_irq(aie_irq); - alarm_enabled = 0; - } - } - spin_unlock_irq(&rtc_lock); - return 0; -} - -static irqreturn_t elapsedtime_interrupt(int irq, void *dev_id) -{ - struct platform_device *pdev = (struct platform_device *)dev_id; - struct rtc_device *rtc = platform_get_drvdata(pdev); - - rtc2_write(RTCINTREG, ELAPSEDTIME_INT); - - rtc_update_irq(rtc, 1, RTC_AF); - - return IRQ_HANDLED; -} - -static irqreturn_t rtclong1_interrupt(int irq, void *dev_id) -{ - struct platform_device *pdev = (struct platform_device *)dev_id; - struct rtc_device *rtc = platform_get_drvdata(pdev); - unsigned long count = periodic_count; - - rtc2_write(RTCINTREG, RTCLONG1_INT); - - rtc1_write(RTCL1LREG, count); - rtc1_write(RTCL1HREG, count >> 16); - - rtc_update_irq(rtc, 1, RTC_PF); - - return IRQ_HANDLED; -} - -static const struct rtc_class_ops vr41xx_rtc_ops = { - .ioctl = vr41xx_rtc_ioctl, - .read_time = vr41xx_rtc_read_time, - .set_time = vr41xx_rtc_set_time, - .read_alarm = vr41xx_rtc_read_alarm, - .set_alarm = vr41xx_rtc_set_alarm, - .alarm_irq_enable = vr41xx_rtc_alarm_irq_enable, -}; - -static int rtc_probe(struct platform_device *pdev) -{ - struct resource *res; - struct rtc_device *rtc; - int retval; - - if (pdev->num_resources != 4) - return -EBUSY; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EBUSY; - - rtc1_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!rtc1_base) - return -EBUSY; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) { - retval = -EBUSY; - goto err_rtc1_iounmap; - } - - rtc2_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!rtc2_base) { - retval = -EBUSY; - goto err_rtc1_iounmap; - } - - rtc = devm_rtc_allocate_device(&pdev->dev); - if (IS_ERR(rtc)) { - retval = PTR_ERR(rtc); - goto err_iounmap_all; - } - - rtc->ops = &vr41xx_rtc_ops; - - /* 48-bit counter at 32.768 kHz */ - rtc->range_max = (1ULL << 33) - 1; - rtc->max_user_freq = MAX_PERIODIC_RATE; - - spin_lock_irq(&rtc_lock); - - rtc1_write(ECMPLREG, 0); - rtc1_write(ECMPMREG, 0); - rtc1_write(ECMPHREG, 0); - rtc1_write(RTCL1LREG, 0); - rtc1_write(RTCL1HREG, 0); - - spin_unlock_irq(&rtc_lock); - - aie_irq = platform_get_irq(pdev, 0); - if (aie_irq <= 0) { - retval = -EBUSY; - goto err_iounmap_all; - } - - retval = devm_request_irq(&pdev->dev, aie_irq, elapsedtime_interrupt, 0, - "elapsed_time", pdev); - if (retval < 0) - goto err_iounmap_all; - - pie_irq = platform_get_irq(pdev, 1); - if (pie_irq <= 0) { - retval = -EBUSY; - goto err_iounmap_all; - } - - retval = devm_request_irq(&pdev->dev, pie_irq, rtclong1_interrupt, 0, - "rtclong1", pdev); - if (retval < 0) - goto err_iounmap_all; - - platform_set_drvdata(pdev, rtc); - - disable_irq(aie_irq); - disable_irq(pie_irq); - - dev_info(&pdev->dev, "Real Time Clock of NEC VR4100 series\n"); - - retval = devm_rtc_register_device(rtc); - if (retval) - goto err_iounmap_all; - - return 0; - -err_iounmap_all: - rtc2_base = NULL; - -err_rtc1_iounmap: - rtc1_base = NULL; - - return retval; -} - -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:RTC"); - -static struct platform_driver rtc_platform_driver = { - .probe = rtc_probe, - .driver = { - .name = rtc_name, - }, -}; - -module_platform_driver(rtc_platform_driver); diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c index d1d5a44d9122..ba0d22a5b421 100644 --- a/drivers/rtc/rtc-x1205.c +++ b/drivers/rtc/rtc-x1205.c @@ -614,8 +614,7 @@ static void x1205_sysfs_unregister(struct device *dev) } -static int x1205_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int x1205_probe(struct i2c_client *client) { int err = 0; unsigned char sr; @@ -681,7 +680,7 @@ static struct i2c_driver x1205_driver = { .name = "rtc-x1205", .of_match_table = x1205_dt_ids, }, - .probe = x1205_probe, + .probe_new = x1205_probe, .remove = x1205_remove, .id_table = x1205_id, }; diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c index f440bb52be92..c9b85c838ebe 100644 --- a/drivers/rtc/rtc-zynqmp.c +++ b/drivers/rtc/rtc-zynqmp.c @@ -6,6 +6,7 @@ * */ +#include <linux/clk.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/io.h> @@ -36,17 +37,23 @@ #define RTC_OSC_EN BIT(24) #define RTC_BATT_EN BIT(31) -#define RTC_CALIB_DEF 0x198233 +#define RTC_CALIB_DEF 0x7FFF #define RTC_CALIB_MASK 0x1FFFFF #define RTC_ALRM_MASK BIT(1) #define RTC_MSEC 1000 +#define RTC_FR_MASK 0xF0000 +#define RTC_FR_MAX_TICKS 16 +#define RTC_PPB 1000000000LL +#define RTC_MIN_OFFSET -32768000 +#define RTC_MAX_OFFSET 32767000 struct xlnx_rtc_dev { struct rtc_device *rtc; void __iomem *reg_base; int alarm_irq; int sec_irq; - unsigned int calibval; + struct clk *rtc_clk; + unsigned int freq; }; static int xlnx_rtc_set_time(struct device *dev, struct rtc_time *tm) @@ -61,13 +68,6 @@ static int xlnx_rtc_set_time(struct device *dev, struct rtc_time *tm) */ new_time = rtc_tm_to_time64(tm) + 1; - /* - * Writing into calibration register will clear the Tick Counter and - * force the next second to be signaled exactly in 1 second period - */ - xrtcdev->calibval &= RTC_CALIB_MASK; - writel(xrtcdev->calibval, (xrtcdev->reg_base + RTC_CALIB_WR)); - writel(new_time, xrtcdev->reg_base + RTC_SET_TM_WR); /* @@ -173,15 +173,76 @@ static void xlnx_init_rtc(struct xlnx_rtc_dev *xrtcdev) rtc_ctrl = readl(xrtcdev->reg_base + RTC_CTRL); rtc_ctrl |= RTC_BATT_EN; writel(rtc_ctrl, xrtcdev->reg_base + RTC_CTRL); +} - /* - * Based on crystal freq of 33.330 KHz - * set the seconds counter and enable, set fractions counter - * to default value suggested as per design spec - * to correct RTC delay in frequency over period of time. +static int xlnx_rtc_read_offset(struct device *dev, long *offset) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + unsigned long long rtc_ppb = RTC_PPB; + unsigned int tick_mult = do_div(rtc_ppb, xrtcdev->freq); + unsigned int calibval; + long offset_val; + + calibval = readl(xrtcdev->reg_base + RTC_CALIB_RD); + /* Offset with seconds ticks */ + offset_val = calibval & RTC_TICK_MASK; + offset_val = offset_val - RTC_CALIB_DEF; + offset_val = offset_val * tick_mult; + + /* Offset with fractional ticks */ + if (calibval & RTC_FR_EN) + offset_val += ((calibval & RTC_FR_MASK) >> RTC_FR_DATSHIFT) + * (tick_mult / RTC_FR_MAX_TICKS); + *offset = offset_val; + + return 0; +} + +static int xlnx_rtc_set_offset(struct device *dev, long offset) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + unsigned long long rtc_ppb = RTC_PPB; + unsigned int tick_mult = do_div(rtc_ppb, xrtcdev->freq); + unsigned char fract_tick = 0; + unsigned int calibval; + short int max_tick; + int fract_offset; + + if (offset < RTC_MIN_OFFSET || offset > RTC_MAX_OFFSET) + return -ERANGE; + + /* Number ticks for given offset */ + max_tick = div_s64_rem(offset, tick_mult, &fract_offset); + + /* Number fractional ticks for given offset */ + if (fract_offset) { + if (fract_offset < 0) { + fract_offset = fract_offset + tick_mult; + max_tick--; + } + if (fract_offset > (tick_mult / RTC_FR_MAX_TICKS)) { + for (fract_tick = 1; fract_tick < 16; fract_tick++) { + if (fract_offset <= + (fract_tick * + (tick_mult / RTC_FR_MAX_TICKS))) + break; + } + } + } + + /* Zynqmp RTC uses second and fractional tick + * counters for compensation */ - xrtcdev->calibval &= RTC_CALIB_MASK; - writel(xrtcdev->calibval, (xrtcdev->reg_base + RTC_CALIB_WR)); + calibval = max_tick + RTC_CALIB_DEF; + + if (fract_tick) + calibval |= RTC_FR_EN; + + calibval |= (fract_tick << RTC_FR_DATSHIFT); + + writel(calibval, (xrtcdev->reg_base + RTC_CALIB_WR)); + + return 0; } static const struct rtc_class_ops xlnx_rtc_ops = { @@ -190,6 +251,8 @@ static const struct rtc_class_ops xlnx_rtc_ops = { .read_alarm = xlnx_rtc_read_alarm, .set_alarm = xlnx_rtc_set_alarm, .alarm_irq_enable = xlnx_rtc_alarm_irq_enable, + .read_offset = xlnx_rtc_read_offset, + .set_offset = xlnx_rtc_set_offset, }; static irqreturn_t xlnx_rtc_interrupt(int irq, void *id) @@ -255,10 +318,22 @@ static int xlnx_rtc_probe(struct platform_device *pdev) return ret; } - ret = of_property_read_u32(pdev->dev.of_node, "calibration", - &xrtcdev->calibval); - if (ret) - xrtcdev->calibval = RTC_CALIB_DEF; + /* Getting the rtc_clk info */ + xrtcdev->rtc_clk = devm_clk_get_optional(&pdev->dev, "rtc_clk"); + if (IS_ERR(xrtcdev->rtc_clk)) { + if (PTR_ERR(xrtcdev->rtc_clk) != -EPROBE_DEFER) + dev_warn(&pdev->dev, "Device clock not found.\n"); + } + xrtcdev->freq = clk_get_rate(xrtcdev->rtc_clk); + if (!xrtcdev->freq) { + ret = of_property_read_u32(pdev->dev.of_node, "calibration", + &xrtcdev->freq); + if (ret) + xrtcdev->freq = RTC_CALIB_DEF; + } + ret = readl(xrtcdev->reg_base + RTC_CALIB_RD); + if (!ret) + writel(xrtcdev->freq, (xrtcdev->reg_base + RTC_CALIB_WR)); xlnx_init_rtc(xrtcdev); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 35d4b398c197..8bd9fd51208c 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -763,6 +763,49 @@ static void qeth_issue_ipa_msg(struct qeth_ipa_cmd *cmd, int rc, ipa_name, com, CARD_DEVID(card)); } +static void qeth_default_link_info(struct qeth_card *card) +{ + struct qeth_link_info *link_info = &card->info.link_info; + + QETH_CARD_TEXT(card, 2, "dftlinfo"); + link_info->duplex = DUPLEX_FULL; + + if (IS_IQD(card) || IS_VM_NIC(card)) { + link_info->speed = SPEED_10000; + link_info->port = PORT_FIBRE; + link_info->link_mode = QETH_LINK_MODE_FIBRE_SHORT; + } else { + switch (card->info.link_type) { + case QETH_LINK_TYPE_FAST_ETH: + case QETH_LINK_TYPE_LANE_ETH100: + link_info->speed = SPEED_100; + link_info->port = PORT_TP; + break; + case QETH_LINK_TYPE_GBIT_ETH: + case QETH_LINK_TYPE_LANE_ETH1000: + link_info->speed = SPEED_1000; + link_info->port = PORT_FIBRE; + break; + case QETH_LINK_TYPE_10GBIT_ETH: + link_info->speed = SPEED_10000; + link_info->port = PORT_FIBRE; + break; + case QETH_LINK_TYPE_25GBIT_ETH: + link_info->speed = SPEED_25000; + link_info->port = PORT_FIBRE; + break; + default: + dev_info(&card->gdev->dev, + "Unknown link type %x\n", + card->info.link_type); + link_info->speed = SPEED_UNKNOWN; + link_info->port = PORT_OTHER; + } + + link_info->link_mode = QETH_LINK_MODE_UNKNOWN; + } +} + static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, struct qeth_ipa_cmd *cmd) { @@ -790,6 +833,7 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, netdev_name(card->dev), card->info.chpid); qeth_issue_ipa_msg(cmd, cmd->hdr.return_code, card); netif_carrier_off(card->dev); + qeth_default_link_info(card); } return NULL; case IPA_CMD_STARTLAN: @@ -4744,92 +4788,6 @@ out_free: return rc; } -static int qeth_query_card_info_cb(struct qeth_card *card, - struct qeth_reply *reply, unsigned long data) -{ - struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data; - struct qeth_link_info *link_info = reply->param; - struct qeth_query_card_info *card_info; - - QETH_CARD_TEXT(card, 2, "qcrdincb"); - if (qeth_setadpparms_inspect_rc(cmd)) - return -EIO; - - card_info = &cmd->data.setadapterparms.data.card_info; - netdev_dbg(card->dev, - "card info: card_type=0x%02x, port_mode=0x%04x, port_speed=0x%08x\n", - card_info->card_type, card_info->port_mode, - card_info->port_speed); - - switch (card_info->port_mode) { - case CARD_INFO_PORTM_FULLDUPLEX: - link_info->duplex = DUPLEX_FULL; - break; - case CARD_INFO_PORTM_HALFDUPLEX: - link_info->duplex = DUPLEX_HALF; - break; - default: - link_info->duplex = DUPLEX_UNKNOWN; - } - - switch (card_info->card_type) { - case CARD_INFO_TYPE_1G_COPPER_A: - case CARD_INFO_TYPE_1G_COPPER_B: - link_info->speed = SPEED_1000; - link_info->port = PORT_TP; - break; - case CARD_INFO_TYPE_1G_FIBRE_A: - case CARD_INFO_TYPE_1G_FIBRE_B: - link_info->speed = SPEED_1000; - link_info->port = PORT_FIBRE; - break; - case CARD_INFO_TYPE_10G_FIBRE_A: - case CARD_INFO_TYPE_10G_FIBRE_B: - link_info->speed = SPEED_10000; - link_info->port = PORT_FIBRE; - break; - default: - switch (card_info->port_speed) { - case CARD_INFO_PORTS_10M: - link_info->speed = SPEED_10; - break; - case CARD_INFO_PORTS_100M: - link_info->speed = SPEED_100; - break; - case CARD_INFO_PORTS_1G: - link_info->speed = SPEED_1000; - break; - case CARD_INFO_PORTS_10G: - link_info->speed = SPEED_10000; - break; - case CARD_INFO_PORTS_25G: - link_info->speed = SPEED_25000; - break; - default: - link_info->speed = SPEED_UNKNOWN; - } - - link_info->port = PORT_OTHER; - } - - return 0; -} - -int qeth_query_card_info(struct qeth_card *card, - struct qeth_link_info *link_info) -{ - struct qeth_cmd_buffer *iob; - - QETH_CARD_TEXT(card, 2, "qcrdinfo"); - if (!qeth_adp_supported(card, IPA_SETADP_QUERY_CARD_INFO)) - return -EOPNOTSUPP; - iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_CARD_INFO, 0); - if (!iob) - return -ENOMEM; - - return qeth_send_ipa_cmd(card, iob, qeth_query_card_info_cb, link_info); -} - static int qeth_init_link_info_oat_cb(struct qeth_card *card, struct qeth_reply *reply_priv, unsigned long data) @@ -4839,6 +4797,7 @@ static int qeth_init_link_info_oat_cb(struct qeth_card *card, struct qeth_query_oat_physical_if *phys_if; struct qeth_query_oat_reply *reply; + QETH_CARD_TEXT(card, 2, "qoatincb"); if (qeth_setadpparms_inspect_rc(cmd)) return -EIO; @@ -4918,41 +4877,7 @@ static int qeth_init_link_info_oat_cb(struct qeth_card *card, static void qeth_init_link_info(struct qeth_card *card) { - card->info.link_info.duplex = DUPLEX_FULL; - - if (IS_IQD(card) || IS_VM_NIC(card)) { - card->info.link_info.speed = SPEED_10000; - card->info.link_info.port = PORT_FIBRE; - card->info.link_info.link_mode = QETH_LINK_MODE_FIBRE_SHORT; - } else { - switch (card->info.link_type) { - case QETH_LINK_TYPE_FAST_ETH: - case QETH_LINK_TYPE_LANE_ETH100: - card->info.link_info.speed = SPEED_100; - card->info.link_info.port = PORT_TP; - break; - case QETH_LINK_TYPE_GBIT_ETH: - case QETH_LINK_TYPE_LANE_ETH1000: - card->info.link_info.speed = SPEED_1000; - card->info.link_info.port = PORT_FIBRE; - break; - case QETH_LINK_TYPE_10GBIT_ETH: - card->info.link_info.speed = SPEED_10000; - card->info.link_info.port = PORT_FIBRE; - break; - case QETH_LINK_TYPE_25GBIT_ETH: - card->info.link_info.speed = SPEED_25000; - card->info.link_info.port = PORT_FIBRE; - break; - default: - dev_info(&card->gdev->dev, "Unknown link type %x\n", - card->info.link_type); - card->info.link_info.speed = SPEED_UNKNOWN; - card->info.link_info.port = PORT_OTHER; - } - - card->info.link_info.link_mode = QETH_LINK_MODE_UNKNOWN; - } + qeth_default_link_info(card); /* Get more accurate data via QUERY OAT: */ if (qeth_adp_supported(card, IPA_SETADP_QUERY_OAT)) { @@ -5461,6 +5386,7 @@ int qeth_set_offline(struct qeth_card *card, const struct qeth_discipline *disc, qeth_clear_working_pool_list(card); qeth_flush_local_addrs(card); card->info.promisc_mode = 0; + qeth_default_link_info(card); rc = qeth_stop_channel(&card->data); rc2 = qeth_stop_channel(&card->write); diff --git a/drivers/s390/net/qeth_ethtool.c b/drivers/s390/net/qeth_ethtool.c index b0b36b2132fe..9eba0a32e9f9 100644 --- a/drivers/s390/net/qeth_ethtool.c +++ b/drivers/s390/net/qeth_ethtool.c @@ -428,8 +428,8 @@ static int qeth_get_link_ksettings(struct net_device *netdev, struct ethtool_link_ksettings *cmd) { struct qeth_card *card = netdev->ml_priv; - struct qeth_link_info link_info; + QETH_CARD_TEXT(card, 4, "ethtglks"); cmd->base.speed = card->info.link_info.speed; cmd->base.duplex = card->info.link_info.duplex; cmd->base.port = card->info.link_info.port; @@ -439,16 +439,6 @@ static int qeth_get_link_ksettings(struct net_device *netdev, cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID; cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; - /* Check if we can obtain more accurate information. */ - if (!qeth_query_card_info(card, &link_info)) { - if (link_info.speed != SPEED_UNKNOWN) - cmd->base.speed = link_info.speed; - if (link_info.duplex != DUPLEX_UNKNOWN) - cmd->base.duplex = link_info.duplex; - if (link_info.port != PORT_OTHER) - cmd->base.port = link_info.port; - } - qeth_set_ethtool_link_modes(cmd, card->info.link_info.link_mode); return 0; diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index 511bf8e0a436..b61acbb09be3 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -145,27 +145,33 @@ void zfcp_fc_enqueue_event(struct zfcp_adapter *adapter, static int zfcp_fc_wka_port_get(struct zfcp_fc_wka_port *wka_port) { + int ret = -EIO; + if (mutex_lock_interruptible(&wka_port->mutex)) return -ERESTARTSYS; if (wka_port->status == ZFCP_FC_WKA_PORT_OFFLINE || wka_port->status == ZFCP_FC_WKA_PORT_CLOSING) { wka_port->status = ZFCP_FC_WKA_PORT_OPENING; - if (zfcp_fsf_open_wka_port(wka_port)) + if (zfcp_fsf_open_wka_port(wka_port)) { + /* could not even send request, nothing to wait for */ wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; + goto out; + } } - mutex_unlock(&wka_port->mutex); - - wait_event(wka_port->completion_wq, + wait_event(wka_port->opened, wka_port->status == ZFCP_FC_WKA_PORT_ONLINE || wka_port->status == ZFCP_FC_WKA_PORT_OFFLINE); if (wka_port->status == ZFCP_FC_WKA_PORT_ONLINE) { atomic_inc(&wka_port->refcount); - return 0; + ret = 0; + goto out; } - return -EIO; +out: + mutex_unlock(&wka_port->mutex); + return ret; } static void zfcp_fc_wka_port_offline(struct work_struct *work) @@ -181,9 +187,12 @@ static void zfcp_fc_wka_port_offline(struct work_struct *work) wka_port->status = ZFCP_FC_WKA_PORT_CLOSING; if (zfcp_fsf_close_wka_port(wka_port)) { + /* could not even send request, nothing to wait for */ wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; - wake_up(&wka_port->completion_wq); + goto out; } + wait_event(wka_port->closed, + wka_port->status == ZFCP_FC_WKA_PORT_OFFLINE); out: mutex_unlock(&wka_port->mutex); } @@ -193,13 +202,15 @@ static void zfcp_fc_wka_port_put(struct zfcp_fc_wka_port *wka_port) if (atomic_dec_return(&wka_port->refcount) != 0) return; /* wait 10 milliseconds, other reqs might pop in */ - schedule_delayed_work(&wka_port->work, HZ / 100); + queue_delayed_work(wka_port->adapter->work_queue, &wka_port->work, + msecs_to_jiffies(10)); } static void zfcp_fc_wka_port_init(struct zfcp_fc_wka_port *wka_port, u32 d_id, struct zfcp_adapter *adapter) { - init_waitqueue_head(&wka_port->completion_wq); + init_waitqueue_head(&wka_port->opened); + init_waitqueue_head(&wka_port->closed); wka_port->adapter = adapter; wka_port->d_id = d_id; diff --git a/drivers/s390/scsi/zfcp_fc.h b/drivers/s390/scsi/zfcp_fc.h index 8aaf409ce9cb..97755407ce1b 100644 --- a/drivers/s390/scsi/zfcp_fc.h +++ b/drivers/s390/scsi/zfcp_fc.h @@ -185,7 +185,8 @@ enum zfcp_fc_wka_status { /** * struct zfcp_fc_wka_port - representation of well-known-address (WKA) FC port * @adapter: Pointer to adapter structure this WKA port belongs to - * @completion_wq: Wait for completion of open/close command + * @opened: Wait for completion of open command + * @closed: Wait for completion of close command * @status: Current status of WKA port * @refcount: Reference count to keep port open as long as it is in use * @d_id: FC destination id or well-known-address @@ -195,7 +196,8 @@ enum zfcp_fc_wka_status { */ struct zfcp_fc_wka_port { struct zfcp_adapter *adapter; - wait_queue_head_t completion_wq; + wait_queue_head_t opened; + wait_queue_head_t closed; enum zfcp_fc_wka_status status; atomic_t refcount; u32 d_id; diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 4f1e4385ce58..19223b075568 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1907,7 +1907,7 @@ static void zfcp_fsf_open_wka_port_handler(struct zfcp_fsf_req *req) wka_port->status = ZFCP_FC_WKA_PORT_ONLINE; } out: - wake_up(&wka_port->completion_wq); + wake_up(&wka_port->opened); } /** @@ -1966,7 +1966,7 @@ static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req) } wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE; - wake_up(&wka_port->completion_wq); + wake_up(&wka_port->closed); } /** diff --git a/drivers/s390/virtio/virtio_ccw.c b/drivers/s390/virtio/virtio_ccw.c index aa96f67dd0b1..896896e32664 100644 --- a/drivers/s390/virtio/virtio_ccw.c +++ b/drivers/s390/virtio/virtio_ccw.c @@ -532,6 +532,9 @@ static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev, err = -ENOMEM; goto out_err; } + + vq->num_max = info->num; + /* it may have been reduced */ info->num = virtqueue_get_vring_size(vq); @@ -634,6 +637,7 @@ static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char * const names[], + u32 sizes[], const bool *ctx, struct irq_affinity *desc) { diff --git a/drivers/scsi/FlashPoint.c b/drivers/scsi/FlashPoint.c index 90253208a72f..3d9c56ac8224 100644 --- a/drivers/scsi/FlashPoint.c +++ b/drivers/scsi/FlashPoint.c @@ -1712,7 +1712,7 @@ static unsigned char FlashPoint_InterruptPending(void *pCurrCard) static int FlashPoint_HandleInterrupt(void *pcard) { struct sccb *currSCCB; - unsigned char thisCard, result, bm_status, bm_int_st; + unsigned char thisCard, result, bm_status; unsigned short hp_int; unsigned char i, target; struct sccb_card *pCurrCard = pcard; @@ -1723,7 +1723,7 @@ static int FlashPoint_HandleInterrupt(void *pcard) MDISABLE_INT(ioport); - if ((bm_int_st = RD_HARPOON(ioport + hp_int_status)) & EXT_STATUS_ON) + if (RD_HARPOON(ioport + hp_int_status) & EXT_STATUS_ON) bm_status = RD_HARPOON(ioport + hp_ext_status) & (unsigned char)BAD_EXT_STATUS; else diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 26bf3b153595..0738238ed6cc 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -190,6 +190,15 @@ void scsi_remove_host(struct Scsi_Host *shost) transport_unregister_device(&shost->shost_gendev); device_unregister(&shost->shost_dev); device_del(&shost->shost_gendev); + + /* + * After scsi_remove_host() has returned the scsi LLD module can be + * unloaded and/or the host resources can be released. Hence wait until + * the dependent SCSI targets and devices are gone before returning. + */ + wait_event(shost->targets_wq, atomic_read(&shost->target_count) == 0); + + scsi_mq_destroy_tags(shost); } EXPORT_SYMBOL(scsi_remove_host); @@ -300,8 +309,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, return error; /* - * Any host allocation in this function will be freed in - * scsi_host_dev_release(). + * Any resources associated with the SCSI host in this function except + * the tag set will be freed by scsi_host_dev_release(). */ out_del_dev: device_del(&shost->shost_dev); @@ -317,6 +326,7 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, pm_runtime_disable(&shost->shost_gendev); pm_runtime_set_suspended(&shost->shost_gendev); pm_runtime_put_noidle(&shost->shost_gendev); + scsi_mq_destroy_tags(shost); fail: return error; } @@ -350,9 +360,6 @@ static void scsi_host_dev_release(struct device *dev) kfree(dev_name(&shost->shost_dev)); } - if (shost->tag_set.tags) - scsi_mq_destroy_tags(shost); - kfree(shost->shost_data); ida_free(&host_index_ida, shost->host_no); @@ -399,6 +406,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) INIT_LIST_HEAD(&shost->starved_list); init_waitqueue_head(&shost->host_wait); mutex_init(&shost->scan_mutex); + init_waitqueue_head(&shost->targets_wq); index = ida_alloc(&host_index_ida, GFP_KERNEL); if (index < 0) { diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 4a0eadd1c22c..c69c5a0979ec 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -7948,6 +7948,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) /* The lpfc_wq workqueue for deferred irq use */ phba->wq = alloc_workqueue("lpfc_wq", WQ_MEM_RECLAIM, 0); + if (!phba->wq) + return -ENOMEM; /* * Initialize timers used by driver diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 5b5885d9732b..e48d4261d0bc 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -3199,7 +3199,6 @@ megasas_build_io_fusion(struct megasas_instance *instance, struct megasas_cmd_fusion *cmd) { int sge_count; - u8 cmd_type; u16 pd_index = 0; u8 drive_type = 0; struct MPI2_RAID_SCSI_IO_REQUEST *io_request = cmd->io_request; @@ -3225,7 +3224,7 @@ megasas_build_io_fusion(struct megasas_instance *instance, */ io_request->IoFlags = cpu_to_le16(scp->cmd_len); - switch (cmd_type = megasas_cmd_type(scp)) { + switch (megasas_cmd_type(scp)) { case READ_WRITE_LDIO: megasas_build_ldio_fusion(instance, scp, cmd); break; diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index 4acaff479916..91d78d0a38fe 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -3138,7 +3138,7 @@ int pm8001_mpi_local_phy_ctl(struct pm8001_hba_info *pm8001_ha, void *piomb) * * when HBA driver received the identify done event or initiate FIS received * event(for SATA), it will invoke this function to notify the sas layer that - * the sas toplogy has formed, please discover the the whole sas domain, + * the sas toplogy has formed, please discover the whole sas domain, * while receive a broadcast(change) primitive just tell the sas * layer to discover the changed domain rather than the whole domain. */ diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index c59eac7a32f2..086ec5b5862d 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -586,10 +586,13 @@ EXPORT_SYMBOL(scsi_device_get); */ void scsi_device_put(struct scsi_device *sdev) { - struct module *mod = sdev->host->hostt->module; - + /* + * Decreasing the module reference count before the device reference + * count is safe since scsi_remove_host() only returns after all + * devices have been removed. + */ + module_put(sdev->host->hostt->module); put_device(&sdev->sdev_gendev); - module_put(mod); } EXPORT_SYMBOL(scsi_device_put); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 91ac901a6682..ac6059702d13 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -406,9 +406,14 @@ static void scsi_target_destroy(struct scsi_target *starget) static void scsi_target_dev_release(struct device *dev) { struct device *parent = dev->parent; + struct Scsi_Host *shost = dev_to_shost(parent); struct scsi_target *starget = to_scsi_target(dev); kfree(starget); + + if (atomic_dec_return(&shost->target_count) == 0) + wake_up(&shost->targets_wq); + put_device(parent); } @@ -521,6 +526,10 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, starget->state = STARGET_CREATED; starget->scsi_level = SCSI_2; starget->max_target_blocked = SCSI_DEFAULT_TARGET_BLOCKED; + init_waitqueue_head(&starget->sdev_wq); + + atomic_inc(&shost->target_count); + retry: spin_lock_irqsave(shost->host_lock, flags); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index aa70d9282161..9dad2fd5297f 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -443,18 +443,15 @@ static void scsi_device_cls_release(struct device *class_dev) static void scsi_device_dev_release_usercontext(struct work_struct *work) { - struct scsi_device *sdev; + struct scsi_device *sdev = container_of(work, struct scsi_device, + ew.work); + struct scsi_target *starget = sdev->sdev_target; struct device *parent; struct list_head *this, *tmp; struct scsi_vpd *vpd_pg80 = NULL, *vpd_pg83 = NULL; struct scsi_vpd *vpd_pg0 = NULL, *vpd_pg89 = NULL; struct scsi_vpd *vpd_pgb0 = NULL, *vpd_pgb1 = NULL, *vpd_pgb2 = NULL; unsigned long flags; - struct module *mod; - - sdev = container_of(work, struct scsi_device, ew.work); - - mod = sdev->host->hostt->module; scsi_dh_release_device(sdev); @@ -516,19 +513,16 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work) kfree(sdev->inquiry); kfree(sdev); + if (starget && atomic_dec_return(&starget->sdev_count) == 0) + wake_up(&starget->sdev_wq); + if (parent) put_device(parent); - module_put(mod); } static void scsi_device_dev_release(struct device *dev) { struct scsi_device *sdp = to_scsi_device(dev); - - /* Set module pointer as NULL in case of module unloading */ - if (!try_module_get(sdp->host->hostt->module)) - sdp->host->hostt->module = NULL; - execute_in_process_context(scsi_device_dev_release_usercontext, &sdp->ew); } @@ -1535,6 +1529,14 @@ static void __scsi_remove_target(struct scsi_target *starget) goto restart; } spin_unlock_irqrestore(shost->host_lock, flags); + + /* + * After scsi_remove_target() returns its caller can remove resources + * associated with @starget, e.g. an rport or session. Wait until all + * devices associated with @starget have been removed to prevent that + * a SCSI error handling callback function triggers a use-after-free. + */ + wait_event(starget->sdev_wq, atomic_read(&starget->sdev_count) == 0); } /** @@ -1645,6 +1647,9 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev) list_add_tail(&sdev->same_target_siblings, &starget->devices); list_add_tail(&sdev->siblings, &shost->__devices); spin_unlock_irqrestore(shost->host_lock, flags); + + atomic_inc(&starget->sdev_count); + /* * device can now only be removed via __scsi_remove_device() so hold * the target. Target will be held in CREATED state until something diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index 58df0145e8d0..fb91423a4e2e 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -934,8 +934,7 @@ static void core_alua_queue_state_change_ua(struct t10_alua_tg_pt_gp *tg_pt_gp) spin_lock(&lun->lun_deve_lock); list_for_each_entry(se_deve, &lun->lun_deve_list, lun_link) { - lacl = rcu_dereference_check(se_deve->se_lun_acl, - lockdep_is_held(&lun->lun_deve_lock)); + lacl = se_deve->se_lun_acl; /* * spc4r37 p.242: diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 086ac9c9343c..b7f16ee8aa0e 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -75,7 +75,7 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd) return TCM_WRITE_PROTECTED; } - se_lun = rcu_dereference(deve->se_lun); + se_lun = deve->se_lun; if (!percpu_ref_tryget_live(&se_lun->lun_ref)) { se_lun = NULL; @@ -152,7 +152,7 @@ int transport_lookup_tmr_lun(struct se_cmd *se_cmd) rcu_read_lock(); deve = target_nacl_find_deve(nacl, se_cmd->orig_fe_lun); if (deve) { - se_lun = rcu_dereference(deve->se_lun); + se_lun = deve->se_lun; if (!percpu_ref_tryget_live(&se_lun->lun_ref)) { se_lun = NULL; @@ -216,7 +216,7 @@ struct se_dev_entry *core_get_se_deve_from_rtpi( rcu_read_lock(); hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link) { - lun = rcu_dereference(deve->se_lun); + lun = deve->se_lun; if (!lun) { pr_err("%s device entries device pointer is" " NULL, but Initiator has access.\n", @@ -243,11 +243,8 @@ void core_free_device_list_for_node( struct se_dev_entry *deve; mutex_lock(&nacl->lun_entry_mutex); - hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link) { - struct se_lun *lun = rcu_dereference_check(deve->se_lun, - lockdep_is_held(&nacl->lun_entry_mutex)); - core_disable_device_list_for_node(lun, deve, nacl, tpg); - } + hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link) + core_disable_device_list_for_node(deve->se_lun, deve, nacl, tpg); mutex_unlock(&nacl->lun_entry_mutex); } @@ -334,8 +331,7 @@ int core_enable_device_list_for_node( mutex_lock(&nacl->lun_entry_mutex); orig = target_nacl_find_deve(nacl, mapped_lun); if (orig && orig->se_lun) { - struct se_lun *orig_lun = rcu_dereference_check(orig->se_lun, - lockdep_is_held(&nacl->lun_entry_mutex)); + struct se_lun *orig_lun = orig->se_lun; if (orig_lun != lun) { pr_err("Existing orig->se_lun doesn't match new lun" @@ -355,8 +351,8 @@ int core_enable_device_list_for_node( return -EINVAL; } - rcu_assign_pointer(new->se_lun, lun); - rcu_assign_pointer(new->se_lun_acl, lun_acl); + new->se_lun = lun; + new->se_lun_acl = lun_acl; hlist_del_rcu(&orig->link); hlist_add_head_rcu(&new->link, &nacl->lun_entry_hlist); mutex_unlock(&nacl->lun_entry_mutex); @@ -374,8 +370,8 @@ int core_enable_device_list_for_node( return 0; } - rcu_assign_pointer(new->se_lun, lun); - rcu_assign_pointer(new->se_lun_acl, lun_acl); + new->se_lun = lun; + new->se_lun_acl = lun_acl; hlist_add_head_rcu(&new->link, &nacl->lun_entry_hlist); mutex_unlock(&nacl->lun_entry_mutex); @@ -434,9 +430,6 @@ void core_disable_device_list_for_node( kref_put(&orig->pr_kref, target_pr_kref_release); wait_for_completion(&orig->pr_comp); - rcu_assign_pointer(orig->se_lun, NULL); - rcu_assign_pointer(orig->se_lun_acl, NULL); - kfree_rcu(orig, rcu_head); core_scsi3_free_pr_reg_from_nacl(dev, nacl); @@ -457,10 +450,7 @@ void core_clear_lun_from_tpg(struct se_lun *lun, struct se_portal_group *tpg) mutex_lock(&nacl->lun_entry_mutex); hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link) { - struct se_lun *tmp_lun = rcu_dereference_check(deve->se_lun, - lockdep_is_held(&nacl->lun_entry_mutex)); - - if (lun != tmp_lun) + if (lun != deve->se_lun) continue; core_disable_device_list_for_node(lun, deve, nacl, tpg); diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 3829b61b56c1..a1d67554709f 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -739,8 +739,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( if (!deve_tmp->se_lun_acl) continue; - lacl_tmp = rcu_dereference_check(deve_tmp->se_lun_acl, - lockdep_is_held(&lun_tmp->lun_deve_lock)); + lacl_tmp = deve_tmp->se_lun_acl; nacl_tmp = lacl_tmp->se_lun_nacl; /* * Skip the matching struct se_node_acl that is allocated @@ -784,8 +783,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( * the original *pr_reg is processed in * __core_scsi3_add_registration() */ - dest_lun = rcu_dereference_check(deve_tmp->se_lun, - kref_read(&deve_tmp->pr_kref) != 0); + dest_lun = deve_tmp->se_lun; pr_reg_atp = __core_scsi3_do_alloc_registration(dev, nacl_tmp, dest_lun, deve_tmp, @@ -1437,34 +1435,26 @@ static void core_scsi3_nodeacl_undepend_item(struct se_node_acl *nacl) static int core_scsi3_lunacl_depend_item(struct se_dev_entry *se_deve) { - struct se_lun_acl *lun_acl; - /* * For nacl->dynamic_node_acl=1 */ - lun_acl = rcu_dereference_check(se_deve->se_lun_acl, - kref_read(&se_deve->pr_kref) != 0); - if (!lun_acl) + if (!se_deve->se_lun_acl) return 0; - return target_depend_item(&lun_acl->se_lun_group.cg_item); + return target_depend_item(&se_deve->se_lun_acl->se_lun_group.cg_item); } static void core_scsi3_lunacl_undepend_item(struct se_dev_entry *se_deve) { - struct se_lun_acl *lun_acl; - /* * For nacl->dynamic_node_acl=1 */ - lun_acl = rcu_dereference_check(se_deve->se_lun_acl, - kref_read(&se_deve->pr_kref) != 0); - if (!lun_acl) { + if (!se_deve->se_lun_acl) { kref_put(&se_deve->pr_kref, target_pr_kref_release); return; } - target_undepend_item(&lun_acl->se_lun_group.cg_item); + target_undepend_item(&se_deve->se_lun_acl->se_lun_group.cg_item); kref_put(&se_deve->pr_kref, target_pr_kref_release); } @@ -1751,8 +1741,7 @@ core_scsi3_decode_spec_i_port( * and then call __core_scsi3_add_registration() in the * 2nd loop which will never fail. */ - dest_lun = rcu_dereference_check(dest_se_deve->se_lun, - kref_read(&dest_se_deve->pr_kref) != 0); + dest_lun = dest_se_deve->se_lun; dest_pr_reg = __core_scsi3_alloc_registration(cmd->se_dev, dest_node_acl, dest_lun, dest_se_deve, @@ -3446,8 +3435,7 @@ after_iport_check: dest_pr_reg = __core_scsi3_locate_pr_reg(dev, dest_node_acl, iport_ptr); if (!dest_pr_reg) { - struct se_lun *dest_lun = rcu_dereference_check(dest_se_deve->se_lun, - kref_read(&dest_se_deve->pr_kref) != 0); + struct se_lun *dest_lun = dest_se_deve->se_lun; spin_unlock(&dev->dev_reservation_lock); if (core_scsi3_alloc_registration(cmd->se_dev, dest_node_acl, diff --git a/drivers/target/target_core_stat.c b/drivers/target/target_core_stat.c index 62d15bcc3d93..f85ee5b0fd80 100644 --- a/drivers/target/target_core_stat.c +++ b/drivers/target/target_core_stat.c @@ -877,7 +877,6 @@ static ssize_t target_stat_auth_dev_show(struct config_item *item, struct se_lun_acl *lacl = auth_to_lacl(item); struct se_node_acl *nacl = lacl->se_lun_nacl; struct se_dev_entry *deve; - struct se_lun *lun; ssize_t ret; rcu_read_lock(); @@ -886,9 +885,9 @@ static ssize_t target_stat_auth_dev_show(struct config_item *item, rcu_read_unlock(); return -ENODEV; } - lun = rcu_dereference(deve->se_lun); + /* scsiDeviceIndex */ - ret = snprintf(page, PAGE_SIZE, "%u\n", lun->lun_index); + ret = snprintf(page, PAGE_SIZE, "%u\n", deve->se_lun->lun_index); rcu_read_unlock(); return ret; } @@ -1217,7 +1216,6 @@ static ssize_t target_stat_iport_dev_show(struct config_item *item, struct se_lun_acl *lacl = iport_to_lacl(item); struct se_node_acl *nacl = lacl->se_lun_nacl; struct se_dev_entry *deve; - struct se_lun *lun; ssize_t ret; rcu_read_lock(); @@ -1226,9 +1224,9 @@ static ssize_t target_stat_iport_dev_show(struct config_item *item, rcu_read_unlock(); return -ENODEV; } - lun = rcu_dereference(deve->se_lun); + /* scsiDeviceIndex */ - ret = snprintf(page, PAGE_SIZE, "%u\n", lun->lun_index); + ret = snprintf(page, PAGE_SIZE, "%u\n", deve->se_lun->lun_index); rcu_read_unlock(); return ret; } diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c index 6bb20aa9c5bc..8713cda0c2fb 100644 --- a/drivers/target/target_core_xcopy.c +++ b/drivers/target/target_core_xcopy.c @@ -88,7 +88,7 @@ static int target_xcopy_locate_se_dev_e4(struct se_session *sess, struct se_device *this_dev; int rc; - this_lun = rcu_dereference(deve->se_lun); + this_lun = deve->se_lun; this_dev = rcu_dereference_raw(this_lun->lun_se_dev); rc = target_xcopy_locate_se_dev_e4_iter(this_dev, dev_wwn); diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 3bc0709a5dc2..6bc679d22927 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -8326,6 +8326,7 @@ static struct scsi_host_template ufshcd_driver_template = { .cmd_per_lun = UFSHCD_CMD_PER_LUN, .can_queue = UFSHCD_CAN_QUEUE, .max_segment_size = PRDT_DATA_BYTE_COUNT_MAX, + .max_sectors = (1 << 20) / SECTOR_SIZE, /* 1 MiB */ .max_host_blocked = 1, .track_queue_depth = 1, .sdev_groups = ufshcd_driver_groups, @@ -9508,12 +9509,8 @@ EXPORT_SYMBOL(ufshcd_runtime_resume); int ufshcd_shutdown(struct ufs_hba *hba) { if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba)) - goto out; - - pm_runtime_get_sync(hba->dev); + ufshcd_suspend(hba); - ufshcd_suspend(hba); -out: hba->is_powered = false; /* allow force shutdown even in case of errors */ return 0; diff --git a/drivers/ufs/host/ufshcd-pci.c b/drivers/ufs/host/ufshcd-pci.c index 24af1f389bf2..1c91f43e15c8 100644 --- a/drivers/ufs/host/ufshcd-pci.c +++ b/drivers/ufs/host/ufshcd-pci.c @@ -24,7 +24,7 @@ struct ufs_host { void (*late_init)(struct ufs_hba *hba); }; -enum { +enum intel_ufs_dsm_func_id { INTEL_DSM_FNS = 0, INTEL_DSM_RESET = 1, }; @@ -42,6 +42,15 @@ static const guid_t intel_dsm_guid = GUID_INIT(0x1A4832A0, 0x7D03, 0x43CA, 0xB0, 0x20, 0xF6, 0xDC, 0xD1, 0x2A, 0x19, 0x50); +static bool __intel_dsm_supported(struct intel_host *host, + enum intel_ufs_dsm_func_id fn) +{ + return fn < 32 && fn >= 0 && (host->dsm_fns & (1u << fn)); +} + +#define INTEL_DSM_SUPPORTED(host, name) \ + __intel_dsm_supported(host, INTEL_DSM_##name) + static int __intel_dsm(struct intel_host *intel_host, struct device *dev, unsigned int fn, u32 *result) { @@ -71,7 +80,7 @@ out: static int intel_dsm(struct intel_host *intel_host, struct device *dev, unsigned int fn, u32 *result) { - if (fn > 31 || !(intel_host->dsm_fns & (1 << fn))) + if (!__intel_dsm_supported(intel_host, fn)) return -EOPNOTSUPP; return __intel_dsm(intel_host, dev, fn, result); @@ -300,7 +309,7 @@ static int ufs_intel_device_reset(struct ufs_hba *hba) { struct intel_host *host = ufshcd_get_variant(hba); - if (host->dsm_fns & INTEL_DSM_RESET) { + if (INTEL_DSM_SUPPORTED(host, RESET)) { u32 result = 0; int err; @@ -342,7 +351,7 @@ static int ufs_intel_common_init(struct ufs_hba *hba) return -ENOMEM; ufshcd_set_variant(hba, host); intel_dsm_init(host, hba->dev); - if (host->dsm_fns & INTEL_DSM_RESET) { + if (INTEL_DSM_SUPPORTED(host, RESET)) { if (hba->vops->device_reset) hba->caps |= UFSHCD_CAP_DEEPSLEEP; } else { diff --git a/drivers/vdpa/ifcvf/ifcvf_base.c b/drivers/vdpa/ifcvf/ifcvf_base.c index 48c4dadb0c7c..75a703b803a2 100644 --- a/drivers/vdpa/ifcvf/ifcvf_base.c +++ b/drivers/vdpa/ifcvf/ifcvf_base.c @@ -29,7 +29,6 @@ u16 ifcvf_set_config_vector(struct ifcvf_hw *hw, int vector) { struct virtio_pci_common_cfg __iomem *cfg = hw->common_cfg; - cfg = hw->common_cfg; vp_iowrite16(vector, &cfg->msix_config); return vp_ioread16(&cfg->msix_config); @@ -128,6 +127,7 @@ int ifcvf_init_hw(struct ifcvf_hw *hw, struct pci_dev *pdev) break; case VIRTIO_PCI_CAP_DEVICE_CFG: hw->dev_cfg = get_cap_addr(hw, &cap); + hw->cap_dev_config_size = le32_to_cpu(cap.length); IFCVF_DBG(pdev, "hw->dev_cfg = %p\n", hw->dev_cfg); break; } @@ -233,15 +233,23 @@ int ifcvf_verify_min_features(struct ifcvf_hw *hw, u64 features) u32 ifcvf_get_config_size(struct ifcvf_hw *hw) { struct ifcvf_adapter *adapter; + u32 net_config_size = sizeof(struct virtio_net_config); + u32 blk_config_size = sizeof(struct virtio_blk_config); + u32 cap_size = hw->cap_dev_config_size; u32 config_size; adapter = vf_to_adapter(hw); + /* If the onboard device config space size is greater than + * the size of struct virtio_net/blk_config, only the spec + * implementing contents size is returned, this is very + * unlikely, defensive programming. + */ switch (hw->dev_type) { case VIRTIO_ID_NET: - config_size = sizeof(struct virtio_net_config); + config_size = min(cap_size, net_config_size); break; case VIRTIO_ID_BLOCK: - config_size = sizeof(struct virtio_blk_config); + config_size = min(cap_size, blk_config_size); break; default: config_size = 0; diff --git a/drivers/vdpa/ifcvf/ifcvf_base.h b/drivers/vdpa/ifcvf/ifcvf_base.h index 115b61f4924b..f5563f665cc6 100644 --- a/drivers/vdpa/ifcvf/ifcvf_base.h +++ b/drivers/vdpa/ifcvf/ifcvf_base.h @@ -87,6 +87,8 @@ struct ifcvf_hw { int config_irq; int vqs_reused_irq; u16 nr_vring; + /* VIRTIO_PCI_CAP_DEVICE_CFG size */ + u32 cap_dev_config_size; }; struct ifcvf_adapter { diff --git a/drivers/vdpa/ifcvf/ifcvf_main.c b/drivers/vdpa/ifcvf/ifcvf_main.c index 0a5670729412..f9c0044c6442 100644 --- a/drivers/vdpa/ifcvf/ifcvf_main.c +++ b/drivers/vdpa/ifcvf/ifcvf_main.c @@ -685,7 +685,7 @@ static struct vdpa_notification_area ifcvf_get_vq_notification(struct vdpa_devic } /* - * IFCVF currently does't have on-chip IOMMU, so not + * IFCVF currently doesn't have on-chip IOMMU, so not * implemented set_map()/dma_map()/dma_unmap() */ static const struct vdpa_config_ops ifc_vdpa_ops = { @@ -752,59 +752,36 @@ static int ifcvf_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name, { struct ifcvf_vdpa_mgmt_dev *ifcvf_mgmt_dev; struct ifcvf_adapter *adapter; + struct vdpa_device *vdpa_dev; struct pci_dev *pdev; struct ifcvf_hw *vf; - struct device *dev; - int ret, i; + int ret; ifcvf_mgmt_dev = container_of(mdev, struct ifcvf_vdpa_mgmt_dev, mdev); - if (ifcvf_mgmt_dev->adapter) + if (!ifcvf_mgmt_dev->adapter) return -EOPNOTSUPP; - pdev = ifcvf_mgmt_dev->pdev; - dev = &pdev->dev; - adapter = vdpa_alloc_device(struct ifcvf_adapter, vdpa, - dev, &ifc_vdpa_ops, 1, 1, name, false); - if (IS_ERR(adapter)) { - IFCVF_ERR(pdev, "Failed to allocate vDPA structure"); - return PTR_ERR(adapter); - } - - ifcvf_mgmt_dev->adapter = adapter; - + adapter = ifcvf_mgmt_dev->adapter; vf = &adapter->vf; - vf->dev_type = get_dev_type(pdev); - vf->base = pcim_iomap_table(pdev); + pdev = adapter->pdev; + vdpa_dev = &adapter->vdpa; - adapter->pdev = pdev; - adapter->vdpa.dma_dev = &pdev->dev; - - ret = ifcvf_init_hw(vf, pdev); - if (ret) { - IFCVF_ERR(pdev, "Failed to init IFCVF hw\n"); - goto err; - } - - for (i = 0; i < vf->nr_vring; i++) - vf->vring[i].irq = -EINVAL; - - vf->hw_features = ifcvf_get_hw_features(vf); - vf->config_size = ifcvf_get_config_size(vf); + if (name) + ret = dev_set_name(&vdpa_dev->dev, "%s", name); + else + ret = dev_set_name(&vdpa_dev->dev, "vdpa%u", vdpa_dev->index); - adapter->vdpa.mdev = &ifcvf_mgmt_dev->mdev; ret = _vdpa_register_device(&adapter->vdpa, vf->nr_vring); if (ret) { + put_device(&adapter->vdpa.dev); IFCVF_ERR(pdev, "Failed to register to vDPA bus"); - goto err; + return ret; } return 0; - -err: - put_device(&adapter->vdpa.dev); - return ret; } + static void ifcvf_vdpa_dev_del(struct vdpa_mgmt_dev *mdev, struct vdpa_device *dev) { struct ifcvf_vdpa_mgmt_dev *ifcvf_mgmt_dev; @@ -823,61 +800,94 @@ static int ifcvf_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct ifcvf_vdpa_mgmt_dev *ifcvf_mgmt_dev; struct device *dev = &pdev->dev; + struct ifcvf_adapter *adapter; + struct ifcvf_hw *vf; u32 dev_type; - int ret; - - ifcvf_mgmt_dev = kzalloc(sizeof(struct ifcvf_vdpa_mgmt_dev), GFP_KERNEL); - if (!ifcvf_mgmt_dev) { - IFCVF_ERR(pdev, "Failed to alloc memory for the vDPA management device\n"); - return -ENOMEM; - } - - dev_type = get_dev_type(pdev); - switch (dev_type) { - case VIRTIO_ID_NET: - ifcvf_mgmt_dev->mdev.id_table = id_table_net; - break; - case VIRTIO_ID_BLOCK: - ifcvf_mgmt_dev->mdev.id_table = id_table_blk; - break; - default: - IFCVF_ERR(pdev, "VIRTIO ID %u not supported\n", dev_type); - ret = -EOPNOTSUPP; - goto err; - } - - ifcvf_mgmt_dev->mdev.ops = &ifcvf_vdpa_mgmt_dev_ops; - ifcvf_mgmt_dev->mdev.device = dev; - ifcvf_mgmt_dev->pdev = pdev; + int ret, i; ret = pcim_enable_device(pdev); if (ret) { IFCVF_ERR(pdev, "Failed to enable device\n"); - goto err; + return ret; } - ret = pcim_iomap_regions(pdev, BIT(0) | BIT(2) | BIT(4), IFCVF_DRIVER_NAME); if (ret) { IFCVF_ERR(pdev, "Failed to request MMIO region\n"); - goto err; + return ret; } ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); if (ret) { IFCVF_ERR(pdev, "No usable DMA configuration\n"); - goto err; + return ret; } ret = devm_add_action_or_reset(dev, ifcvf_free_irq_vectors, pdev); if (ret) { IFCVF_ERR(pdev, "Failed for adding devres for freeing irq vectors\n"); - goto err; + return ret; } pci_set_master(pdev); + adapter = vdpa_alloc_device(struct ifcvf_adapter, vdpa, + dev, &ifc_vdpa_ops, 1, 1, NULL, false); + if (IS_ERR(adapter)) { + IFCVF_ERR(pdev, "Failed to allocate vDPA structure"); + return PTR_ERR(adapter); + } + + vf = &adapter->vf; + vf->dev_type = get_dev_type(pdev); + vf->base = pcim_iomap_table(pdev); + + adapter->pdev = pdev; + adapter->vdpa.dma_dev = &pdev->dev; + + ret = ifcvf_init_hw(vf, pdev); + if (ret) { + IFCVF_ERR(pdev, "Failed to init IFCVF hw\n"); + return ret; + } + + for (i = 0; i < vf->nr_vring; i++) + vf->vring[i].irq = -EINVAL; + + vf->hw_features = ifcvf_get_hw_features(vf); + vf->config_size = ifcvf_get_config_size(vf); + + ifcvf_mgmt_dev = kzalloc(sizeof(struct ifcvf_vdpa_mgmt_dev), GFP_KERNEL); + if (!ifcvf_mgmt_dev) { + IFCVF_ERR(pdev, "Failed to alloc memory for the vDPA management device\n"); + return -ENOMEM; + } + + ifcvf_mgmt_dev->mdev.ops = &ifcvf_vdpa_mgmt_dev_ops; + ifcvf_mgmt_dev->mdev.device = dev; + ifcvf_mgmt_dev->adapter = adapter; + + dev_type = get_dev_type(pdev); + switch (dev_type) { + case VIRTIO_ID_NET: + ifcvf_mgmt_dev->mdev.id_table = id_table_net; + break; + case VIRTIO_ID_BLOCK: + ifcvf_mgmt_dev->mdev.id_table = id_table_blk; + break; + default: + IFCVF_ERR(pdev, "VIRTIO ID %u not supported\n", dev_type); + ret = -EOPNOTSUPP; + goto err; + } + + ifcvf_mgmt_dev->mdev.max_supported_vqs = vf->nr_vring; + ifcvf_mgmt_dev->mdev.supported_features = vf->hw_features; + + adapter->vdpa.mdev = &ifcvf_mgmt_dev->mdev; + + ret = vdpa_mgmtdev_register(&ifcvf_mgmt_dev->mdev); if (ret) { IFCVF_ERR(pdev, diff --git a/drivers/vdpa/mlx5/core/mlx5_vdpa.h b/drivers/vdpa/mlx5/core/mlx5_vdpa.h index 44104093163b..6af9fdbb86b7 100644 --- a/drivers/vdpa/mlx5/core/mlx5_vdpa.h +++ b/drivers/vdpa/mlx5/core/mlx5_vdpa.h @@ -70,6 +70,16 @@ struct mlx5_vdpa_wq_ent { struct mlx5_vdpa_dev *mvdev; }; +enum { + MLX5_VDPA_DATAVQ_GROUP, + MLX5_VDPA_CVQ_GROUP, + MLX5_VDPA_NUMVQ_GROUPS +}; + +enum { + MLX5_VDPA_NUM_AS = MLX5_VDPA_NUMVQ_GROUPS +}; + struct mlx5_vdpa_dev { struct vdpa_device vdev; struct mlx5_core_dev *mdev; @@ -85,6 +95,7 @@ struct mlx5_vdpa_dev { struct mlx5_vdpa_mr mr; struct mlx5_control_vq cvq; struct workqueue_struct *wq; + unsigned int group2asid[MLX5_VDPA_NUMVQ_GROUPS]; }; int mlx5_vdpa_alloc_pd(struct mlx5_vdpa_dev *dev, u32 *pdn, u16 uid); diff --git a/drivers/vdpa/mlx5/net/mlx5_vnet.c b/drivers/vdpa/mlx5/net/mlx5_vnet.c index e85c1d71f4ed..ed100a35e596 100644 --- a/drivers/vdpa/mlx5/net/mlx5_vnet.c +++ b/drivers/vdpa/mlx5/net/mlx5_vnet.c @@ -164,6 +164,7 @@ struct mlx5_vdpa_net { bool setup; u32 cur_num_vqs; u32 rqt_size; + bool nb_registered; struct notifier_block nb; struct vdpa_callback config_cb; struct mlx5_vdpa_wq_ent cvq_ent; @@ -895,6 +896,7 @@ static int create_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtque if (err) goto err_cmd; + mvq->fw_state = MLX5_VIRTIO_NET_Q_OBJECT_STATE_INIT; kfree(in); mvq->virtq_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id); @@ -922,6 +924,7 @@ static void destroy_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtq mlx5_vdpa_warn(&ndev->mvdev, "destroy virtqueue 0x%x\n", mvq->virtq_id); return; } + mvq->fw_state = MLX5_VIRTIO_NET_Q_OBJECT_NONE; umems_destroy(ndev, mvq); } @@ -1121,6 +1124,20 @@ err_cmd: return err; } +static bool is_valid_state_change(int oldstate, int newstate) +{ + switch (oldstate) { + case MLX5_VIRTIO_NET_Q_OBJECT_STATE_INIT: + return newstate == MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY; + case MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY: + return newstate == MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND; + case MLX5_VIRTIO_NET_Q_OBJECT_STATE_SUSPEND: + case MLX5_VIRTIO_NET_Q_OBJECT_STATE_ERR: + default: + return false; + } +} + static int modify_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtqueue *mvq, int state) { int inlen = MLX5_ST_SZ_BYTES(modify_virtio_net_q_in); @@ -1130,6 +1147,12 @@ static int modify_virtqueue(struct mlx5_vdpa_net *ndev, struct mlx5_vdpa_virtque void *in; int err; + if (mvq->fw_state == MLX5_VIRTIO_NET_Q_OBJECT_NONE) + return 0; + + if (!is_valid_state_change(mvq->fw_state, state)) + return -EINVAL; + in = kzalloc(inlen, GFP_KERNEL); if (!in) return -ENOMEM; @@ -1440,7 +1463,7 @@ static int mlx5_vdpa_add_mac_vlan_rules(struct mlx5_vdpa_net *ndev, u8 *mac, headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, outer_headers); dmac_c = MLX5_ADDR_OF(fte_match_param, headers_c, outer_headers.dmac_47_16); dmac_v = MLX5_ADDR_OF(fte_match_param, headers_v, outer_headers.dmac_47_16); - memset(dmac_c, 0xff, ETH_ALEN); + eth_broadcast_addr(dmac_c); ether_addr_copy(dmac_v, mac); MLX5_SET(fte_match_set_lyr_2_4, headers_c, cvlan_tag, 1); if (tagged) { @@ -1992,6 +2015,7 @@ static void mlx5_vdpa_set_vq_ready(struct vdpa_device *vdev, u16 idx, bool ready struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev); struct mlx5_vdpa_virtqueue *mvq; + int err; if (!mvdev->actual_features) return; @@ -2005,8 +2029,16 @@ static void mlx5_vdpa_set_vq_ready(struct vdpa_device *vdev, u16 idx, bool ready } mvq = &ndev->vqs[idx]; - if (!ready) + if (!ready) { suspend_vq(ndev, mvq); + } else { + err = modify_virtqueue(ndev, mvq, MLX5_VIRTIO_NET_Q_OBJECT_STATE_RDY); + if (err) { + mlx5_vdpa_warn(mvdev, "modify VQ %d to ready failed (%d)\n", idx, err); + ready = false; + } + } + mvq->ready = ready; } @@ -2095,9 +2127,14 @@ static u32 mlx5_vdpa_get_vq_align(struct vdpa_device *vdev) return PAGE_SIZE; } -static u32 mlx5_vdpa_get_vq_group(struct vdpa_device *vdpa, u16 idx) +static u32 mlx5_vdpa_get_vq_group(struct vdpa_device *vdev, u16 idx) { - return 0; + struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); + + if (is_ctrl_vq_idx(mvdev, idx)) + return MLX5_VDPA_CVQ_GROUP; + + return MLX5_VDPA_DATAVQ_GROUP; } enum { MLX5_VIRTIO_NET_F_GUEST_CSUM = 1 << 9, @@ -2511,6 +2548,15 @@ err_clear: up_write(&ndev->reslock); } +static void init_group_to_asid_map(struct mlx5_vdpa_dev *mvdev) +{ + int i; + + /* default mapping all groups are mapped to asid 0 */ + for (i = 0; i < MLX5_VDPA_NUMVQ_GROUPS; i++) + mvdev->group2asid[i] = 0; +} + static int mlx5_vdpa_reset(struct vdpa_device *vdev) { struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); @@ -2529,7 +2575,9 @@ static int mlx5_vdpa_reset(struct vdpa_device *vdev) ndev->mvdev.cvq.completed_desc = 0; memset(ndev->event_cbs, 0, sizeof(*ndev->event_cbs) * (mvdev->max_vqs + 1)); ndev->mvdev.actual_features = 0; + init_group_to_asid_map(mvdev); ++mvdev->generation; + if (MLX5_CAP_GEN(mvdev->mdev, umem_uid_0)) { if (mlx5_vdpa_create_mr(mvdev, NULL)) mlx5_vdpa_warn(mvdev, "create MR failed\n"); @@ -2567,26 +2615,63 @@ static u32 mlx5_vdpa_get_generation(struct vdpa_device *vdev) return mvdev->generation; } -static int mlx5_vdpa_set_map(struct vdpa_device *vdev, unsigned int asid, - struct vhost_iotlb *iotlb) +static int set_map_control(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb) +{ + u64 start = 0ULL, last = 0ULL - 1; + struct vhost_iotlb_map *map; + int err = 0; + + spin_lock(&mvdev->cvq.iommu_lock); + vhost_iotlb_reset(mvdev->cvq.iotlb); + + for (map = vhost_iotlb_itree_first(iotlb, start, last); map; + map = vhost_iotlb_itree_next(map, start, last)) { + err = vhost_iotlb_add_range(mvdev->cvq.iotlb, map->start, + map->last, map->addr, map->perm); + if (err) + goto out; + } + +out: + spin_unlock(&mvdev->cvq.iommu_lock); + return err; +} + +static int set_map_data(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb) { - struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); - struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev); bool change_map; int err; - down_write(&ndev->reslock); - err = mlx5_vdpa_handle_set_map(mvdev, iotlb, &change_map); if (err) { mlx5_vdpa_warn(mvdev, "set map failed(%d)\n", err); - goto err; + return err; } if (change_map) err = mlx5_vdpa_change_map(mvdev, iotlb); -err: + return err; +} + +static int mlx5_vdpa_set_map(struct vdpa_device *vdev, unsigned int asid, + struct vhost_iotlb *iotlb) +{ + struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); + struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev); + int err = -EINVAL; + + down_write(&ndev->reslock); + if (mvdev->group2asid[MLX5_VDPA_DATAVQ_GROUP] == asid) { + err = set_map_data(mvdev, iotlb); + if (err) + goto out; + } + + if (mvdev->group2asid[MLX5_VDPA_CVQ_GROUP] == asid) + err = set_map_control(mvdev, iotlb); + +out: up_write(&ndev->reslock); return err; } @@ -2733,6 +2818,49 @@ out_err: return err; } +static void mlx5_vdpa_cvq_suspend(struct mlx5_vdpa_dev *mvdev) +{ + struct mlx5_control_vq *cvq; + + if (!(mvdev->actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VQ))) + return; + + cvq = &mvdev->cvq; + cvq->ready = false; +} + +static int mlx5_vdpa_suspend(struct vdpa_device *vdev) +{ + struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); + struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev); + struct mlx5_vdpa_virtqueue *mvq; + int i; + + down_write(&ndev->reslock); + mlx5_notifier_unregister(mvdev->mdev, &ndev->nb); + ndev->nb_registered = false; + flush_workqueue(ndev->mvdev.wq); + for (i = 0; i < ndev->cur_num_vqs; i++) { + mvq = &ndev->vqs[i]; + suspend_vq(ndev, mvq); + } + mlx5_vdpa_cvq_suspend(mvdev); + up_write(&ndev->reslock); + return 0; +} + +static int mlx5_set_group_asid(struct vdpa_device *vdev, u32 group, + unsigned int asid) +{ + struct mlx5_vdpa_dev *mvdev = to_mvdev(vdev); + + if (group >= MLX5_VDPA_NUMVQ_GROUPS) + return -EINVAL; + + mvdev->group2asid[group] = asid; + return 0; +} + static const struct vdpa_config_ops mlx5_vdpa_ops = { .set_vq_address = mlx5_vdpa_set_vq_address, .set_vq_num = mlx5_vdpa_set_vq_num, @@ -2762,7 +2890,9 @@ static const struct vdpa_config_ops mlx5_vdpa_ops = { .set_config = mlx5_vdpa_set_config, .get_generation = mlx5_vdpa_get_generation, .set_map = mlx5_vdpa_set_map, + .set_group_asid = mlx5_set_group_asid, .free = mlx5_vdpa_free, + .suspend = mlx5_vdpa_suspend, }; static int query_mtu(struct mlx5_core_dev *mdev, u16 *mtu) @@ -2828,6 +2958,7 @@ static void init_mvqs(struct mlx5_vdpa_net *ndev) mvq->index = i; mvq->ndev = ndev; mvq->fwqp.fw = true; + mvq->fw_state = MLX5_VIRTIO_NET_Q_OBJECT_NONE; } for (; i < ndev->mvdev.max_vqs; i++) { mvq = &ndev->vqs[i]; @@ -2902,13 +3033,21 @@ static int event_handler(struct notifier_block *nb, unsigned long event, void *p switch (eqe->sub_type) { case MLX5_PORT_CHANGE_SUBTYPE_DOWN: case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE: + down_read(&ndev->reslock); + if (!ndev->nb_registered) { + up_read(&ndev->reslock); + return NOTIFY_DONE; + } wqent = kzalloc(sizeof(*wqent), GFP_ATOMIC); - if (!wqent) + if (!wqent) { + up_read(&ndev->reslock); return NOTIFY_DONE; + } wqent->mvdev = &ndev->mvdev; INIT_WORK(&wqent->work, update_carrier); queue_work(ndev->mvdev.wq, &wqent->work); + up_read(&ndev->reslock); ret = NOTIFY_OK; break; default: @@ -2982,7 +3121,7 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name, } ndev = vdpa_alloc_device(struct mlx5_vdpa_net, mvdev.vdev, mdev->device, &mlx5_vdpa_ops, - 1, 1, name, false); + MLX5_VDPA_NUMVQ_GROUPS, MLX5_VDPA_NUM_AS, name, false); if (IS_ERR(ndev)) return PTR_ERR(ndev); @@ -3062,6 +3201,7 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name, ndev->nb.notifier_call = event_handler; mlx5_notifier_register(mdev, &ndev->nb); + ndev->nb_registered = true; mvdev->vdev.mdev = &mgtdev->mgtdev; err = _vdpa_register_device(&mvdev->vdev, max_vqs + 1); if (err) @@ -3093,7 +3233,10 @@ static void mlx5_vdpa_dev_del(struct vdpa_mgmt_dev *v_mdev, struct vdpa_device * struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev); struct workqueue_struct *wq; - mlx5_notifier_unregister(mvdev->mdev, &ndev->nb); + if (ndev->nb_registered) { + mlx5_notifier_unregister(mvdev->mdev, &ndev->nb); + ndev->nb_registered = false; + } wq = mvdev->wq; mvdev->wq = NULL; destroy_workqueue(wq); diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c index ebf2f363fbe7..c06c02704461 100644 --- a/drivers/vdpa/vdpa.c +++ b/drivers/vdpa/vdpa.c @@ -824,11 +824,11 @@ static int vdpa_dev_net_config_fill(struct vdpa_device *vdev, struct sk_buff *ms config.mac)) return -EMSGSIZE; - val_u16 = le16_to_cpu(config.status); + val_u16 = __virtio16_to_cpu(true, config.status); if (nla_put_u16(msg, VDPA_ATTR_DEV_NET_STATUS, val_u16)) return -EMSGSIZE; - val_u16 = le16_to_cpu(config.mtu); + val_u16 = __virtio16_to_cpu(true, config.mtu); if (nla_put_u16(msg, VDPA_ATTR_DEV_NET_CFG_MTU, val_u16)) return -EMSGSIZE; @@ -846,17 +846,9 @@ vdpa_dev_config_fill(struct vdpa_device *vdev, struct sk_buff *msg, u32 portid, { u32 device_id; void *hdr; - u8 status; int err; down_read(&vdev->cf_lock); - status = vdev->config->get_status(vdev); - if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) { - NL_SET_ERR_MSG_MOD(extack, "Features negotiation not completed"); - err = -EAGAIN; - goto out; - } - hdr = genlmsg_put(msg, portid, seq, &vdpa_nl_family, flags, VDPA_CMD_DEV_CONFIG_GET); if (!hdr) { @@ -913,7 +905,7 @@ static int vdpa_fill_stats_rec(struct vdpa_device *vdev, struct sk_buff *msg, } vdpa_get_config_unlocked(vdev, 0, &config, sizeof(config)); - max_vqp = le16_to_cpu(config.max_virtqueue_pairs); + max_vqp = __virtio16_to_cpu(true, config.max_virtqueue_pairs); if (nla_put_u16(msg, VDPA_ATTR_DEV_NET_CFG_MAX_VQP, max_vqp)) return -EMSGSIZE; diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c index 0f2865899647..225b7f5d8be3 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c @@ -33,7 +33,7 @@ MODULE_PARM_DESC(batch_mapping, "Batched mapping 1 -Enable; 0 - Disable"); static int max_iotlb_entries = 2048; module_param(max_iotlb_entries, int, 0444); MODULE_PARM_DESC(max_iotlb_entries, - "Maximum number of iotlb entries. 0 means unlimited. (default: 2048)"); + "Maximum number of iotlb entries for each address space. 0 means unlimited. (default: 2048)"); #define VDPASIM_QUEUE_ALIGN PAGE_SIZE #define VDPASIM_QUEUE_MAX 256 @@ -107,6 +107,7 @@ static void vdpasim_do_reset(struct vdpasim *vdpasim) for (i = 0; i < vdpasim->dev_attr.nas; i++) vhost_iotlb_reset(&vdpasim->iommu[i]); + vdpasim->running = true; spin_unlock(&vdpasim->iommu_lock); vdpasim->features = 0; @@ -291,7 +292,7 @@ struct vdpasim *vdpasim_create(struct vdpasim_dev_attr *dev_attr) goto err_iommu; for (i = 0; i < vdpasim->dev_attr.nas; i++) - vhost_iotlb_init(&vdpasim->iommu[i], 0, 0); + vhost_iotlb_init(&vdpasim->iommu[i], max_iotlb_entries, 0); vdpasim->buffer = kvmalloc(dev_attr->buffer_size, GFP_KERNEL); if (!vdpasim->buffer) @@ -505,6 +506,17 @@ static int vdpasim_reset(struct vdpa_device *vdpa) return 0; } +static int vdpasim_suspend(struct vdpa_device *vdpa) +{ + struct vdpasim *vdpasim = vdpa_to_sim(vdpa); + + spin_lock(&vdpasim->lock); + vdpasim->running = false; + spin_unlock(&vdpasim->lock); + + return 0; +} + static size_t vdpasim_get_config_size(struct vdpa_device *vdpa) { struct vdpasim *vdpasim = vdpa_to_sim(vdpa); @@ -694,6 +706,7 @@ static const struct vdpa_config_ops vdpasim_config_ops = { .get_status = vdpasim_get_status, .set_status = vdpasim_set_status, .reset = vdpasim_reset, + .suspend = vdpasim_suspend, .get_config_size = vdpasim_get_config_size, .get_config = vdpasim_get_config, .set_config = vdpasim_set_config, @@ -726,6 +739,7 @@ static const struct vdpa_config_ops vdpasim_batch_config_ops = { .get_status = vdpasim_get_status, .set_status = vdpasim_set_status, .reset = vdpasim_reset, + .suspend = vdpasim_suspend, .get_config_size = vdpasim_get_config_size, .get_config = vdpasim_get_config, .set_config = vdpasim_set_config, diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.h b/drivers/vdpa/vdpa_sim/vdpa_sim.h index 622782e92239..061986f30911 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim.h +++ b/drivers/vdpa/vdpa_sim/vdpa_sim.h @@ -66,6 +66,7 @@ struct vdpasim { u32 generation; u64 features; u32 groups; + bool running; /* spinlock to synchronize iommu table */ spinlock_t iommu_lock; }; diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c index 42d401d43911..c8bfea3b7db2 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c @@ -25,31 +25,49 @@ #define DRV_LICENSE "GPL v2" #define VDPASIM_BLK_FEATURES (VDPASIM_FEATURES | \ + (1ULL << VIRTIO_BLK_F_FLUSH) | \ (1ULL << VIRTIO_BLK_F_SIZE_MAX) | \ (1ULL << VIRTIO_BLK_F_SEG_MAX) | \ (1ULL << VIRTIO_BLK_F_BLK_SIZE) | \ (1ULL << VIRTIO_BLK_F_TOPOLOGY) | \ - (1ULL << VIRTIO_BLK_F_MQ)) + (1ULL << VIRTIO_BLK_F_MQ) | \ + (1ULL << VIRTIO_BLK_F_DISCARD) | \ + (1ULL << VIRTIO_BLK_F_WRITE_ZEROES)) #define VDPASIM_BLK_CAPACITY 0x40000 #define VDPASIM_BLK_SIZE_MAX 0x1000 #define VDPASIM_BLK_SEG_MAX 32 +#define VDPASIM_BLK_DWZ_MAX_SECTORS UINT_MAX + +/* 1 virtqueue, 1 address space, 1 virtqueue group */ #define VDPASIM_BLK_VQ_NUM 1 +#define VDPASIM_BLK_AS_NUM 1 +#define VDPASIM_BLK_GROUP_NUM 1 static char vdpasim_blk_id[VIRTIO_BLK_ID_BYTES] = "vdpa_blk_sim"; -static bool vdpasim_blk_check_range(u64 start_sector, size_t range_size) +static bool vdpasim_blk_check_range(struct vdpasim *vdpasim, u64 start_sector, + u64 num_sectors, u64 max_sectors) { - u64 range_sectors = range_size >> SECTOR_SHIFT; - - if (range_size > VDPASIM_BLK_SIZE_MAX * VDPASIM_BLK_SEG_MAX) - return false; + if (start_sector > VDPASIM_BLK_CAPACITY) { + dev_dbg(&vdpasim->vdpa.dev, + "starting sector exceeds the capacity - start: 0x%llx capacity: 0x%x\n", + start_sector, VDPASIM_BLK_CAPACITY); + } - if (start_sector > VDPASIM_BLK_CAPACITY) + if (num_sectors > max_sectors) { + dev_dbg(&vdpasim->vdpa.dev, + "number of sectors exceeds the max allowed in a request - num: 0x%llx max: 0x%llx\n", + num_sectors, max_sectors); return false; + } - if (range_sectors > VDPASIM_BLK_CAPACITY - start_sector) + if (num_sectors > VDPASIM_BLK_CAPACITY - start_sector) { + dev_dbg(&vdpasim->vdpa.dev, + "request exceeds the capacity - start: 0x%llx num: 0x%llx capacity: 0x%x\n", + start_sector, num_sectors, VDPASIM_BLK_CAPACITY); return false; + } return true; } @@ -63,6 +81,7 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, { size_t pushed = 0, to_pull, to_push; struct virtio_blk_outhdr hdr; + bool handled = false; ssize_t bytes; loff_t offset; u64 sector; @@ -76,14 +95,14 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, return false; if (vq->out_iov.used < 1 || vq->in_iov.used < 1) { - dev_err(&vdpasim->vdpa.dev, "missing headers - out_iov: %u in_iov %u\n", + dev_dbg(&vdpasim->vdpa.dev, "missing headers - out_iov: %u in_iov %u\n", vq->out_iov.used, vq->in_iov.used); - return false; + goto err; } if (vq->in_iov.iov[vq->in_iov.used - 1].iov_len < 1) { - dev_err(&vdpasim->vdpa.dev, "request in header too short\n"); - return false; + dev_dbg(&vdpasim->vdpa.dev, "request in header too short\n"); + goto err; } /* The last byte is the status and we checked if the last iov has @@ -96,8 +115,8 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov, &hdr, sizeof(hdr)); if (bytes != sizeof(hdr)) { - dev_err(&vdpasim->vdpa.dev, "request out header too short\n"); - return false; + dev_dbg(&vdpasim->vdpa.dev, "request out header too short\n"); + goto err; } to_pull -= bytes; @@ -107,12 +126,20 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, offset = sector << SECTOR_SHIFT; status = VIRTIO_BLK_S_OK; + if (type != VIRTIO_BLK_T_IN && type != VIRTIO_BLK_T_OUT && + sector != 0) { + dev_dbg(&vdpasim->vdpa.dev, + "sector must be 0 for %u request - sector: 0x%llx\n", + type, sector); + status = VIRTIO_BLK_S_IOERR; + goto err_status; + } + switch (type) { case VIRTIO_BLK_T_IN: - if (!vdpasim_blk_check_range(sector, to_push)) { - dev_err(&vdpasim->vdpa.dev, - "reading over the capacity - offset: 0x%llx len: 0x%zx\n", - offset, to_push); + if (!vdpasim_blk_check_range(vdpasim, sector, + to_push >> SECTOR_SHIFT, + VDPASIM_BLK_SIZE_MAX * VDPASIM_BLK_SEG_MAX)) { status = VIRTIO_BLK_S_IOERR; break; } @@ -121,7 +148,7 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, vdpasim->buffer + offset, to_push); if (bytes < 0) { - dev_err(&vdpasim->vdpa.dev, + dev_dbg(&vdpasim->vdpa.dev, "vringh_iov_push_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n", bytes, offset, to_push); status = VIRTIO_BLK_S_IOERR; @@ -132,10 +159,9 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, break; case VIRTIO_BLK_T_OUT: - if (!vdpasim_blk_check_range(sector, to_pull)) { - dev_err(&vdpasim->vdpa.dev, - "writing over the capacity - offset: 0x%llx len: 0x%zx\n", - offset, to_pull); + if (!vdpasim_blk_check_range(vdpasim, sector, + to_pull >> SECTOR_SHIFT, + VDPASIM_BLK_SIZE_MAX * VDPASIM_BLK_SEG_MAX)) { status = VIRTIO_BLK_S_IOERR; break; } @@ -144,7 +170,7 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, vdpasim->buffer + offset, to_pull); if (bytes < 0) { - dev_err(&vdpasim->vdpa.dev, + dev_dbg(&vdpasim->vdpa.dev, "vringh_iov_pull_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n", bytes, offset, to_pull); status = VIRTIO_BLK_S_IOERR; @@ -157,7 +183,7 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, vdpasim_blk_id, VIRTIO_BLK_ID_BYTES); if (bytes < 0) { - dev_err(&vdpasim->vdpa.dev, + dev_dbg(&vdpasim->vdpa.dev, "vringh_iov_push_iotlb() error: %zd\n", bytes); status = VIRTIO_BLK_S_IOERR; break; @@ -166,13 +192,76 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, pushed += bytes; break; + case VIRTIO_BLK_T_FLUSH: + /* nothing to do */ + break; + + case VIRTIO_BLK_T_DISCARD: + case VIRTIO_BLK_T_WRITE_ZEROES: { + struct virtio_blk_discard_write_zeroes range; + u32 num_sectors, flags; + + if (to_pull != sizeof(range)) { + dev_dbg(&vdpasim->vdpa.dev, + "discard/write_zeroes header len: 0x%zx [expected: 0x%zx]\n", + to_pull, sizeof(range)); + status = VIRTIO_BLK_S_IOERR; + break; + } + + bytes = vringh_iov_pull_iotlb(&vq->vring, &vq->out_iov, &range, + to_pull); + if (bytes < 0) { + dev_dbg(&vdpasim->vdpa.dev, + "vringh_iov_pull_iotlb() error: %zd offset: 0x%llx len: 0x%zx\n", + bytes, offset, to_pull); + status = VIRTIO_BLK_S_IOERR; + break; + } + + sector = le64_to_cpu(range.sector); + offset = sector << SECTOR_SHIFT; + num_sectors = le32_to_cpu(range.num_sectors); + flags = le32_to_cpu(range.flags); + + if (type == VIRTIO_BLK_T_DISCARD && flags != 0) { + dev_dbg(&vdpasim->vdpa.dev, + "discard unexpected flags set - flags: 0x%x\n", + flags); + status = VIRTIO_BLK_S_UNSUPP; + break; + } + + if (type == VIRTIO_BLK_T_WRITE_ZEROES && + flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) { + dev_dbg(&vdpasim->vdpa.dev, + "write_zeroes unexpected flags set - flags: 0x%x\n", + flags); + status = VIRTIO_BLK_S_UNSUPP; + break; + } + + if (!vdpasim_blk_check_range(vdpasim, sector, num_sectors, + VDPASIM_BLK_DWZ_MAX_SECTORS)) { + status = VIRTIO_BLK_S_IOERR; + break; + } + + if (type == VIRTIO_BLK_T_WRITE_ZEROES) { + memset(vdpasim->buffer + offset, 0, + num_sectors << SECTOR_SHIFT); + } + + break; + } default: - dev_warn(&vdpasim->vdpa.dev, - "Unsupported request type %d\n", type); + dev_dbg(&vdpasim->vdpa.dev, + "Unsupported request type %d\n", type); status = VIRTIO_BLK_S_IOERR; break; } +err_status: /* If some operations fail, we need to skip the remaining bytes * to put the status in the last byte */ @@ -182,21 +271,25 @@ static bool vdpasim_blk_handle_req(struct vdpasim *vdpasim, /* Last byte is the status */ bytes = vringh_iov_push_iotlb(&vq->vring, &vq->in_iov, &status, 1); if (bytes != 1) - return false; + goto err; pushed += bytes; /* Make sure data is wrote before advancing index */ smp_wmb(); + handled = true; + +err: vringh_complete_iotlb(&vq->vring, vq->head, pushed); - return true; + return handled; } static void vdpasim_blk_work(struct work_struct *work) { struct vdpasim *vdpasim = container_of(work, struct vdpasim, work); + bool reschedule = false; int i; spin_lock(&vdpasim->lock); @@ -204,8 +297,12 @@ static void vdpasim_blk_work(struct work_struct *work) if (!(vdpasim->status & VIRTIO_CONFIG_S_DRIVER_OK)) goto out; + if (!vdpasim->running) + goto out; + for (i = 0; i < VDPASIM_BLK_VQ_NUM; i++) { struct vdpasim_virtqueue *vq = &vdpasim->vqs[i]; + int reqs = 0; if (!vq->ready) continue; @@ -218,10 +315,18 @@ static void vdpasim_blk_work(struct work_struct *work) if (vringh_need_notify_iotlb(&vq->vring) > 0) vringh_notify(&vq->vring); local_bh_enable(); + + if (++reqs > 4) { + reschedule = true; + break; + } } } out: spin_unlock(&vdpasim->lock); + + if (reschedule) + schedule_work(&vdpasim->work); } static void vdpasim_blk_get_config(struct vdpasim *vdpasim, void *config) @@ -237,6 +342,17 @@ static void vdpasim_blk_get_config(struct vdpasim *vdpasim, void *config) blk_config->min_io_size = cpu_to_vdpasim16(vdpasim, 1); blk_config->opt_io_size = cpu_to_vdpasim32(vdpasim, 1); blk_config->blk_size = cpu_to_vdpasim32(vdpasim, SECTOR_SIZE); + /* VIRTIO_BLK_F_DISCARD */ + blk_config->discard_sector_alignment = + cpu_to_vdpasim32(vdpasim, SECTOR_SIZE); + blk_config->max_discard_sectors = + cpu_to_vdpasim32(vdpasim, VDPASIM_BLK_DWZ_MAX_SECTORS); + blk_config->max_discard_seg = cpu_to_vdpasim32(vdpasim, 1); + /* VIRTIO_BLK_F_WRITE_ZEROES */ + blk_config->max_write_zeroes_sectors = + cpu_to_vdpasim32(vdpasim, VDPASIM_BLK_DWZ_MAX_SECTORS); + blk_config->max_write_zeroes_seg = cpu_to_vdpasim32(vdpasim, 1); + } static void vdpasim_blk_mgmtdev_release(struct device *dev) @@ -260,6 +376,8 @@ static int vdpasim_blk_dev_add(struct vdpa_mgmt_dev *mdev, const char *name, dev_attr.id = VIRTIO_ID_BLOCK; dev_attr.supported_features = VDPASIM_BLK_FEATURES; dev_attr.nvqs = VDPASIM_BLK_VQ_NUM; + dev_attr.ngroups = VDPASIM_BLK_GROUP_NUM; + dev_attr.nas = VDPASIM_BLK_AS_NUM; dev_attr.config_size = sizeof(struct virtio_blk_config); dev_attr.get_config = vdpasim_blk_get_config; dev_attr.work_fn = vdpasim_blk_work; diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim_net.c b/drivers/vdpa/vdpa_sim/vdpa_sim_net.c index 5125976a4df8..886449e88502 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim_net.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim_net.c @@ -154,6 +154,9 @@ static void vdpasim_net_work(struct work_struct *work) spin_lock(&vdpasim->lock); + if (!vdpasim->running) + goto out; + if (!(vdpasim->status & VIRTIO_CONFIG_S_DRIVER_OK)) goto out; diff --git a/drivers/vdpa/vdpa_user/iova_domain.c b/drivers/vdpa/vdpa_user/iova_domain.c index 6daa3978d290..e682bc7ee6c9 100644 --- a/drivers/vdpa/vdpa_user/iova_domain.c +++ b/drivers/vdpa/vdpa_user/iova_domain.c @@ -138,18 +138,17 @@ static void do_bounce(phys_addr_t orig, void *addr, size_t size, { unsigned long pfn = PFN_DOWN(orig); unsigned int offset = offset_in_page(orig); - char *buffer; + struct page *page; unsigned int sz = 0; while (size) { sz = min_t(size_t, PAGE_SIZE - offset, size); - buffer = kmap_atomic(pfn_to_page(pfn)); + page = pfn_to_page(pfn); if (dir == DMA_TO_DEVICE) - memcpy(addr, buffer + offset, sz); + memcpy_from_page(addr, page, offset, sz); else - memcpy(buffer + offset, addr, sz); - kunmap_atomic(buffer); + memcpy_to_page(page, offset, addr, sz); size -= sz; pfn++; @@ -179,8 +178,9 @@ static void vduse_domain_bounce(struct vduse_iova_domain *domain, map->orig_phys == INVALID_PHYS_ADDR)) return; - addr = page_address(map->bounce_page) + offset; - do_bounce(map->orig_phys + offset, addr, sz, dir); + addr = kmap_local_page(map->bounce_page); + do_bounce(map->orig_phys + offset, addr + offset, sz, dir); + kunmap_local(addr); size -= sz; iova += sz; } @@ -213,21 +213,21 @@ vduse_domain_get_bounce_page(struct vduse_iova_domain *domain, u64 iova) struct vduse_bounce_map *map; struct page *page = NULL; - spin_lock(&domain->iotlb_lock); + read_lock(&domain->bounce_lock); map = &domain->bounce_maps[iova >> PAGE_SHIFT]; - if (!map->bounce_page) + if (domain->user_bounce_pages || !map->bounce_page) goto out; page = map->bounce_page; get_page(page); out: - spin_unlock(&domain->iotlb_lock); + read_unlock(&domain->bounce_lock); return page; } static void -vduse_domain_free_bounce_pages(struct vduse_iova_domain *domain) +vduse_domain_free_kernel_bounce_pages(struct vduse_iova_domain *domain) { struct vduse_bounce_map *map; unsigned long pfn, bounce_pfns; @@ -247,6 +247,73 @@ vduse_domain_free_bounce_pages(struct vduse_iova_domain *domain) } } +int vduse_domain_add_user_bounce_pages(struct vduse_iova_domain *domain, + struct page **pages, int count) +{ + struct vduse_bounce_map *map; + int i, ret; + + /* Now we don't support partial mapping */ + if (count != (domain->bounce_size >> PAGE_SHIFT)) + return -EINVAL; + + write_lock(&domain->bounce_lock); + ret = -EEXIST; + if (domain->user_bounce_pages) + goto out; + + for (i = 0; i < count; i++) { + map = &domain->bounce_maps[i]; + if (map->bounce_page) { + /* Copy kernel page to user page if it's in use */ + if (map->orig_phys != INVALID_PHYS_ADDR) + memcpy_to_page(pages[i], 0, + page_address(map->bounce_page), + PAGE_SIZE); + __free_page(map->bounce_page); + } + map->bounce_page = pages[i]; + get_page(pages[i]); + } + domain->user_bounce_pages = true; + ret = 0; +out: + write_unlock(&domain->bounce_lock); + + return ret; +} + +void vduse_domain_remove_user_bounce_pages(struct vduse_iova_domain *domain) +{ + struct vduse_bounce_map *map; + unsigned long i, count; + + write_lock(&domain->bounce_lock); + if (!domain->user_bounce_pages) + goto out; + + count = domain->bounce_size >> PAGE_SHIFT; + for (i = 0; i < count; i++) { + struct page *page = NULL; + + map = &domain->bounce_maps[i]; + if (WARN_ON(!map->bounce_page)) + continue; + + /* Copy user page to kernel page if it's in use */ + if (map->orig_phys != INVALID_PHYS_ADDR) { + page = alloc_page(GFP_ATOMIC | __GFP_NOFAIL); + memcpy_from_page(page_address(page), + map->bounce_page, 0, PAGE_SIZE); + } + put_page(map->bounce_page); + map->bounce_page = page; + } + domain->user_bounce_pages = false; +out: + write_unlock(&domain->bounce_lock); +} + void vduse_domain_reset_bounce_map(struct vduse_iova_domain *domain) { if (!domain->bounce_map) @@ -322,13 +389,18 @@ dma_addr_t vduse_domain_map_page(struct vduse_iova_domain *domain, if (vduse_domain_init_bounce_map(domain)) goto err; + read_lock(&domain->bounce_lock); if (vduse_domain_map_bounce_page(domain, (u64)iova, (u64)size, pa)) - goto err; + goto err_unlock; if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) vduse_domain_bounce(domain, iova, size, DMA_TO_DEVICE); + read_unlock(&domain->bounce_lock); + return iova; +err_unlock: + read_unlock(&domain->bounce_lock); err: vduse_domain_free_iova(iovad, iova, size); return DMA_MAPPING_ERROR; @@ -340,10 +412,12 @@ void vduse_domain_unmap_page(struct vduse_iova_domain *domain, { struct iova_domain *iovad = &domain->stream_iovad; + read_lock(&domain->bounce_lock); if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) vduse_domain_bounce(domain, dma_addr, size, DMA_FROM_DEVICE); vduse_domain_unmap_bounce_page(domain, (u64)dma_addr, (u64)size); + read_unlock(&domain->bounce_lock); vduse_domain_free_iova(iovad, dma_addr, size); } @@ -451,7 +525,8 @@ static int vduse_domain_release(struct inode *inode, struct file *file) spin_lock(&domain->iotlb_lock); vduse_iotlb_del_range(domain, 0, ULLONG_MAX); - vduse_domain_free_bounce_pages(domain); + vduse_domain_remove_user_bounce_pages(domain); + vduse_domain_free_kernel_bounce_pages(domain); spin_unlock(&domain->iotlb_lock); put_iova_domain(&domain->stream_iovad); put_iova_domain(&domain->consistent_iovad); @@ -511,6 +586,7 @@ vduse_domain_create(unsigned long iova_limit, size_t bounce_size) goto err_file; domain->file = file; + rwlock_init(&domain->bounce_lock); spin_lock_init(&domain->iotlb_lock); init_iova_domain(&domain->stream_iovad, PAGE_SIZE, IOVA_START_PFN); diff --git a/drivers/vdpa/vdpa_user/iova_domain.h b/drivers/vdpa/vdpa_user/iova_domain.h index 2722d9b8e21a..4e0e50e7ac15 100644 --- a/drivers/vdpa/vdpa_user/iova_domain.h +++ b/drivers/vdpa/vdpa_user/iova_domain.h @@ -14,6 +14,7 @@ #include <linux/iova.h> #include <linux/dma-mapping.h> #include <linux/vhost_iotlb.h> +#include <linux/rwlock.h> #define IOVA_START_PFN 1 @@ -34,6 +35,8 @@ struct vduse_iova_domain { struct vhost_iotlb *iotlb; spinlock_t iotlb_lock; struct file *file; + bool user_bounce_pages; + rwlock_t bounce_lock; }; int vduse_domain_set_map(struct vduse_iova_domain *domain, @@ -61,6 +64,11 @@ void vduse_domain_free_coherent(struct vduse_iova_domain *domain, size_t size, void vduse_domain_reset_bounce_map(struct vduse_iova_domain *domain); +int vduse_domain_add_user_bounce_pages(struct vduse_iova_domain *domain, + struct page **pages, int count); + +void vduse_domain_remove_user_bounce_pages(struct vduse_iova_domain *domain); + void vduse_domain_destroy(struct vduse_iova_domain *domain); struct vduse_iova_domain *vduse_domain_create(unsigned long iova_limit, diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c index 3bc27de58f46..41c0b29739f1 100644 --- a/drivers/vdpa/vdpa_user/vduse_dev.c +++ b/drivers/vdpa/vdpa_user/vduse_dev.c @@ -21,6 +21,8 @@ #include <linux/uio.h> #include <linux/vdpa.h> #include <linux/nospec.h> +#include <linux/vmalloc.h> +#include <linux/sched/mm.h> #include <uapi/linux/vduse.h> #include <uapi/linux/vdpa.h> #include <uapi/linux/virtio_config.h> @@ -64,6 +66,13 @@ struct vduse_vdpa { struct vduse_dev *dev; }; +struct vduse_umem { + unsigned long iova; + unsigned long npages; + struct page **pages; + struct mm_struct *mm; +}; + struct vduse_dev { struct vduse_vdpa *vdev; struct device *dev; @@ -95,6 +104,8 @@ struct vduse_dev { u8 status; u32 vq_num; u32 vq_align; + struct vduse_umem *umem; + struct mutex mem_lock; }; struct vduse_dev_msg { @@ -917,6 +928,102 @@ unlock: return ret; } +static int vduse_dev_dereg_umem(struct vduse_dev *dev, + u64 iova, u64 size) +{ + int ret; + + mutex_lock(&dev->mem_lock); + ret = -ENOENT; + if (!dev->umem) + goto unlock; + + ret = -EINVAL; + if (dev->umem->iova != iova || size != dev->domain->bounce_size) + goto unlock; + + vduse_domain_remove_user_bounce_pages(dev->domain); + unpin_user_pages_dirty_lock(dev->umem->pages, + dev->umem->npages, true); + atomic64_sub(dev->umem->npages, &dev->umem->mm->pinned_vm); + mmdrop(dev->umem->mm); + vfree(dev->umem->pages); + kfree(dev->umem); + dev->umem = NULL; + ret = 0; +unlock: + mutex_unlock(&dev->mem_lock); + return ret; +} + +static int vduse_dev_reg_umem(struct vduse_dev *dev, + u64 iova, u64 uaddr, u64 size) +{ + struct page **page_list = NULL; + struct vduse_umem *umem = NULL; + long pinned = 0; + unsigned long npages, lock_limit; + int ret; + + if (!dev->domain->bounce_map || + size != dev->domain->bounce_size || + iova != 0 || uaddr & ~PAGE_MASK) + return -EINVAL; + + mutex_lock(&dev->mem_lock); + ret = -EEXIST; + if (dev->umem) + goto unlock; + + ret = -ENOMEM; + npages = size >> PAGE_SHIFT; + page_list = __vmalloc(array_size(npages, sizeof(struct page *)), + GFP_KERNEL_ACCOUNT); + umem = kzalloc(sizeof(*umem), GFP_KERNEL); + if (!page_list || !umem) + goto unlock; + + mmap_read_lock(current->mm); + + lock_limit = PFN_DOWN(rlimit(RLIMIT_MEMLOCK)); + if (npages + atomic64_read(¤t->mm->pinned_vm) > lock_limit) + goto out; + + pinned = pin_user_pages(uaddr, npages, FOLL_LONGTERM | FOLL_WRITE, + page_list, NULL); + if (pinned != npages) { + ret = pinned < 0 ? pinned : -ENOMEM; + goto out; + } + + ret = vduse_domain_add_user_bounce_pages(dev->domain, + page_list, pinned); + if (ret) + goto out; + + atomic64_add(npages, ¤t->mm->pinned_vm); + + umem->pages = page_list; + umem->npages = pinned; + umem->iova = iova; + umem->mm = current->mm; + mmgrab(current->mm); + + dev->umem = umem; +out: + if (ret && pinned > 0) + unpin_user_pages(page_list, pinned); + + mmap_read_unlock(current->mm); +unlock: + if (ret) { + vfree(page_list); + kfree(umem); + } + mutex_unlock(&dev->mem_lock); + return ret; +} + static long vduse_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -1089,6 +1196,77 @@ static long vduse_dev_ioctl(struct file *file, unsigned int cmd, ret = vduse_dev_queue_irq_work(dev, &dev->vqs[index].inject); break; } + case VDUSE_IOTLB_REG_UMEM: { + struct vduse_iova_umem umem; + + ret = -EFAULT; + if (copy_from_user(&umem, argp, sizeof(umem))) + break; + + ret = -EINVAL; + if (!is_mem_zero((const char *)umem.reserved, + sizeof(umem.reserved))) + break; + + ret = vduse_dev_reg_umem(dev, umem.iova, + umem.uaddr, umem.size); + break; + } + case VDUSE_IOTLB_DEREG_UMEM: { + struct vduse_iova_umem umem; + + ret = -EFAULT; + if (copy_from_user(&umem, argp, sizeof(umem))) + break; + + ret = -EINVAL; + if (!is_mem_zero((const char *)umem.reserved, + sizeof(umem.reserved))) + break; + + ret = vduse_dev_dereg_umem(dev, umem.iova, + umem.size); + break; + } + case VDUSE_IOTLB_GET_INFO: { + struct vduse_iova_info info; + struct vhost_iotlb_map *map; + struct vduse_iova_domain *domain = dev->domain; + + ret = -EFAULT; + if (copy_from_user(&info, argp, sizeof(info))) + break; + + ret = -EINVAL; + if (info.start > info.last) + break; + + if (!is_mem_zero((const char *)info.reserved, + sizeof(info.reserved))) + break; + + spin_lock(&domain->iotlb_lock); + map = vhost_iotlb_itree_first(domain->iotlb, + info.start, info.last); + if (map) { + info.start = map->start; + info.last = map->last; + info.capability = 0; + if (domain->bounce_map && map->start == 0 && + map->last == domain->bounce_size - 1) + info.capability |= VDUSE_IOVA_CAP_UMEM; + } + spin_unlock(&domain->iotlb_lock); + if (!map) + break; + + ret = -EFAULT; + if (copy_to_user(argp, &info, sizeof(info))) + break; + + ret = 0; + break; + } default: ret = -ENOIOCTLCMD; break; @@ -1101,6 +1279,7 @@ static int vduse_dev_release(struct inode *inode, struct file *file) { struct vduse_dev *dev = file->private_data; + vduse_dev_dereg_umem(dev, 0, dev->domain->bounce_size); spin_lock(&dev->msg_lock); /* Make sure the inflight messages can processed after reconncection */ list_splice_init(&dev->recv_list, &dev->send_list); @@ -1163,6 +1342,7 @@ static struct vduse_dev *vduse_dev_create(void) return NULL; mutex_init(&dev->lock); + mutex_init(&dev->mem_lock); spin_lock_init(&dev->msg_lock); INIT_LIST_HEAD(&dev->send_list); INIT_LIST_HEAD(&dev->recv_list); diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile index fee73f3d9480..1a32357592e3 100644 --- a/drivers/vfio/Makefile +++ b/drivers/vfio/Makefile @@ -1,6 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 vfio_virqfd-y := virqfd.o +vfio-y += vfio_main.o + obj-$(CONFIG_VFIO) += vfio.o obj-$(CONFIG_VFIO_VIRQFD) += vfio_virqfd.o obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio_main.c index 7cb56c382c97..7cb56c382c97 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio_main.c diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 9b65509424dc..7ebf106d50c1 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -159,9 +159,13 @@ enum { }; #define VHOST_SCSI_MAX_TARGET 256 -#define VHOST_SCSI_MAX_VQ 128 +#define VHOST_SCSI_MAX_IO_VQ 1024 #define VHOST_SCSI_MAX_EVENT 128 +static unsigned vhost_scsi_max_io_vqs = 128; +module_param_named(max_io_vqs, vhost_scsi_max_io_vqs, uint, 0644); +MODULE_PARM_DESC(max_io_vqs, "Set the max number of IO virtqueues a vhost scsi device can support. The default is 128. The max is 1024."); + struct vhost_scsi_virtqueue { struct vhost_virtqueue vq; /* @@ -186,7 +190,9 @@ struct vhost_scsi { char vs_vhost_wwpn[TRANSPORT_IQN_LEN]; struct vhost_dev dev; - struct vhost_scsi_virtqueue vqs[VHOST_SCSI_MAX_VQ]; + struct vhost_scsi_virtqueue *vqs; + unsigned long *compl_bitmap; + struct vhost_scsi_inflight **old_inflight; struct vhost_work vs_completion_work; /* cmd completion work item */ struct llist_head vs_completion_list; /* cmd completion queue */ @@ -245,7 +251,7 @@ static void vhost_scsi_init_inflight(struct vhost_scsi *vs, struct vhost_virtqueue *vq; int idx, i; - for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) { + for (i = 0; i < vs->dev.nvqs; i++) { vq = &vs->vqs[i].vq; mutex_lock(&vq->mutex); @@ -533,7 +539,6 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) { struct vhost_scsi *vs = container_of(work, struct vhost_scsi, vs_completion_work); - DECLARE_BITMAP(signal, VHOST_SCSI_MAX_VQ); struct virtio_scsi_cmd_resp v_rsp; struct vhost_scsi_cmd *cmd, *t; struct llist_node *llnode; @@ -541,7 +546,7 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) struct iov_iter iov_iter; int ret, vq; - bitmap_zero(signal, VHOST_SCSI_MAX_VQ); + bitmap_zero(vs->compl_bitmap, vs->dev.nvqs); llnode = llist_del_all(&vs->vs_completion_list); llist_for_each_entry_safe(cmd, t, llnode, tvc_completion_list) { se_cmd = &cmd->tvc_se_cmd; @@ -566,7 +571,7 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) vhost_add_used(cmd->tvc_vq, cmd->tvc_vq_desc, 0); q = container_of(cmd->tvc_vq, struct vhost_scsi_virtqueue, vq); vq = q - vs->vqs; - __set_bit(vq, signal); + __set_bit(vq, vs->compl_bitmap); } else pr_err("Faulted on virtio_scsi_cmd_resp\n"); @@ -574,8 +579,8 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) } vq = -1; - while ((vq = find_next_bit(signal, VHOST_SCSI_MAX_VQ, vq + 1)) - < VHOST_SCSI_MAX_VQ) + while ((vq = find_next_bit(vs->compl_bitmap, vs->dev.nvqs, vq + 1)) + < vs->dev.nvqs) vhost_signal(&vs->dev, &vs->vqs[vq].vq); } @@ -1419,26 +1424,25 @@ static void vhost_scsi_handle_kick(struct vhost_work *work) /* Callers must hold dev mutex */ static void vhost_scsi_flush(struct vhost_scsi *vs) { - struct vhost_scsi_inflight *old_inflight[VHOST_SCSI_MAX_VQ]; int i; /* Init new inflight and remember the old inflight */ - vhost_scsi_init_inflight(vs, old_inflight); + vhost_scsi_init_inflight(vs, vs->old_inflight); /* * The inflight->kref was initialized to 1. We decrement it here to * indicate the start of the flush operation so that it will reach 0 * when all the reqs are finished. */ - for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) - kref_put(&old_inflight[i]->kref, vhost_scsi_done_inflight); + for (i = 0; i < vs->dev.nvqs; i++) + kref_put(&vs->old_inflight[i]->kref, vhost_scsi_done_inflight); /* Flush both the vhost poll and vhost work */ vhost_dev_flush(&vs->dev); /* Wait for all reqs issued before the flush to be finished */ - for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) - wait_for_completion(&old_inflight[i]->comp); + for (i = 0; i < vs->dev.nvqs; i++) + wait_for_completion(&vs->old_inflight[i]->comp); } static void vhost_scsi_destroy_vq_cmds(struct vhost_virtqueue *vq) @@ -1601,7 +1605,7 @@ vhost_scsi_set_endpoint(struct vhost_scsi *vs, memcpy(vs->vs_vhost_wwpn, t->vhost_wwpn, sizeof(vs->vs_vhost_wwpn)); - for (i = VHOST_SCSI_VQ_IO; i < VHOST_SCSI_MAX_VQ; i++) { + for (i = VHOST_SCSI_VQ_IO; i < vs->dev.nvqs; i++) { vq = &vs->vqs[i].vq; if (!vhost_vq_is_setup(vq)) continue; @@ -1611,7 +1615,7 @@ vhost_scsi_set_endpoint(struct vhost_scsi *vs, goto destroy_vq_cmds; } - for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) { + for (i = 0; i < vs->dev.nvqs; i++) { vq = &vs->vqs[i].vq; mutex_lock(&vq->mutex); vhost_vq_set_backend(vq, vs_tpg); @@ -1713,7 +1717,7 @@ vhost_scsi_clear_endpoint(struct vhost_scsi *vs, target_undepend_item(&se_tpg->tpg_group.cg_item); } if (match) { - for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) { + for (i = 0; i < vs->dev.nvqs; i++) { vq = &vs->vqs[i].vq; mutex_lock(&vq->mutex); vhost_vq_set_backend(vq, NULL); @@ -1722,7 +1726,7 @@ vhost_scsi_clear_endpoint(struct vhost_scsi *vs, /* Make sure cmds are not running before tearing them down. */ vhost_scsi_flush(vs); - for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) { + for (i = 0; i < vs->dev.nvqs; i++) { vq = &vs->vqs[i].vq; vhost_scsi_destroy_vq_cmds(vq); } @@ -1762,7 +1766,7 @@ static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features) return -EFAULT; } - for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) { + for (i = 0; i < vs->dev.nvqs; i++) { vq = &vs->vqs[i].vq; mutex_lock(&vq->mutex); vq->acked_features = features; @@ -1776,16 +1780,40 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) { struct vhost_scsi *vs; struct vhost_virtqueue **vqs; - int r = -ENOMEM, i; + int r = -ENOMEM, i, nvqs = vhost_scsi_max_io_vqs; vs = kvzalloc(sizeof(*vs), GFP_KERNEL); if (!vs) goto err_vs; - vqs = kmalloc_array(VHOST_SCSI_MAX_VQ, sizeof(*vqs), GFP_KERNEL); - if (!vqs) + if (nvqs > VHOST_SCSI_MAX_IO_VQ) { + pr_err("Invalid max_io_vqs of %d. Using %d.\n", nvqs, + VHOST_SCSI_MAX_IO_VQ); + nvqs = VHOST_SCSI_MAX_IO_VQ; + } else if (nvqs == 0) { + pr_err("Invalid max_io_vqs of %d. Using 1.\n", nvqs); + nvqs = 1; + } + nvqs += VHOST_SCSI_VQ_IO; + + vs->compl_bitmap = bitmap_alloc(nvqs, GFP_KERNEL); + if (!vs->compl_bitmap) + goto err_compl_bitmap; + + vs->old_inflight = kmalloc_array(nvqs, sizeof(*vs->old_inflight), + GFP_KERNEL | __GFP_ZERO); + if (!vs->old_inflight) + goto err_inflight; + + vs->vqs = kmalloc_array(nvqs, sizeof(*vs->vqs), + GFP_KERNEL | __GFP_ZERO); + if (!vs->vqs) goto err_vqs; + vqs = kmalloc_array(nvqs, sizeof(*vqs), GFP_KERNEL); + if (!vqs) + goto err_local_vqs; + vhost_work_init(&vs->vs_completion_work, vhost_scsi_complete_cmd_work); vhost_work_init(&vs->vs_event_work, vhost_scsi_evt_work); @@ -1796,11 +1824,11 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) vqs[VHOST_SCSI_VQ_EVT] = &vs->vqs[VHOST_SCSI_VQ_EVT].vq; vs->vqs[VHOST_SCSI_VQ_CTL].vq.handle_kick = vhost_scsi_ctl_handle_kick; vs->vqs[VHOST_SCSI_VQ_EVT].vq.handle_kick = vhost_scsi_evt_handle_kick; - for (i = VHOST_SCSI_VQ_IO; i < VHOST_SCSI_MAX_VQ; i++) { + for (i = VHOST_SCSI_VQ_IO; i < nvqs; i++) { vqs[i] = &vs->vqs[i].vq; vs->vqs[i].vq.handle_kick = vhost_scsi_handle_kick; } - vhost_dev_init(&vs->dev, vqs, VHOST_SCSI_MAX_VQ, UIO_MAXIOV, + vhost_dev_init(&vs->dev, vqs, nvqs, UIO_MAXIOV, VHOST_SCSI_WEIGHT, 0, true, NULL); vhost_scsi_init_inflight(vs, NULL); @@ -1808,7 +1836,13 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) f->private_data = vs; return 0; +err_local_vqs: + kfree(vs->vqs); err_vqs: + kfree(vs->old_inflight); +err_inflight: + bitmap_free(vs->compl_bitmap); +err_compl_bitmap: kvfree(vs); err_vs: return r; @@ -1826,6 +1860,9 @@ static int vhost_scsi_release(struct inode *inode, struct file *f) vhost_dev_stop(&vs->dev); vhost_dev_cleanup(&vs->dev); kfree(vs->dev.vqs); + kfree(vs->vqs); + kfree(vs->old_inflight); + bitmap_free(vs->compl_bitmap); kvfree(vs); return 0; } diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index 23dcbfdfa13b..166044642fd5 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -347,6 +347,14 @@ static long vhost_vdpa_set_config(struct vhost_vdpa *v, return 0; } +static bool vhost_vdpa_can_suspend(const struct vhost_vdpa *v) +{ + struct vdpa_device *vdpa = v->vdpa; + const struct vdpa_config_ops *ops = vdpa->config; + + return ops->suspend; +} + static long vhost_vdpa_get_features(struct vhost_vdpa *v, u64 __user *featurep) { struct vdpa_device *vdpa = v->vdpa; @@ -470,6 +478,22 @@ static long vhost_vdpa_get_vqs_count(struct vhost_vdpa *v, u32 __user *argp) return 0; } +/* After a successful return of ioctl the device must not process more + * virtqueue descriptors. The device can answer to read or writes of config + * fields as if it were not suspended. In particular, writing to "queue_enable" + * with a value of 1 will not make the device start processing buffers. + */ +static long vhost_vdpa_suspend(struct vhost_vdpa *v) +{ + struct vdpa_device *vdpa = v->vdpa; + const struct vdpa_config_ops *ops = vdpa->config; + + if (!ops->suspend) + return -EOPNOTSUPP; + + return ops->suspend(vdpa); +} + static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, void __user *argp) { @@ -577,7 +601,11 @@ static long vhost_vdpa_unlocked_ioctl(struct file *filep, if (cmd == VHOST_SET_BACKEND_FEATURES) { if (copy_from_user(&features, featurep, sizeof(features))) return -EFAULT; - if (features & ~VHOST_VDPA_BACKEND_FEATURES) + if (features & ~(VHOST_VDPA_BACKEND_FEATURES | + BIT_ULL(VHOST_BACKEND_F_SUSPEND))) + return -EOPNOTSUPP; + if ((features & BIT_ULL(VHOST_BACKEND_F_SUSPEND)) && + !vhost_vdpa_can_suspend(v)) return -EOPNOTSUPP; vhost_set_backend_features(&v->vdev, features); return 0; @@ -628,6 +656,8 @@ static long vhost_vdpa_unlocked_ioctl(struct file *filep, break; case VHOST_GET_BACKEND_FEATURES: features = VHOST_VDPA_BACKEND_FEATURES; + if (vhost_vdpa_can_suspend(v)) + features |= BIT_ULL(VHOST_BACKEND_F_SUSPEND); if (copy_to_user(featurep, &features, sizeof(features))) r = -EFAULT; break; @@ -640,6 +670,9 @@ static long vhost_vdpa_unlocked_ioctl(struct file *filep, case VHOST_VDPA_GET_VQS_COUNT: r = vhost_vdpa_get_vqs_count(v, argp); break; + case VHOST_VDPA_SUSPEND: + r = vhost_vdpa_suspend(v); + break; default: r = vhost_dev_ioctl(&v->vdev, cmd, argp); if (r == -ENOIOCTLCMD) @@ -1076,7 +1109,7 @@ static int vhost_vdpa_alloc_domain(struct vhost_vdpa *v) if (!bus) return -EFAULT; - if (!iommu_capable(bus, IOMMU_CAP_CACHE_COHERENCY)) + if (!device_iommu_capable(dma_dev, IOMMU_CAP_CACHE_COHERENCY)) return -ENOTSUPP; v->domain = iommu_domain_alloc(bus); @@ -1363,6 +1396,7 @@ static int vhost_vdpa_probe(struct vdpa_device *vdpa) err: put_device(&v->dev); + ida_simple_remove(&vhost_vdpa_ida, v->minor); return r; } diff --git a/drivers/vhost/vringh.c b/drivers/vhost/vringh.c index eab55accf381..11f59dd06a74 100644 --- a/drivers/vhost/vringh.c +++ b/drivers/vhost/vringh.c @@ -1095,7 +1095,8 @@ EXPORT_SYMBOL(vringh_need_notify_kern); #if IS_REACHABLE(CONFIG_VHOST_IOTLB) static int iotlb_translate(const struct vringh *vrh, - u64 addr, u64 len, struct bio_vec iov[], + u64 addr, u64 len, u64 *translated, + struct bio_vec iov[], int iov_size, u32 perm) { struct vhost_iotlb_map *map; @@ -1136,43 +1137,76 @@ static int iotlb_translate(const struct vringh *vrh, spin_unlock(vrh->iotlb_lock); + if (translated) + *translated = min(len, s); + return ret; } static inline int copy_from_iotlb(const struct vringh *vrh, void *dst, void *src, size_t len) { - struct iov_iter iter; - struct bio_vec iov[16]; - int ret; + u64 total_translated = 0; - ret = iotlb_translate(vrh, (u64)(uintptr_t)src, - len, iov, 16, VHOST_MAP_RO); - if (ret < 0) - return ret; + while (total_translated < len) { + struct bio_vec iov[16]; + struct iov_iter iter; + u64 translated; + int ret; - iov_iter_bvec(&iter, READ, iov, ret, len); + ret = iotlb_translate(vrh, (u64)(uintptr_t)src, + len - total_translated, &translated, + iov, ARRAY_SIZE(iov), VHOST_MAP_RO); + if (ret == -ENOBUFS) + ret = ARRAY_SIZE(iov); + else if (ret < 0) + return ret; - ret = copy_from_iter(dst, len, &iter); + iov_iter_bvec(&iter, READ, iov, ret, translated); - return ret; + ret = copy_from_iter(dst, translated, &iter); + if (ret < 0) + return ret; + + src += translated; + dst += translated; + total_translated += translated; + } + + return total_translated; } static inline int copy_to_iotlb(const struct vringh *vrh, void *dst, void *src, size_t len) { - struct iov_iter iter; - struct bio_vec iov[16]; - int ret; + u64 total_translated = 0; - ret = iotlb_translate(vrh, (u64)(uintptr_t)dst, - len, iov, 16, VHOST_MAP_WO); - if (ret < 0) - return ret; + while (total_translated < len) { + struct bio_vec iov[16]; + struct iov_iter iter; + u64 translated; + int ret; + + ret = iotlb_translate(vrh, (u64)(uintptr_t)dst, + len - total_translated, &translated, + iov, ARRAY_SIZE(iov), VHOST_MAP_WO); + if (ret == -ENOBUFS) + ret = ARRAY_SIZE(iov); + else if (ret < 0) + return ret; - iov_iter_bvec(&iter, WRITE, iov, ret, len); + iov_iter_bvec(&iter, WRITE, iov, ret, translated); + + ret = copy_to_iter(src, translated, &iter); + if (ret < 0) + return ret; + + src += translated; + dst += translated; + total_translated += translated; + } - return copy_to_iter(src, len, &iter); + return total_translated; } static inline int getu16_iotlb(const struct vringh *vrh, @@ -1183,7 +1217,7 @@ static inline int getu16_iotlb(const struct vringh *vrh, int ret; /* Atomic read is needed for getu16 */ - ret = iotlb_translate(vrh, (u64)(uintptr_t)p, sizeof(*p), + ret = iotlb_translate(vrh, (u64)(uintptr_t)p, sizeof(*p), NULL, &iov, 1, VHOST_MAP_RO); if (ret < 0) return ret; @@ -1204,7 +1238,7 @@ static inline int putu16_iotlb(const struct vringh *vrh, int ret; /* Atomic write is needed for putu16 */ - ret = iotlb_translate(vrh, (u64)(uintptr_t)p, sizeof(*p), + ret = iotlb_translate(vrh, (u64)(uintptr_t)p, sizeof(*p), NULL, &iov, 1, VHOST_MAP_WO); if (ret < 0) return ret; diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 56c77f63cd22..0a53a61231c2 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -35,11 +35,12 @@ if VIRTIO_MENU config VIRTIO_HARDEN_NOTIFICATION bool "Harden virtio notification" + depends on BROKEN help Enable this to harden the device notifications and suppress those that happen at a time where notifications are illegal. - Experimental: Note that several drivers still have bugs that + Experimental: Note that several drivers still have issues that may cause crashes or hangs when correct handling of notifications is enforced; depending on the subset of drivers and devices you use, this may or may not work. @@ -126,9 +127,11 @@ config VIRTIO_MEM This driver provides access to virtio-mem paravirtualized memory devices, allowing to hotplug and hotunplug memory. - This driver was only tested under x86-64 and arm64, but should - theoretically work on all architectures that support memory hotplug - and hotremove. + This driver currently only supports x86-64 and arm64. Although it + should compile on other architectures that implement memory + hot(un)plug, architecture-specific and/or common + code changes may be required for virtio-mem, kdump and kexec to work as + expected. If unsure, say M. diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index 14c142d77fba..828ced060742 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -428,7 +428,9 @@ int register_virtio_device(struct virtio_device *dev) goto out; dev->index = err; - dev_set_name(&dev->dev, "virtio%u", dev->index); + err = dev_set_name(&dev->dev, "virtio%u", dev->index); + if (err) + goto out_ida_remove; err = virtio_device_of_init(dev); if (err) diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index 083ff1eb743d..c492a57531c6 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -360,7 +360,7 @@ static void vm_synchronize_cbs(struct virtio_device *vdev) static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned int index, void (*callback)(struct virtqueue *vq), - const char *name, bool ctx) + const char *name, u32 size, bool ctx) { struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); struct virtio_mmio_vq_info *info; @@ -395,14 +395,19 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned int in goto error_new_virtqueue; } + if (!size || size > num) + size = num; + /* Create the vring */ - vq = vring_create_virtqueue(index, num, VIRTIO_MMIO_VRING_ALIGN, vdev, + vq = vring_create_virtqueue(index, size, VIRTIO_MMIO_VRING_ALIGN, vdev, true, true, ctx, vm_notify, callback, name); if (!vq) { err = -ENOMEM; goto error_new_virtqueue; } + vq->num_max = num; + /* Activate the queue */ writel(virtqueue_get_vring_size(vq), vm_dev->base + VIRTIO_MMIO_QUEUE_NUM); if (vm_dev->version == 1) { @@ -472,6 +477,7 @@ static int vm_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char * const names[], + u32 sizes[], const bool *ctx, struct irq_affinity *desc) { @@ -487,6 +493,9 @@ static int vm_find_vqs(struct virtio_device *vdev, unsigned int nvqs, if (err) return err; + if (of_property_read_bool(vm_dev->pdev->dev.of_node, "wakeup-source")) + enable_irq_wake(irq); + for (i = 0; i < nvqs; ++i) { if (!names[i]) { vqs[i] = NULL; @@ -494,6 +503,7 @@ static int vm_find_vqs(struct virtio_device *vdev, unsigned int nvqs, } vqs[i] = vm_setup_vq(vdev, queue_idx++, callbacks[i], names[i], + sizes ? sizes[i] : 0, ctx ? ctx[i] : false); if (IS_ERR(vqs[i])) { vm_del_vqs(vdev); diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index ca51fcc9daab..00ad476a815d 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -174,6 +174,7 @@ error: static struct virtqueue *vp_setup_vq(struct virtio_device *vdev, unsigned int index, void (*callback)(struct virtqueue *vq), const char *name, + u32 size, bool ctx, u16 msix_vec) { @@ -186,7 +187,7 @@ static struct virtqueue *vp_setup_vq(struct virtio_device *vdev, unsigned int in if (!info) return ERR_PTR(-ENOMEM); - vq = vp_dev->setup_vq(vp_dev, info, index, callback, name, ctx, + vq = vp_dev->setup_vq(vp_dev, info, index, callback, name, size, ctx, msix_vec); if (IS_ERR(vq)) goto out_info; @@ -214,9 +215,15 @@ static void vp_del_vq(struct virtqueue *vq) struct virtio_pci_vq_info *info = vp_dev->vqs[vq->index]; unsigned long flags; - spin_lock_irqsave(&vp_dev->lock, flags); - list_del(&info->node); - spin_unlock_irqrestore(&vp_dev->lock, flags); + /* + * If it fails during re-enable reset vq. This way we won't rejoin + * info->node to the queue. Prevent unexpected irqs. + */ + if (!vq->reset) { + spin_lock_irqsave(&vp_dev->lock, flags); + list_del(&info->node); + spin_unlock_irqrestore(&vp_dev->lock, flags); + } vp_dev->del_vq(info); kfree(info); @@ -277,7 +284,7 @@ void vp_del_vqs(struct virtio_device *vdev) static int vp_find_vqs_msix(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[], bool per_vq_vectors, + const char * const names[], u32 sizes[], bool per_vq_vectors, const bool *ctx, struct irq_affinity *desc) { @@ -320,8 +327,8 @@ static int vp_find_vqs_msix(struct virtio_device *vdev, unsigned int nvqs, else msix_vec = VP_MSIX_VQ_VECTOR; vqs[i] = vp_setup_vq(vdev, queue_idx++, callbacks[i], names[i], - ctx ? ctx[i] : false, - msix_vec); + sizes ? sizes[i] : 0, + ctx ? ctx[i] : false, msix_vec); if (IS_ERR(vqs[i])) { err = PTR_ERR(vqs[i]); goto error_find; @@ -351,7 +358,7 @@ error_find: static int vp_find_vqs_intx(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[], const bool *ctx) + const char * const names[], u32 sizes[], const bool *ctx) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); int i, err, queue_idx = 0; @@ -373,6 +380,7 @@ static int vp_find_vqs_intx(struct virtio_device *vdev, unsigned int nvqs, continue; } vqs[i] = vp_setup_vq(vdev, queue_idx++, callbacks[i], names[i], + sizes ? sizes[i] : 0, ctx ? ctx[i] : false, VIRTIO_MSI_NO_VECTOR); if (IS_ERR(vqs[i])) { @@ -390,21 +398,21 @@ out_del_vqs: /* the config->find_vqs() implementation */ int vp_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[], const bool *ctx, + const char * const names[], u32 sizes[], const bool *ctx, struct irq_affinity *desc) { int err; /* Try MSI-X with one vector per queue. */ - err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, true, ctx, desc); + err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, sizes, true, ctx, desc); if (!err) return 0; /* Fallback: MSI-X with one vector for config, one shared for queues. */ - err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, false, ctx, desc); + err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, sizes, false, ctx, desc); if (!err) return 0; /* Finally fall back to regular interrupts. */ - return vp_find_vqs_intx(vdev, nvqs, vqs, callbacks, names, ctx); + return vp_find_vqs_intx(vdev, nvqs, vqs, callbacks, names, sizes, ctx); } const char *vp_bus_name(struct virtio_device *vdev) diff --git a/drivers/virtio/virtio_pci_common.h b/drivers/virtio/virtio_pci_common.h index 23112d84218f..c0448378b698 100644 --- a/drivers/virtio/virtio_pci_common.h +++ b/drivers/virtio/virtio_pci_common.h @@ -80,6 +80,7 @@ struct virtio_pci_device { unsigned int idx, void (*callback)(struct virtqueue *vq), const char *name, + u32 size, bool ctx, u16 msix_vec); void (*del_vq)(struct virtio_pci_vq_info *info); @@ -110,7 +111,7 @@ void vp_del_vqs(struct virtio_device *vdev); /* the config->find_vqs() implementation */ int vp_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[], const bool *ctx, + const char * const names[], u32 sizes[], const bool *ctx, struct irq_affinity *desc); const char *vp_bus_name(struct virtio_device *vdev); diff --git a/drivers/virtio/virtio_pci_legacy.c b/drivers/virtio/virtio_pci_legacy.c index a5e5721145c7..d75e5c4e637f 100644 --- a/drivers/virtio/virtio_pci_legacy.c +++ b/drivers/virtio/virtio_pci_legacy.c @@ -112,6 +112,7 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, unsigned int index, void (*callback)(struct virtqueue *vq), const char *name, + u32 size, bool ctx, u16 msix_vec) { @@ -125,16 +126,21 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, if (!num || vp_legacy_get_queue_enable(&vp_dev->ldev, index)) return ERR_PTR(-ENOENT); + if (!size || size > num) + size = num; + info->msix_vector = msix_vec; /* create the vring */ - vq = vring_create_virtqueue(index, num, + vq = vring_create_virtqueue(index, size, VIRTIO_PCI_VRING_ALIGN, &vp_dev->vdev, true, false, ctx, vp_notify, callback, name); if (!vq) return ERR_PTR(-ENOMEM); + vq->num_max = num; + q_pfn = virtqueue_get_desc_addr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT; if (q_pfn >> 32) { dev_err(&vp_dev->pci_dev->dev, diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c index 623906b4996c..f7965c5dd36b 100644 --- a/drivers/virtio/virtio_pci_modern.c +++ b/drivers/virtio/virtio_pci_modern.c @@ -34,6 +34,9 @@ static void vp_transport_features(struct virtio_device *vdev, u64 features) if ((features & BIT_ULL(VIRTIO_F_SR_IOV)) && pci_find_ext_capability(pci_dev, PCI_EXT_CAP_ID_SRIOV)) __virtio_set_bit(vdev, VIRTIO_F_SR_IOV); + + if (features & BIT_ULL(VIRTIO_F_RING_RESET)) + __virtio_set_bit(vdev, VIRTIO_F_RING_RESET); } /* virtio config->finalize_features() implementation */ @@ -176,6 +179,110 @@ static void vp_reset(struct virtio_device *vdev) vp_synchronize_vectors(vdev); } +static int vp_active_vq(struct virtqueue *vq, u16 msix_vec) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); + struct virtio_pci_modern_device *mdev = &vp_dev->mdev; + unsigned long index; + + index = vq->index; + + /* activate the queue */ + vp_modern_set_queue_size(mdev, index, virtqueue_get_vring_size(vq)); + vp_modern_queue_address(mdev, index, virtqueue_get_desc_addr(vq), + virtqueue_get_avail_addr(vq), + virtqueue_get_used_addr(vq)); + + if (msix_vec != VIRTIO_MSI_NO_VECTOR) { + msix_vec = vp_modern_queue_vector(mdev, index, msix_vec); + if (msix_vec == VIRTIO_MSI_NO_VECTOR) + return -EBUSY; + } + + return 0; +} + +static int vp_modern_disable_vq_and_reset(struct virtqueue *vq) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); + struct virtio_pci_modern_device *mdev = &vp_dev->mdev; + struct virtio_pci_vq_info *info; + unsigned long flags; + + if (!virtio_has_feature(vq->vdev, VIRTIO_F_RING_RESET)) + return -ENOENT; + + vp_modern_set_queue_reset(mdev, vq->index); + + info = vp_dev->vqs[vq->index]; + + /* delete vq from irq handler */ + spin_lock_irqsave(&vp_dev->lock, flags); + list_del(&info->node); + spin_unlock_irqrestore(&vp_dev->lock, flags); + + INIT_LIST_HEAD(&info->node); + +#ifdef CONFIG_VIRTIO_HARDEN_NOTIFICATION + __virtqueue_break(vq); +#endif + + /* For the case where vq has an exclusive irq, call synchronize_irq() to + * wait for completion. + * + * note: We can't use disable_irq() since it conflicts with the affinity + * managed IRQ that is used by some drivers. + */ + if (vp_dev->per_vq_vectors && info->msix_vector != VIRTIO_MSI_NO_VECTOR) + synchronize_irq(pci_irq_vector(vp_dev->pci_dev, info->msix_vector)); + + vq->reset = true; + + return 0; +} + +static int vp_modern_enable_vq_after_reset(struct virtqueue *vq) +{ + struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); + struct virtio_pci_modern_device *mdev = &vp_dev->mdev; + struct virtio_pci_vq_info *info; + unsigned long flags, index; + int err; + + if (!vq->reset) + return -EBUSY; + + index = vq->index; + info = vp_dev->vqs[index]; + + if (vp_modern_get_queue_reset(mdev, index)) + return -EBUSY; + + if (vp_modern_get_queue_enable(mdev, index)) + return -EBUSY; + + err = vp_active_vq(vq, info->msix_vector); + if (err) + return err; + + if (vq->callback) { + spin_lock_irqsave(&vp_dev->lock, flags); + list_add(&info->node, &vp_dev->virtqueues); + spin_unlock_irqrestore(&vp_dev->lock, flags); + } else { + INIT_LIST_HEAD(&info->node); + } + +#ifdef CONFIG_VIRTIO_HARDEN_NOTIFICATION + __virtqueue_unbreak(vq); +#endif + + vp_modern_set_queue_enable(&vp_dev->mdev, index, true); + vq->reset = false; + + return 0; +} + static u16 vp_config_vector(struct virtio_pci_device *vp_dev, u16 vector) { return vp_modern_config_vector(&vp_dev->mdev, vector); @@ -186,6 +293,7 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, unsigned int index, void (*callback)(struct virtqueue *vq), const char *name, + u32 size, bool ctx, u16 msix_vec) { @@ -203,47 +311,39 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev, if (!num || vp_modern_get_queue_enable(mdev, index)) return ERR_PTR(-ENOENT); - if (num & (num - 1)) { - dev_warn(&vp_dev->pci_dev->dev, "bad queue size %u", num); + if (!size || size > num) + size = num; + + if (size & (size - 1)) { + dev_warn(&vp_dev->pci_dev->dev, "bad queue size %u", size); return ERR_PTR(-EINVAL); } info->msix_vector = msix_vec; /* create the vring */ - vq = vring_create_virtqueue(index, num, + vq = vring_create_virtqueue(index, size, SMP_CACHE_BYTES, &vp_dev->vdev, true, true, ctx, vp_notify, callback, name); if (!vq) return ERR_PTR(-ENOMEM); - /* activate the queue */ - vp_modern_set_queue_size(mdev, index, virtqueue_get_vring_size(vq)); - vp_modern_queue_address(mdev, index, virtqueue_get_desc_addr(vq), - virtqueue_get_avail_addr(vq), - virtqueue_get_used_addr(vq)); + vq->num_max = num; + + err = vp_active_vq(vq, msix_vec); + if (err) + goto err; vq->priv = (void __force *)vp_modern_map_vq_notify(mdev, index, NULL); if (!vq->priv) { err = -ENOMEM; - goto err_map_notify; - } - - if (msix_vec != VIRTIO_MSI_NO_VECTOR) { - msix_vec = vp_modern_queue_vector(mdev, index, msix_vec); - if (msix_vec == VIRTIO_MSI_NO_VECTOR) { - err = -EBUSY; - goto err_assign_vector; - } + goto err; } return vq; -err_assign_vector: - if (!mdev->notify_base) - pci_iounmap(mdev->pci_dev, (void __iomem __force *)vq->priv); -err_map_notify: +err: vring_del_virtqueue(vq); return ERR_PTR(err); } @@ -251,12 +351,15 @@ err_map_notify: static int vp_modern_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[], const bool *ctx, + const char * const names[], + u32 sizes[], + const bool *ctx, struct irq_affinity *desc) { struct virtio_pci_device *vp_dev = to_vp_device(vdev); struct virtqueue *vq; - int rc = vp_find_vqs(vdev, nvqs, vqs, callbacks, names, ctx, desc); + int rc = vp_find_vqs(vdev, nvqs, vqs, callbacks, names, sizes, ctx, + desc); if (rc) return rc; @@ -401,6 +504,8 @@ static const struct virtio_config_ops virtio_pci_config_nodev_ops = { .set_vq_affinity = vp_set_vq_affinity, .get_vq_affinity = vp_get_vq_affinity, .get_shm_region = vp_get_shm_region, + .disable_vq_and_reset = vp_modern_disable_vq_and_reset, + .enable_vq_after_reset = vp_modern_enable_vq_after_reset, }; static const struct virtio_config_ops virtio_pci_config_ops = { @@ -419,6 +524,8 @@ static const struct virtio_config_ops virtio_pci_config_ops = { .set_vq_affinity = vp_set_vq_affinity, .get_vq_affinity = vp_get_vq_affinity, .get_shm_region = vp_get_shm_region, + .disable_vq_and_reset = vp_modern_disable_vq_and_reset, + .enable_vq_after_reset = vp_modern_enable_vq_after_reset, }; /* the PCI probing function */ diff --git a/drivers/virtio/virtio_pci_modern_dev.c b/drivers/virtio/virtio_pci_modern_dev.c index fa2a9445bb18..869cb46bef96 100644 --- a/drivers/virtio/virtio_pci_modern_dev.c +++ b/drivers/virtio/virtio_pci_modern_dev.c @@ -3,6 +3,7 @@ #include <linux/virtio_pci_modern.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/delay.h> /* * vp_modern_map_capability - map a part of virtio pci capability @@ -475,6 +476,44 @@ void vp_modern_set_status(struct virtio_pci_modern_device *mdev, EXPORT_SYMBOL_GPL(vp_modern_set_status); /* + * vp_modern_get_queue_reset - get the queue reset status + * @mdev: the modern virtio-pci device + * @index: queue index + */ +int vp_modern_get_queue_reset(struct virtio_pci_modern_device *mdev, u16 index) +{ + struct virtio_pci_modern_common_cfg __iomem *cfg; + + cfg = (struct virtio_pci_modern_common_cfg __iomem *)mdev->common; + + vp_iowrite16(index, &cfg->cfg.queue_select); + return vp_ioread16(&cfg->queue_reset); +} +EXPORT_SYMBOL_GPL(vp_modern_get_queue_reset); + +/* + * vp_modern_set_queue_reset - reset the queue + * @mdev: the modern virtio-pci device + * @index: queue index + */ +void vp_modern_set_queue_reset(struct virtio_pci_modern_device *mdev, u16 index) +{ + struct virtio_pci_modern_common_cfg __iomem *cfg; + + cfg = (struct virtio_pci_modern_common_cfg __iomem *)mdev->common; + + vp_iowrite16(index, &cfg->cfg.queue_select); + vp_iowrite16(1, &cfg->queue_reset); + + while (vp_ioread16(&cfg->queue_reset)) + msleep(1); + + while (vp_ioread16(&cfg->cfg.queue_enable)) + msleep(1); +} +EXPORT_SYMBOL_GPL(vp_modern_set_queue_reset); + +/* * vp_modern_queue_vector - set the MSIX vector for a specific virtqueue * @mdev: the modern virtio-pci device * @index: queue index diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 643ca779fcc6..d66c8e6d0ef3 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -85,6 +85,71 @@ struct vring_desc_extra { u16 next; /* The next desc state in a list. */ }; +struct vring_virtqueue_split { + /* Actual memory layout for this queue. */ + struct vring vring; + + /* Last written value to avail->flags */ + u16 avail_flags_shadow; + + /* + * Last written value to avail->idx in + * guest byte order. + */ + u16 avail_idx_shadow; + + /* Per-descriptor state. */ + struct vring_desc_state_split *desc_state; + struct vring_desc_extra *desc_extra; + + /* DMA address and size information */ + dma_addr_t queue_dma_addr; + size_t queue_size_in_bytes; + + /* + * The parameters for creating vrings are reserved for creating new + * vring. + */ + u32 vring_align; + bool may_reduce_num; +}; + +struct vring_virtqueue_packed { + /* Actual memory layout for this queue. */ + struct { + unsigned int num; + struct vring_packed_desc *desc; + struct vring_packed_desc_event *driver; + struct vring_packed_desc_event *device; + } vring; + + /* Driver ring wrap counter. */ + bool avail_wrap_counter; + + /* Avail used flags. */ + u16 avail_used_flags; + + /* Index of the next avail descriptor. */ + u16 next_avail_idx; + + /* + * Last written value to driver->flags in + * guest byte order. + */ + u16 event_flags_shadow; + + /* Per-descriptor state. */ + struct vring_desc_state_packed *desc_state; + struct vring_desc_extra *desc_extra; + + /* DMA address and size information */ + dma_addr_t ring_dma_addr; + dma_addr_t driver_event_dma_addr; + dma_addr_t device_event_dma_addr; + size_t ring_size_in_bytes; + size_t event_size_in_bytes; +}; + struct vring_virtqueue { struct virtqueue vq; @@ -124,64 +189,10 @@ struct vring_virtqueue { union { /* Available for split ring */ - struct { - /* Actual memory layout for this queue. */ - struct vring vring; - - /* Last written value to avail->flags */ - u16 avail_flags_shadow; - - /* - * Last written value to avail->idx in - * guest byte order. - */ - u16 avail_idx_shadow; - - /* Per-descriptor state. */ - struct vring_desc_state_split *desc_state; - struct vring_desc_extra *desc_extra; - - /* DMA address and size information */ - dma_addr_t queue_dma_addr; - size_t queue_size_in_bytes; - } split; + struct vring_virtqueue_split split; /* Available for packed ring */ - struct { - /* Actual memory layout for this queue. */ - struct { - unsigned int num; - struct vring_packed_desc *desc; - struct vring_packed_desc_event *driver; - struct vring_packed_desc_event *device; - } vring; - - /* Driver ring wrap counter. */ - bool avail_wrap_counter; - - /* Avail used flags. */ - u16 avail_used_flags; - - /* Index of the next avail descriptor. */ - u16 next_avail_idx; - - /* - * Last written value to driver->flags in - * guest byte order. - */ - u16 event_flags_shadow; - - /* Per-descriptor state. */ - struct vring_desc_state_packed *desc_state; - struct vring_desc_extra *desc_extra; - - /* DMA address and size information */ - dma_addr_t ring_dma_addr; - dma_addr_t driver_event_dma_addr; - dma_addr_t device_event_dma_addr; - size_t ring_size_in_bytes; - size_t event_size_in_bytes; - } packed; + struct vring_virtqueue_packed packed; }; /* How to notify other side. FIXME: commonalize hcalls! */ @@ -200,6 +211,16 @@ struct vring_virtqueue { #endif }; +static struct virtqueue *__vring_new_virtqueue(unsigned int index, + struct vring_virtqueue_split *vring_split, + struct virtio_device *vdev, + bool weak_barriers, + bool context, + bool (*notify)(struct virtqueue *), + void (*callback)(struct virtqueue *), + const char *name); +static struct vring_desc_extra *vring_alloc_desc_extra(unsigned int num); +static void vring_free(struct virtqueue *_vq); /* * Helpers. @@ -364,6 +385,24 @@ static int vring_mapping_error(const struct vring_virtqueue *vq, return dma_mapping_error(vring_dma_dev(vq), addr); } +static void virtqueue_init(struct vring_virtqueue *vq, u32 num) +{ + vq->vq.num_free = num; + + if (vq->packed_ring) + vq->last_used_idx = 0 | (1 << VRING_PACKED_EVENT_F_WRAP_CTR); + else + vq->last_used_idx = 0; + + vq->event_triggered = false; + vq->num_added = 0; + +#ifdef DEBUG + vq->in_use = false; + vq->last_add_time_valid = false; +#endif +} + /* * Split ring specific functions - *_split(). @@ -907,28 +946,107 @@ static void *virtqueue_detach_unused_buf_split(struct virtqueue *_vq) return NULL; } -static struct virtqueue *vring_create_virtqueue_split( - unsigned int index, - unsigned int num, - unsigned int vring_align, - struct virtio_device *vdev, - bool weak_barriers, - bool may_reduce_num, - bool context, - bool (*notify)(struct virtqueue *), - void (*callback)(struct virtqueue *), - const char *name) +static void virtqueue_vring_init_split(struct vring_virtqueue_split *vring_split, + struct vring_virtqueue *vq) +{ + struct virtio_device *vdev; + + vdev = vq->vq.vdev; + + vring_split->avail_flags_shadow = 0; + vring_split->avail_idx_shadow = 0; + + /* No callback? Tell other side not to bother us. */ + if (!vq->vq.callback) { + vring_split->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT; + if (!vq->event) + vring_split->vring.avail->flags = cpu_to_virtio16(vdev, + vring_split->avail_flags_shadow); + } +} + +static void virtqueue_reinit_split(struct vring_virtqueue *vq) +{ + int num; + + num = vq->split.vring.num; + + vq->split.vring.avail->flags = 0; + vq->split.vring.avail->idx = 0; + + /* reset avail event */ + vq->split.vring.avail->ring[num] = 0; + + vq->split.vring.used->flags = 0; + vq->split.vring.used->idx = 0; + + /* reset used event */ + *(__virtio16 *)&(vq->split.vring.used->ring[num]) = 0; + + virtqueue_init(vq, num); + + virtqueue_vring_init_split(&vq->split, vq); +} + +static void virtqueue_vring_attach_split(struct vring_virtqueue *vq, + struct vring_virtqueue_split *vring_split) +{ + vq->split = *vring_split; + + /* Put everything in free lists. */ + vq->free_head = 0; +} + +static int vring_alloc_state_extra_split(struct vring_virtqueue_split *vring_split) +{ + struct vring_desc_state_split *state; + struct vring_desc_extra *extra; + u32 num = vring_split->vring.num; + + state = kmalloc_array(num, sizeof(struct vring_desc_state_split), GFP_KERNEL); + if (!state) + goto err_state; + + extra = vring_alloc_desc_extra(num); + if (!extra) + goto err_extra; + + memset(state, 0, num * sizeof(struct vring_desc_state_split)); + + vring_split->desc_state = state; + vring_split->desc_extra = extra; + return 0; + +err_extra: + kfree(state); +err_state: + return -ENOMEM; +} + +static void vring_free_split(struct vring_virtqueue_split *vring_split, + struct virtio_device *vdev) +{ + vring_free_queue(vdev, vring_split->queue_size_in_bytes, + vring_split->vring.desc, + vring_split->queue_dma_addr); + + kfree(vring_split->desc_state); + kfree(vring_split->desc_extra); +} + +static int vring_alloc_queue_split(struct vring_virtqueue_split *vring_split, + struct virtio_device *vdev, + u32 num, + unsigned int vring_align, + bool may_reduce_num) { - struct virtqueue *vq; void *queue = NULL; dma_addr_t dma_addr; - size_t queue_size_in_bytes; - struct vring vring; /* We assume num is a power of 2. */ if (num & (num - 1)) { dev_warn(&vdev->dev, "Bad virtqueue length %u\n", num); - return NULL; + return -EINVAL; } /* TODO: allocate each queue chunk individually */ @@ -939,11 +1057,11 @@ static struct virtqueue *vring_create_virtqueue_split( if (queue) break; if (!may_reduce_num) - return NULL; + return -ENOMEM; } if (!num) - return NULL; + return -ENOMEM; if (!queue) { /* Try to get a single page. You are my only hope! */ @@ -951,26 +1069,85 @@ static struct virtqueue *vring_create_virtqueue_split( &dma_addr, GFP_KERNEL|__GFP_ZERO); } if (!queue) - return NULL; + return -ENOMEM; + + vring_init(&vring_split->vring, num, queue, vring_align); + + vring_split->queue_dma_addr = dma_addr; + vring_split->queue_size_in_bytes = vring_size(num, vring_align); - queue_size_in_bytes = vring_size(num, vring_align); - vring_init(&vring, num, queue, vring_align); + vring_split->vring_align = vring_align; + vring_split->may_reduce_num = may_reduce_num; - vq = __vring_new_virtqueue(index, vring, vdev, weak_barriers, context, - notify, callback, name); + return 0; +} + +static struct virtqueue *vring_create_virtqueue_split( + unsigned int index, + unsigned int num, + unsigned int vring_align, + struct virtio_device *vdev, + bool weak_barriers, + bool may_reduce_num, + bool context, + bool (*notify)(struct virtqueue *), + void (*callback)(struct virtqueue *), + const char *name) +{ + struct vring_virtqueue_split vring_split = {}; + struct virtqueue *vq; + int err; + + err = vring_alloc_queue_split(&vring_split, vdev, num, vring_align, + may_reduce_num); + if (err) + return NULL; + + vq = __vring_new_virtqueue(index, &vring_split, vdev, weak_barriers, + context, notify, callback, name); if (!vq) { - vring_free_queue(vdev, queue_size_in_bytes, queue, - dma_addr); + vring_free_split(&vring_split, vdev); return NULL; } - to_vvq(vq)->split.queue_dma_addr = dma_addr; - to_vvq(vq)->split.queue_size_in_bytes = queue_size_in_bytes; to_vvq(vq)->we_own_ring = true; return vq; } +static int virtqueue_resize_split(struct virtqueue *_vq, u32 num) +{ + struct vring_virtqueue_split vring_split = {}; + struct vring_virtqueue *vq = to_vvq(_vq); + struct virtio_device *vdev = _vq->vdev; + int err; + + err = vring_alloc_queue_split(&vring_split, vdev, num, + vq->split.vring_align, + vq->split.may_reduce_num); + if (err) + goto err; + + err = vring_alloc_state_extra_split(&vring_split); + if (err) + goto err_state_extra; + + vring_free(&vq->vq); + + virtqueue_vring_init_split(&vring_split, vq); + + virtqueue_init(vq, vring_split.vring.num); + virtqueue_vring_attach_split(vq, &vring_split); + + return 0; + +err_state_extra: + vring_free_split(&vring_split, vdev); +err: + virtqueue_reinit_split(vq); + return -ENOMEM; +} + /* * Packed ring specific functions - *_packed(). @@ -1637,8 +1814,7 @@ static void *virtqueue_detach_unused_buf_packed(struct virtqueue *_vq) return NULL; } -static struct vring_desc_extra *vring_alloc_desc_extra(struct vring_virtqueue *vq, - unsigned int num) +static struct vring_desc_extra *vring_alloc_desc_extra(unsigned int num) { struct vring_desc_extra *desc_extra; unsigned int i; @@ -1656,19 +1832,32 @@ static struct vring_desc_extra *vring_alloc_desc_extra(struct vring_virtqueue *v return desc_extra; } -static struct virtqueue *vring_create_virtqueue_packed( - unsigned int index, - unsigned int num, - unsigned int vring_align, - struct virtio_device *vdev, - bool weak_barriers, - bool may_reduce_num, - bool context, - bool (*notify)(struct virtqueue *), - void (*callback)(struct virtqueue *), - const char *name) +static void vring_free_packed(struct vring_virtqueue_packed *vring_packed, + struct virtio_device *vdev) +{ + if (vring_packed->vring.desc) + vring_free_queue(vdev, vring_packed->ring_size_in_bytes, + vring_packed->vring.desc, + vring_packed->ring_dma_addr); + + if (vring_packed->vring.driver) + vring_free_queue(vdev, vring_packed->event_size_in_bytes, + vring_packed->vring.driver, + vring_packed->driver_event_dma_addr); + + if (vring_packed->vring.device) + vring_free_queue(vdev, vring_packed->event_size_in_bytes, + vring_packed->vring.device, + vring_packed->device_event_dma_addr); + + kfree(vring_packed->desc_state); + kfree(vring_packed->desc_extra); +} + +static int vring_alloc_queue_packed(struct vring_virtqueue_packed *vring_packed, + struct virtio_device *vdev, + u32 num) { - struct vring_virtqueue *vq; struct vring_packed_desc *ring; struct vring_packed_desc_event *driver, *device; dma_addr_t ring_dma_addr, driver_event_dma_addr, device_event_dma_addr; @@ -1680,7 +1869,11 @@ static struct virtqueue *vring_create_virtqueue_packed( &ring_dma_addr, GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO); if (!ring) - goto err_ring; + goto err; + + vring_packed->vring.desc = ring; + vring_packed->ring_dma_addr = ring_dma_addr; + vring_packed->ring_size_in_bytes = ring_size_in_bytes; event_size_in_bytes = sizeof(struct vring_packed_desc_event); @@ -1688,13 +1881,112 @@ static struct virtqueue *vring_create_virtqueue_packed( &driver_event_dma_addr, GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO); if (!driver) - goto err_driver; + goto err; + + vring_packed->vring.driver = driver; + vring_packed->event_size_in_bytes = event_size_in_bytes; + vring_packed->driver_event_dma_addr = driver_event_dma_addr; device = vring_alloc_queue(vdev, event_size_in_bytes, &device_event_dma_addr, GFP_KERNEL|__GFP_NOWARN|__GFP_ZERO); if (!device) - goto err_device; + goto err; + + vring_packed->vring.device = device; + vring_packed->device_event_dma_addr = device_event_dma_addr; + + vring_packed->vring.num = num; + + return 0; + +err: + vring_free_packed(vring_packed, vdev); + return -ENOMEM; +} + +static int vring_alloc_state_extra_packed(struct vring_virtqueue_packed *vring_packed) +{ + struct vring_desc_state_packed *state; + struct vring_desc_extra *extra; + u32 num = vring_packed->vring.num; + + state = kmalloc_array(num, sizeof(struct vring_desc_state_packed), GFP_KERNEL); + if (!state) + goto err_desc_state; + + memset(state, 0, num * sizeof(struct vring_desc_state_packed)); + + extra = vring_alloc_desc_extra(num); + if (!extra) + goto err_desc_extra; + + vring_packed->desc_state = state; + vring_packed->desc_extra = extra; + + return 0; + +err_desc_extra: + kfree(state); +err_desc_state: + return -ENOMEM; +} + +static void virtqueue_vring_init_packed(struct vring_virtqueue_packed *vring_packed, + bool callback) +{ + vring_packed->next_avail_idx = 0; + vring_packed->avail_wrap_counter = 1; + vring_packed->event_flags_shadow = 0; + vring_packed->avail_used_flags = 1 << VRING_PACKED_DESC_F_AVAIL; + + /* No callback? Tell other side not to bother us. */ + if (!callback) { + vring_packed->event_flags_shadow = VRING_PACKED_EVENT_FLAG_DISABLE; + vring_packed->vring.driver->flags = + cpu_to_le16(vring_packed->event_flags_shadow); + } +} + +static void virtqueue_vring_attach_packed(struct vring_virtqueue *vq, + struct vring_virtqueue_packed *vring_packed) +{ + vq->packed = *vring_packed; + + /* Put everything in free lists. */ + vq->free_head = 0; +} + +static void virtqueue_reinit_packed(struct vring_virtqueue *vq) +{ + memset(vq->packed.vring.device, 0, vq->packed.event_size_in_bytes); + memset(vq->packed.vring.driver, 0, vq->packed.event_size_in_bytes); + + /* we need to reset the desc.flags. For more, see is_used_desc_packed() */ + memset(vq->packed.vring.desc, 0, vq->packed.ring_size_in_bytes); + + virtqueue_init(vq, vq->packed.vring.num); + virtqueue_vring_init_packed(&vq->packed, !!vq->vq.callback); +} + +static struct virtqueue *vring_create_virtqueue_packed( + unsigned int index, + unsigned int num, + unsigned int vring_align, + struct virtio_device *vdev, + bool weak_barriers, + bool may_reduce_num, + bool context, + bool (*notify)(struct virtqueue *), + void (*callback)(struct virtqueue *), + const char *name) +{ + struct vring_virtqueue_packed vring_packed = {}; + struct vring_virtqueue *vq; + int err; + + if (vring_alloc_queue_packed(&vring_packed, vdev, num)) + goto err_ring; vq = kmalloc(sizeof(*vq), GFP_KERNEL); if (!vq) @@ -1703,8 +1995,8 @@ static struct virtqueue *vring_create_virtqueue_packed( vq->vq.callback = callback; vq->vq.vdev = vdev; vq->vq.name = name; - vq->vq.num_free = num; vq->vq.index = index; + vq->vq.reset = false; vq->we_own_ring = true; vq->notify = notify; vq->weak_barriers = weak_barriers; @@ -1713,15 +2005,8 @@ static struct virtqueue *vring_create_virtqueue_packed( #else vq->broken = false; #endif - vq->last_used_idx = 0 | (1 << VRING_PACKED_EVENT_F_WRAP_CTR); - vq->event_triggered = false; - vq->num_added = 0; vq->packed_ring = true; vq->use_dma_api = vring_use_dma_api(vdev); -#ifdef DEBUG - vq->in_use = false; - vq->last_add_time_valid = false; -#endif vq->indirect = virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC) && !context; @@ -1730,65 +2015,58 @@ static struct virtqueue *vring_create_virtqueue_packed( if (virtio_has_feature(vdev, VIRTIO_F_ORDER_PLATFORM)) vq->weak_barriers = false; - vq->packed.ring_dma_addr = ring_dma_addr; - vq->packed.driver_event_dma_addr = driver_event_dma_addr; - vq->packed.device_event_dma_addr = device_event_dma_addr; - - vq->packed.ring_size_in_bytes = ring_size_in_bytes; - vq->packed.event_size_in_bytes = event_size_in_bytes; - - vq->packed.vring.num = num; - vq->packed.vring.desc = ring; - vq->packed.vring.driver = driver; - vq->packed.vring.device = device; - - vq->packed.next_avail_idx = 0; - vq->packed.avail_wrap_counter = 1; - vq->packed.event_flags_shadow = 0; - vq->packed.avail_used_flags = 1 << VRING_PACKED_DESC_F_AVAIL; - - vq->packed.desc_state = kmalloc_array(num, - sizeof(struct vring_desc_state_packed), - GFP_KERNEL); - if (!vq->packed.desc_state) - goto err_desc_state; - - memset(vq->packed.desc_state, 0, - num * sizeof(struct vring_desc_state_packed)); - - /* Put everything in free lists. */ - vq->free_head = 0; + err = vring_alloc_state_extra_packed(&vring_packed); + if (err) + goto err_state_extra; - vq->packed.desc_extra = vring_alloc_desc_extra(vq, num); - if (!vq->packed.desc_extra) - goto err_desc_extra; + virtqueue_vring_init_packed(&vring_packed, !!callback); - /* No callback? Tell other side not to bother us. */ - if (!callback) { - vq->packed.event_flags_shadow = VRING_PACKED_EVENT_FLAG_DISABLE; - vq->packed.vring.driver->flags = - cpu_to_le16(vq->packed.event_flags_shadow); - } + virtqueue_init(vq, num); + virtqueue_vring_attach_packed(vq, &vring_packed); spin_lock(&vdev->vqs_list_lock); list_add_tail(&vq->vq.list, &vdev->vqs); spin_unlock(&vdev->vqs_list_lock); return &vq->vq; -err_desc_extra: - kfree(vq->packed.desc_state); -err_desc_state: +err_state_extra: kfree(vq); err_vq: - vring_free_queue(vdev, event_size_in_bytes, device, device_event_dma_addr); -err_device: - vring_free_queue(vdev, event_size_in_bytes, driver, driver_event_dma_addr); -err_driver: - vring_free_queue(vdev, ring_size_in_bytes, ring, ring_dma_addr); + vring_free_packed(&vring_packed, vdev); err_ring: return NULL; } +static int virtqueue_resize_packed(struct virtqueue *_vq, u32 num) +{ + struct vring_virtqueue_packed vring_packed = {}; + struct vring_virtqueue *vq = to_vvq(_vq); + struct virtio_device *vdev = _vq->vdev; + int err; + + if (vring_alloc_queue_packed(&vring_packed, vdev, num)) + goto err_ring; + + err = vring_alloc_state_extra_packed(&vring_packed); + if (err) + goto err_state_extra; + + vring_free(&vq->vq); + + virtqueue_vring_init_packed(&vring_packed, !!vq->vq.callback); + + virtqueue_init(vq, vring_packed.vring.num); + virtqueue_vring_attach_packed(vq, &vring_packed); + + return 0; + +err_state_extra: + vring_free_packed(&vring_packed, vdev); +err_ring: + virtqueue_reinit_packed(vq); + return -ENOMEM; +} + /* * Generic functions and exported symbols. @@ -2131,8 +2409,8 @@ EXPORT_SYMBOL_GPL(virtqueue_enable_cb_delayed); * @_vq: the struct virtqueue we're talking about. * * Returns NULL or the "data" token handed to virtqueue_add_*(). - * This is not valid on an active queue; it is useful only for device - * shutdown. + * This is not valid on an active queue; it is useful for device + * shutdown or the reset queue. */ void *virtqueue_detach_unused_buf(struct virtqueue *_vq) { @@ -2180,16 +2458,17 @@ irqreturn_t vring_interrupt(int irq, void *_vq) EXPORT_SYMBOL_GPL(vring_interrupt); /* Only available for split ring */ -struct virtqueue *__vring_new_virtqueue(unsigned int index, - struct vring vring, - struct virtio_device *vdev, - bool weak_barriers, - bool context, - bool (*notify)(struct virtqueue *), - void (*callback)(struct virtqueue *), - const char *name) +static struct virtqueue *__vring_new_virtqueue(unsigned int index, + struct vring_virtqueue_split *vring_split, + struct virtio_device *vdev, + bool weak_barriers, + bool context, + bool (*notify)(struct virtqueue *), + void (*callback)(struct virtqueue *), + const char *name) { struct vring_virtqueue *vq; + int err; if (virtio_has_feature(vdev, VIRTIO_F_RING_PACKED)) return NULL; @@ -2202,8 +2481,8 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index, vq->vq.callback = callback; vq->vq.vdev = vdev; vq->vq.name = name; - vq->vq.num_free = vring.num; vq->vq.index = index; + vq->vq.reset = false; vq->we_own_ring = false; vq->notify = notify; vq->weak_barriers = weak_barriers; @@ -2212,14 +2491,7 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index, #else vq->broken = false; #endif - vq->last_used_idx = 0; - vq->event_triggered = false; - vq->num_added = 0; vq->use_dma_api = vring_use_dma_api(vdev); -#ifdef DEBUG - vq->in_use = false; - vq->last_add_time_valid = false; -#endif vq->indirect = virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC) && !context; @@ -2228,47 +2500,22 @@ struct virtqueue *__vring_new_virtqueue(unsigned int index, if (virtio_has_feature(vdev, VIRTIO_F_ORDER_PLATFORM)) vq->weak_barriers = false; - vq->split.queue_dma_addr = 0; - vq->split.queue_size_in_bytes = 0; - - vq->split.vring = vring; - vq->split.avail_flags_shadow = 0; - vq->split.avail_idx_shadow = 0; - - /* No callback? Tell other side not to bother us. */ - if (!callback) { - vq->split.avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT; - if (!vq->event) - vq->split.vring.avail->flags = cpu_to_virtio16(vdev, - vq->split.avail_flags_shadow); + err = vring_alloc_state_extra_split(vring_split); + if (err) { + kfree(vq); + return NULL; } - vq->split.desc_state = kmalloc_array(vring.num, - sizeof(struct vring_desc_state_split), GFP_KERNEL); - if (!vq->split.desc_state) - goto err_state; - - vq->split.desc_extra = vring_alloc_desc_extra(vq, vring.num); - if (!vq->split.desc_extra) - goto err_extra; + virtqueue_vring_init_split(vring_split, vq); - /* Put everything in free lists. */ - vq->free_head = 0; - memset(vq->split.desc_state, 0, vring.num * - sizeof(struct vring_desc_state_split)); + virtqueue_init(vq, vring_split->vring.num); + virtqueue_vring_attach_split(vq, vring_split); spin_lock(&vdev->vqs_list_lock); list_add_tail(&vq->vq.list, &vdev->vqs); spin_unlock(&vdev->vqs_list_lock); return &vq->vq; - -err_extra: - kfree(vq->split.desc_state); -err_state: - kfree(vq); - return NULL; } -EXPORT_SYMBOL_GPL(__vring_new_virtqueue); struct virtqueue *vring_create_virtqueue( unsigned int index, @@ -2294,6 +2541,75 @@ struct virtqueue *vring_create_virtqueue( } EXPORT_SYMBOL_GPL(vring_create_virtqueue); +/** + * virtqueue_resize - resize the vring of vq + * @_vq: the struct virtqueue we're talking about. + * @num: new ring num + * @recycle: callback for recycle the useless buffer + * + * When it is really necessary to create a new vring, it will set the current vq + * into the reset state. Then call the passed callback to recycle the buffer + * that is no longer used. Only after the new vring is successfully created, the + * old vring will be released. + * + * Caller must ensure we don't call this with other virtqueue operations + * at the same time (except where noted). + * + * Returns zero or a negative error. + * 0: success. + * -ENOMEM: Failed to allocate a new ring, fall back to the original ring size. + * vq can still work normally + * -EBUSY: Failed to sync with device, vq may not work properly + * -ENOENT: Transport or device not supported + * -E2BIG/-EINVAL: num error + * -EPERM: Operation not permitted + * + */ +int virtqueue_resize(struct virtqueue *_vq, u32 num, + void (*recycle)(struct virtqueue *vq, void *buf)) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + struct virtio_device *vdev = vq->vq.vdev; + void *buf; + int err; + + if (!vq->we_own_ring) + return -EPERM; + + if (num > vq->vq.num_max) + return -E2BIG; + + if (!num) + return -EINVAL; + + if ((vq->packed_ring ? vq->packed.vring.num : vq->split.vring.num) == num) + return 0; + + if (!vdev->config->disable_vq_and_reset) + return -ENOENT; + + if (!vdev->config->enable_vq_after_reset) + return -ENOENT; + + err = vdev->config->disable_vq_and_reset(_vq); + if (err) + return err; + + while ((buf = virtqueue_detach_unused_buf(_vq)) != NULL) + recycle(_vq, buf); + + if (vq->packed_ring) + err = virtqueue_resize_packed(_vq, num); + else + err = virtqueue_resize_split(_vq, num); + + if (vdev->config->enable_vq_after_reset(_vq)) + return -EBUSY; + + return err; +} +EXPORT_SYMBOL_GPL(virtqueue_resize); + /* Only available for split ring */ struct virtqueue *vring_new_virtqueue(unsigned int index, unsigned int num, @@ -2306,25 +2622,21 @@ struct virtqueue *vring_new_virtqueue(unsigned int index, void (*callback)(struct virtqueue *vq), const char *name) { - struct vring vring; + struct vring_virtqueue_split vring_split = {}; if (virtio_has_feature(vdev, VIRTIO_F_RING_PACKED)) return NULL; - vring_init(&vring, num, pages, vring_align); - return __vring_new_virtqueue(index, vring, vdev, weak_barriers, context, - notify, callback, name); + vring_init(&vring_split.vring, num, pages, vring_align); + return __vring_new_virtqueue(index, &vring_split, vdev, weak_barriers, + context, notify, callback, name); } EXPORT_SYMBOL_GPL(vring_new_virtqueue); -void vring_del_virtqueue(struct virtqueue *_vq) +static void vring_free(struct virtqueue *_vq) { struct vring_virtqueue *vq = to_vvq(_vq); - spin_lock(&vq->vq.vdev->vqs_list_lock); - list_del(&_vq->list); - spin_unlock(&vq->vq.vdev->vqs_list_lock); - if (vq->we_own_ring) { if (vq->packed_ring) { vring_free_queue(vq->vq.vdev, @@ -2355,6 +2667,18 @@ void vring_del_virtqueue(struct virtqueue *_vq) kfree(vq->split.desc_state); kfree(vq->split.desc_extra); } +} + +void vring_del_virtqueue(struct virtqueue *_vq) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + + spin_lock(&vq->vq.vdev->vqs_list_lock); + list_del(&_vq->list); + spin_unlock(&vq->vq.vdev->vqs_list_lock); + + vring_free(_vq); + kfree(vq); } EXPORT_SYMBOL_GPL(vring_del_virtqueue); @@ -2402,6 +2726,30 @@ unsigned int virtqueue_get_vring_size(struct virtqueue *_vq) } EXPORT_SYMBOL_GPL(virtqueue_get_vring_size); +/* + * This function should only be called by the core, not directly by the driver. + */ +void __virtqueue_break(struct virtqueue *_vq) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + + /* Pairs with READ_ONCE() in virtqueue_is_broken(). */ + WRITE_ONCE(vq->broken, true); +} +EXPORT_SYMBOL_GPL(__virtqueue_break); + +/* + * This function should only be called by the core, not directly by the driver. + */ +void __virtqueue_unbreak(struct virtqueue *_vq) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + + /* Pairs with READ_ONCE() in virtqueue_is_broken(). */ + WRITE_ONCE(vq->broken, false); +} +EXPORT_SYMBOL_GPL(__virtqueue_unbreak); + bool virtqueue_is_broken(struct virtqueue *_vq) { struct vring_virtqueue *vq = to_vvq(_vq); diff --git a/drivers/virtio/virtio_vdpa.c b/drivers/virtio/virtio_vdpa.c index c40f7deb6b5a..9bc4d110b800 100644 --- a/drivers/virtio/virtio_vdpa.c +++ b/drivers/virtio/virtio_vdpa.c @@ -131,7 +131,7 @@ static irqreturn_t virtio_vdpa_virtqueue_cb(void *private) static struct virtqueue * virtio_vdpa_setup_vq(struct virtio_device *vdev, unsigned int index, void (*callback)(struct virtqueue *vq), - const char *name, bool ctx) + const char *name, u32 size, bool ctx) { struct virtio_vdpa_device *vd_dev = to_virtio_vdpa_device(vdev); struct vdpa_device *vdpa = vd_get_vdpa(vdev); @@ -168,14 +168,17 @@ virtio_vdpa_setup_vq(struct virtio_device *vdev, unsigned int index, goto error_new_virtqueue; } + if (!size || size > max_num) + size = max_num; + if (ops->get_vq_num_min) min_num = ops->get_vq_num_min(vdpa); - may_reduce_num = (max_num == min_num) ? false : true; + may_reduce_num = (size == min_num) ? false : true; /* Create the vring */ align = ops->get_vq_align(vdpa); - vq = vring_create_virtqueue(index, max_num, align, vdev, + vq = vring_create_virtqueue(index, size, align, vdev, true, may_reduce_num, ctx, virtio_vdpa_notify, callback, name); if (!vq) { @@ -183,6 +186,8 @@ virtio_vdpa_setup_vq(struct virtio_device *vdev, unsigned int index, goto error_new_virtqueue; } + vq->num_max = max_num; + /* Setup virtqueue callback */ cb.callback = callback ? virtio_vdpa_virtqueue_cb : NULL; cb.private = info; @@ -267,6 +272,7 @@ static int virtio_vdpa_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char * const names[], + u32 sizes[], const bool *ctx, struct irq_affinity *desc) { @@ -282,9 +288,9 @@ static int virtio_vdpa_find_vqs(struct virtio_device *vdev, unsigned int nvqs, continue; } - vqs[i] = virtio_vdpa_setup_vq(vdev, queue_idx++, - callbacks[i], names[i], ctx ? - ctx[i] : false); + vqs[i] = virtio_vdpa_setup_vq(vdev, queue_idx++, callbacks[i], + names[i], sizes ? sizes[i] : 0, + ctx ? ctx[i] : false); if (IS_ERR(vqs[i])) { err = PTR_ERR(vqs[i]); goto err_setup_vq; |