From 2c861d89ccda2fbcea9358eff9cc5f8fae548be5 Mon Sep 17 00:00:00 2001 From: Dong Jia Shi Date: Wed, 2 May 2018 09:25:59 +0200 Subject: vfio: ccw: fix error return in vfio_ccw_sch_event If the device has not been registered, or there is work pending, we should reschedule a sch_event call again. Signed-off-by: Dong Jia Shi Message-Id: <20180502072559.50691-1-bjsdjshi@linux.vnet.ibm.com> Reviewed-by: Cornelia Huck Signed-off-by: Cornelia Huck --- drivers/s390/cio/vfio_ccw_drv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/cio/vfio_ccw_drv.c b/drivers/s390/cio/vfio_ccw_drv.c index ea6a2d0b2894..770fa9cfc310 100644 --- a/drivers/s390/cio/vfio_ccw_drv.c +++ b/drivers/s390/cio/vfio_ccw_drv.c @@ -177,6 +177,7 @@ static int vfio_ccw_sch_event(struct subchannel *sch, int process) { struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev); unsigned long flags; + int rc = -EAGAIN; spin_lock_irqsave(sch->lock, flags); if (!device_is_registered(&sch->dev)) @@ -187,6 +188,7 @@ static int vfio_ccw_sch_event(struct subchannel *sch, int process) if (cio_update_schib(sch)) { vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER); + rc = 0; goto out_unlock; } @@ -195,11 +197,12 @@ static int vfio_ccw_sch_event(struct subchannel *sch, int process) private->state = private->mdev ? VFIO_CCW_STATE_IDLE : VFIO_CCW_STATE_STANDBY; } + rc = 0; out_unlock: spin_unlock_irqrestore(sch->lock, flags); - return 0; + return rc; } static struct css_device_id vfio_ccw_sch_ids[] = { -- cgit v1.2.3 From fb9e7880af357f0244f57a3dc4dd365091970b1a Mon Sep 17 00:00:00 2001 From: Halil Pasic Date: Wed, 16 May 2018 19:33:42 +0200 Subject: vfio: ccw: push down unsupported IDA check There is at least one relevant guest OS that doesn't set the IDA flags in the ORB as we would like them, but never uses any IDA. So instead of saying -EOPNOTSUPP when observing an ORB, such that a channel program specified by it could be a not supported one, let us say -EOPNOTSUPP only if the channel program is a not supported one. Of course, the real solution would be doing proper translation for all IDA. This is possible, but given the current code not straight forward. Signed-off-by: Halil Pasic Tested-by: Jason J. Herne Message-Id: <20180516173342.15174-1-pasic@linux.ibm.com> Reviewed-by: Dong Jia Shi Signed-off-by: Cornelia Huck --- drivers/s390/cio/vfio_ccw_cp.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index dce92b2a895d..9a2a39df1056 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -365,6 +365,9 @@ static void cp_unpin_free(struct channel_program *cp) * This is the chain length not considering any TICs. * You need to do a new round for each TIC target. * + * The program is also validated for absence of not yet supported + * indirect data addressing scenarios. + * * Returns: the length of the ccw chain or -errno. */ static int ccwchain_calc_length(u64 iova, struct channel_program *cp) @@ -391,6 +394,14 @@ static int ccwchain_calc_length(u64 iova, struct channel_program *cp) do { cnt++; + /* + * As we don't want to fail direct addressing even if the + * orb specified one of the unsupported formats, we defer + * checking for IDAWs in unsupported formats to here. + */ + if ((!cp->orb.cmd.c64 || cp->orb.cmd.i2k) && ccw_is_idal(ccw)) + return -EOPNOTSUPP; + if ((!ccw_is_chain(ccw)) && (!ccw_is_tic(ccw))) break; @@ -656,10 +667,8 @@ int cp_init(struct channel_program *cp, struct device *mdev, union orb *orb) /* * XXX: * Only support prefetch enable mode now. - * Only support 64bit addressing idal. - * Only support 4k IDAW. */ - if (!orb->cmd.pfch || !orb->cmd.c64 || orb->cmd.i2k) + if (!orb->cmd.pfch) return -EOPNOTSUPP; INIT_LIST_HEAD(&cp->ccwchain_list); @@ -688,6 +697,10 @@ int cp_init(struct channel_program *cp, struct device *mdev, union orb *orb) ret = ccwchain_loop_tic(chain, cp); if (ret) cp_unpin_free(cp); + /* It is safe to force: if not set but idals used + * ccwchain_calc_length returns an error. + */ + cp->orb.cmd.c64 = 1; return ret; } -- cgit v1.2.3 From 80c57f7a075b0c53944113e42ce114d8bf0977e4 Mon Sep 17 00:00:00 2001 From: Dong Jia Shi Date: Wed, 23 May 2018 04:56:42 +0200 Subject: vfio: ccw: shorten kernel doc description for pfn_array_pin() The kernel doc description for usage of the struct pfn_array in pfn_array_pin() is unnecessary long. Let's shorten it by describing the contents of the struct pfn_array fields at the struct's definition instead. Suggested-by: Cornelia Huck Signed-off-by: Dong Jia Shi Message-Id: <20180523025645.8978-2-bjsdjshi@linux.ibm.com> Signed-off-by: Cornelia Huck --- drivers/s390/cio/vfio_ccw_cp.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 9a2a39df1056..c532939c1c3f 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -23,9 +23,13 @@ #define CCWCHAIN_LEN_MAX 256 struct pfn_array { + /* Starting guest physical I/O address. */ unsigned long pa_iova; + /* Array that stores PFNs of the pages need to pin. */ unsigned long *pa_iova_pfn; + /* Array that receives PFNs of the pages pinned. */ unsigned long *pa_pfn; + /* Number of pages to pin/pinned from @pa_iova. */ int pa_nr; }; @@ -53,14 +57,8 @@ struct ccwchain { * Attempt to pin user pages in memory. * * Usage of pfn_array: - * @pa->pa_iova starting guest physical I/O address. Assigned by caller. - * @pa->pa_iova_pfn array that stores PFNs of the pages need to pin. Allocated - * by caller. - * @pa->pa_pfn array that receives PFNs of the pages pinned. Allocated by - * caller. - * @pa->pa_nr number of pages from @pa->pa_iova to pin. Assigned by - * caller. - * number of pages pinned. Assigned by callee. + * Any field in this structure should be initialized by caller. + * We expect @pa->pa_nr > 0, and its value will be assigned by callee. * * Returns: * Number of pages pinned on success. -- cgit v1.2.3 From 5c1cfb1c3948fe93a32dfcd75223dda0f1558bb7 Mon Sep 17 00:00:00 2001 From: Dong Jia Shi Date: Wed, 23 May 2018 04:56:43 +0200 Subject: vfio: ccw: refactor and improve pfn_array_alloc_pin() This refactors pfn_array_alloc_pin() and also improves it by adding defensive code in error handling so that calling pfn_array_unpin_free() after error return won't lead to problem. This mainly does: 1. Merge pfn_array_pin() into pfn_array_alloc_pin(), since there is no other user of pfn_array_pin(). As a result, also remove kernel-doc for pfn_array_pin() and add/update kernel-doc for pfn_array_alloc_pin() and struct pfn_array. 2. For a vfio_pin_pages() failure, set pa->pa_nr to zero to indicate zero pages were pinned. 3. Set pa->pa_iova_pfn to NULL right after it was freed. Suggested-by: Pierre Morel Signed-off-by: Dong Jia Shi Message-Id: <20180523025645.8978-3-bjsdjshi@linux.ibm.com> Signed-off-by: Cornelia Huck --- drivers/s390/cio/vfio_ccw_cp.c | 82 +++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index c532939c1c3f..b0f20230fc72 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -29,7 +29,7 @@ struct pfn_array { unsigned long *pa_iova_pfn; /* Array that receives PFNs of the pages pinned. */ unsigned long *pa_pfn; - /* Number of pages to pin/pinned from @pa_iova. */ + /* Number of pages pinned from @pa_iova. */ int pa_nr; }; @@ -50,64 +50,33 @@ struct ccwchain { }; /* - * pfn_array_pin() - pin user pages in memory + * pfn_array_alloc_pin() - alloc memory for PFNs, then pin user pages in memory * @pa: pfn_array on which to perform the operation * @mdev: the mediated device to perform pin/unpin operations + * @iova: target guest physical address + * @len: number of bytes that should be pinned from @iova * - * Attempt to pin user pages in memory. + * Attempt to allocate memory for PFNs, and pin user pages in memory. * * Usage of pfn_array: - * Any field in this structure should be initialized by caller. - * We expect @pa->pa_nr > 0, and its value will be assigned by callee. + * We expect (pa_nr == 0) and (pa_iova_pfn == NULL), any field in + * this structure will be filled in by this function. * * Returns: * Number of pages pinned on success. - * If @pa->pa_nr is 0 or negative, returns 0. + * If @pa->pa_nr is not 0, or @pa->pa_iova_pfn is not NULL initially, + * returns -EINVAL. * If no pages were pinned, returns -errno. */ -static int pfn_array_pin(struct pfn_array *pa, struct device *mdev) -{ - int i, ret; - - if (pa->pa_nr <= 0) { - pa->pa_nr = 0; - return 0; - } - - pa->pa_iova_pfn[0] = pa->pa_iova >> PAGE_SHIFT; - for (i = 1; i < pa->pa_nr; i++) - pa->pa_iova_pfn[i] = pa->pa_iova_pfn[i - 1] + 1; - - ret = vfio_pin_pages(mdev, pa->pa_iova_pfn, pa->pa_nr, - IOMMU_READ | IOMMU_WRITE, pa->pa_pfn); - - if (ret > 0 && ret != pa->pa_nr) { - vfio_unpin_pages(mdev, pa->pa_iova_pfn, ret); - pa->pa_nr = 0; - return 0; - } - - return ret; -} - -/* Unpin the pages before releasing the memory. */ -static void pfn_array_unpin_free(struct pfn_array *pa, struct device *mdev) -{ - vfio_unpin_pages(mdev, pa->pa_iova_pfn, pa->pa_nr); - pa->pa_nr = 0; - kfree(pa->pa_iova_pfn); -} - -/* Alloc memory for PFNs, then pin pages with them. */ static int pfn_array_alloc_pin(struct pfn_array *pa, struct device *mdev, u64 iova, unsigned int len) { - int ret = 0; + int i, ret = 0; if (!len) return 0; - if (pa->pa_nr) + if (pa->pa_nr || pa->pa_iova_pfn) return -EINVAL; pa->pa_iova = iova; @@ -124,18 +93,39 @@ static int pfn_array_alloc_pin(struct pfn_array *pa, struct device *mdev, return -ENOMEM; pa->pa_pfn = pa->pa_iova_pfn + pa->pa_nr; - ret = pfn_array_pin(pa, mdev); + pa->pa_iova_pfn[0] = pa->pa_iova >> PAGE_SHIFT; + for (i = 1; i < pa->pa_nr; i++) + pa->pa_iova_pfn[i] = pa->pa_iova_pfn[i - 1] + 1; + + ret = vfio_pin_pages(mdev, pa->pa_iova_pfn, pa->pa_nr, + IOMMU_READ | IOMMU_WRITE, pa->pa_pfn); - if (ret > 0) - return ret; - else if (!ret) + if (ret < 0) { + goto err_out; + } else if (ret > 0 && ret != pa->pa_nr) { + vfio_unpin_pages(mdev, pa->pa_iova_pfn, ret); ret = -EINVAL; + goto err_out; + } + + return ret; +err_out: + pa->pa_nr = 0; kfree(pa->pa_iova_pfn); + pa->pa_iova_pfn = NULL; return ret; } +/* Unpin the pages before releasing the memory. */ +static void pfn_array_unpin_free(struct pfn_array *pa, struct device *mdev) +{ + vfio_unpin_pages(mdev, pa->pa_iova_pfn, pa->pa_nr); + pa->pa_nr = 0; + kfree(pa->pa_iova_pfn); +} + static int pfn_array_table_init(struct pfn_array_table *pat, int nr) { pat->pat_pa = kcalloc(nr, sizeof(*pat->pat_pa), GFP_KERNEL); -- cgit v1.2.3 From 6238f92132a6da64b731de1a728fa46ffaa21f62 Mon Sep 17 00:00:00 2001 From: Dong Jia Shi Date: Wed, 23 May 2018 04:56:44 +0200 Subject: vfio: ccw: set ccw->cda to NULL defensively Let's avoid free on ccw->cda that points to a guest address or an already freed memory area by setting it to NULL if memory allocation didn't happen or failed. Signed-off-by: Dong Jia Shi Message-Id: <20180523025645.8978-4-bjsdjshi@linux.ibm.com> Signed-off-by: Cornelia Huck --- drivers/s390/cio/vfio_ccw_cp.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index b0f20230fc72..dbe7c7ac9ac8 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -502,7 +502,7 @@ static int ccwchain_fetch_direct(struct ccwchain *chain, struct ccw1 *ccw; struct pfn_array_table *pat; unsigned long *idaws; - int idaw_nr; + int ret; ccw = chain->ch_ccw + idx; @@ -522,18 +522,19 @@ static int ccwchain_fetch_direct(struct ccwchain *chain, * needed when translating a direct ccw to a idal ccw. */ pat = chain->ch_pat + idx; - if (pfn_array_table_init(pat, 1)) - return -ENOMEM; - idaw_nr = pfn_array_alloc_pin(pat->pat_pa, cp->mdev, - ccw->cda, ccw->count); - if (idaw_nr < 0) - return idaw_nr; + ret = pfn_array_table_init(pat, 1); + if (ret) + goto out_init; + + ret = pfn_array_alloc_pin(pat->pat_pa, cp->mdev, ccw->cda, ccw->count); + if (ret < 0) + goto out_init; /* Translate this direct ccw to a idal ccw. */ - idaws = kcalloc(idaw_nr, sizeof(*idaws), GFP_DMA | GFP_KERNEL); + idaws = kcalloc(ret, sizeof(*idaws), GFP_DMA | GFP_KERNEL); if (!idaws) { - pfn_array_table_unpin_free(pat, cp->mdev); - return -ENOMEM; + ret = -ENOMEM; + goto out_unpin; } ccw->cda = (__u32) virt_to_phys(idaws); ccw->flags |= CCW_FLAG_IDA; @@ -541,6 +542,12 @@ static int ccwchain_fetch_direct(struct ccwchain *chain, pfn_array_table_idal_create_words(pat, idaws); return 0; + +out_unpin: + pfn_array_table_unpin_free(pat, cp->mdev); +out_init: + ccw->cda = 0; + return ret; } static int ccwchain_fetch_idal(struct ccwchain *chain, @@ -570,7 +577,7 @@ static int ccwchain_fetch_idal(struct ccwchain *chain, pat = chain->ch_pat + idx; ret = pfn_array_table_init(pat, idaw_nr); if (ret) - return ret; + goto out_init; /* Translate idal ccw to use new allocated idaws. */ idaws = kzalloc(idaw_len, GFP_DMA | GFP_KERNEL); @@ -602,6 +609,8 @@ out_free_idaws: kfree(idaws); out_unpin: pfn_array_table_unpin_free(pat, cp->mdev); +out_init: + ccw->cda = 0; return ret; } -- cgit v1.2.3 From 3cd90214b70f7f971496bffc3c34d23b2141feb3 Mon Sep 17 00:00:00 2001 From: Halil Pasic Date: Wed, 23 May 2018 04:56:45 +0200 Subject: vfio: ccw: add tracepoints for interesting error paths Add some tracepoints so we can inspect what is not working as is should. Signed-off-by: Halil Pasic Signed-off-by: Dong Jia Shi Message-Id: <20180523025645.8978-5-bjsdjshi@linux.ibm.com> Signed-off-by: Cornelia Huck --- drivers/s390/cio/Makefile | 1 + drivers/s390/cio/vfio_ccw_fsm.c | 17 +++++++++++- drivers/s390/cio/vfio_ccw_trace.h | 54 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 drivers/s390/cio/vfio_ccw_trace.h (limited to 'drivers') diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile index a070ef0efe65..f230516abb96 100644 --- a/drivers/s390/cio/Makefile +++ b/drivers/s390/cio/Makefile @@ -5,6 +5,7 @@ # The following is required for define_trace.h to find ./trace.h CFLAGS_trace.o := -I$(src) +CFLAGS_vfio_ccw_fsm.o := -I$(src) obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o \ fcx.o itcw.o crw.o ccwreq.o trace.o ioasm.o diff --git a/drivers/s390/cio/vfio_ccw_fsm.c b/drivers/s390/cio/vfio_ccw_fsm.c index 3c800642134e..797a82731159 100644 --- a/drivers/s390/cio/vfio_ccw_fsm.c +++ b/drivers/s390/cio/vfio_ccw_fsm.c @@ -13,6 +13,9 @@ #include "ioasm.h" #include "vfio_ccw_private.h" +#define CREATE_TRACE_POINTS +#include "vfio_ccw_trace.h" + static int fsm_io_helper(struct vfio_ccw_private *private) { struct subchannel *sch; @@ -110,6 +113,10 @@ static void fsm_disabled_irq(struct vfio_ccw_private *private, */ cio_disable_subchannel(sch); } +inline struct subchannel_id get_schid(struct vfio_ccw_private *p) +{ + return p->sch->schid; +} /* * Deal with the ccw command request from the userspace. @@ -121,6 +128,7 @@ static void fsm_io_request(struct vfio_ccw_private *private, union scsw *scsw = &private->scsw; struct ccw_io_region *io_region = &private->io_region; struct mdev_device *mdev = private->mdev; + char *errstr = "request"; private->state = VFIO_CCW_STATE_BOXED; @@ -132,15 +140,19 @@ static void fsm_io_request(struct vfio_ccw_private *private, /* Don't try to build a cp if transport mode is specified. */ if (orb->tm.b) { io_region->ret_code = -EOPNOTSUPP; + errstr = "transport mode"; goto err_out; } io_region->ret_code = cp_init(&private->cp, mdev_dev(mdev), orb); - if (io_region->ret_code) + if (io_region->ret_code) { + errstr = "cp init"; goto err_out; + } io_region->ret_code = cp_prefetch(&private->cp); if (io_region->ret_code) { + errstr = "cp prefetch"; cp_free(&private->cp); goto err_out; } @@ -148,6 +160,7 @@ static void fsm_io_request(struct vfio_ccw_private *private, /* Start channel program and wait for I/O interrupt. */ io_region->ret_code = fsm_io_helper(private); if (io_region->ret_code) { + errstr = "cp fsm_io_helper"; cp_free(&private->cp); goto err_out; } @@ -164,6 +177,8 @@ static void fsm_io_request(struct vfio_ccw_private *private, err_out: private->state = VFIO_CCW_STATE_IDLE; + trace_vfio_ccw_io_fctl(scsw->cmd.fctl, get_schid(private), + io_region->ret_code, errstr); } /* diff --git a/drivers/s390/cio/vfio_ccw_trace.h b/drivers/s390/cio/vfio_ccw_trace.h new file mode 100644 index 000000000000..b1da53ddec1f --- /dev/null +++ b/drivers/s390/cio/vfio_ccw_trace.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Tracepoints for vfio_ccw driver + * + * Copyright IBM Corp. 2018 + * + * Author(s): Dong Jia Shi + * Halil Pasic + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM vfio_ccw + +#if !defined(_VFIO_CCW_TRACE_) || defined(TRACE_HEADER_MULTI_READ) +#define _VFIO_CCW_TRACE_ + +#include + +TRACE_EVENT(vfio_ccw_io_fctl, + TP_PROTO(int fctl, struct subchannel_id schid, int errno, char *errstr), + TP_ARGS(fctl, schid, errno, errstr), + + TP_STRUCT__entry( + __field(int, fctl) + __field_struct(struct subchannel_id, schid) + __field(int, errno) + __field(char*, errstr) + ), + + TP_fast_assign( + __entry->fctl = fctl; + __entry->schid = schid; + __entry->errno = errno; + __entry->errstr = errstr; + ), + + TP_printk("schid=%x.%x.%04x fctl=%x errno=%d info=%s", + __entry->schid.cssid, + __entry->schid.ssid, + __entry->schid.sch_no, + __entry->fctl, + __entry->errno, + __entry->errstr) +); + +#endif /* _VFIO_CCW_TRACE_ */ + +/* This part must be outside protection */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE vfio_ccw_trace + +#include -- cgit v1.2.3 From 069035c5db3459b9b5f12caf3bffed9a863fa5c4 Mon Sep 17 00:00:00 2001 From: Oleksandr Andrushchenko Date: Tue, 22 May 2018 17:13:04 +0300 Subject: drm: Fix possible race conditions while unplugging DRM device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When unplugging a hotpluggable DRM device we first unregister it with drm_dev_unregister and then set drm_device.unplugged flag which is used to mark device critical sections with drm_dev_enter()/ drm_dev_exit() preventing access to device resources that are not available after the device is gone. But drm_dev_unregister may lead to hotplug uevent(s) fired to user-space on card and/or connector removal, thus making it possible for user-space to try accessing a disconnected device. Fix this by first making sure device is properly marked as disconnected and only then unregister it. Fixes: bee330f3d672 ("drm: Use srcu to protect drm_device.unplugged") Signed-off-by: Oleksandr Andrushchenko Reported-by: Andrii Chepurnyi Cc: "Noralf Trønnes" Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20180522141304.18646-1-andr2000@gmail.com --- drivers/gpu/drm/drm_drv.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index f6910ebe4d0e..cc2675550e28 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -369,13 +369,6 @@ EXPORT_SYMBOL(drm_dev_exit); */ void drm_dev_unplug(struct drm_device *dev) { - drm_dev_unregister(dev); - - mutex_lock(&drm_global_mutex); - if (dev->open_count == 0) - drm_dev_put(dev); - mutex_unlock(&drm_global_mutex); - /* * After synchronizing any critical read section is guaranteed to see * the new value of ->unplugged, and any critical section which might @@ -384,6 +377,13 @@ void drm_dev_unplug(struct drm_device *dev) */ dev->unplugged = true; synchronize_srcu(&drm_unplug_srcu); + + drm_dev_unregister(dev); + + mutex_lock(&drm_global_mutex); + if (dev->open_count == 0) + drm_dev_put(dev); + mutex_unlock(&drm_global_mutex); } EXPORT_SYMBOL(drm_dev_unplug); -- cgit v1.2.3 From 889ad63d41eea20184b0483e7e585e5b20fb6cfe Mon Sep 17 00:00:00 2001 From: Jeremy Cline Date: Fri, 1 Jun 2018 16:05:32 -0400 Subject: drm/qxl: Call qxl_bo_unref outside atomic context "qxl_bo_unref" may sleep, but calling "qxl_release_map" causes "preempt_disable()" to be called and "preempt_enable()" isn't called until "qxl_release_unmap" is used. Move the call to "qxl_bo_unref" out from in between the two to avoid sleeping from an atomic context. This issue can be demonstrated on a kernel with CONFIG_LOCKDEP=y by creating a VM using QXL, using a desktop environment using Xorg, then moving the cursor on or off a window. Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1571128 Fixes: 9428088c90b6 ("drm/qxl: reapply cursor after resetting primary") Cc: stable@vger.kernel.org Signed-off-by: Jeremy Cline Link: http://patchwork.freedesktop.org/patch/msgid/20180601200532.13619-1-jcline@redhat.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/qxl/qxl_display.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index ecb35ed0eac8..61e51516fec5 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -630,7 +630,7 @@ static void qxl_cursor_atomic_update(struct drm_plane *plane, struct qxl_cursor_cmd *cmd; struct qxl_cursor *cursor; struct drm_gem_object *obj; - struct qxl_bo *cursor_bo = NULL, *user_bo = NULL; + struct qxl_bo *cursor_bo = NULL, *user_bo = NULL, *old_cursor_bo = NULL; int ret; void *user_ptr; int size = 64*64*4; @@ -684,7 +684,7 @@ static void qxl_cursor_atomic_update(struct drm_plane *plane, cursor_bo, 0); cmd->type = QXL_CURSOR_SET; - qxl_bo_unref(&qcrtc->cursor_bo); + old_cursor_bo = qcrtc->cursor_bo; qcrtc->cursor_bo = cursor_bo; cursor_bo = NULL; } else { @@ -704,6 +704,9 @@ static void qxl_cursor_atomic_update(struct drm_plane *plane, qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false); qxl_release_fence_buffer_objects(release); + if (old_cursor_bo) + qxl_bo_unref(&old_cursor_bo); + qxl_bo_unref(&cursor_bo); return; -- cgit v1.2.3 From 45615a9baf4661f953887988eeb4de69fd6cd96e Mon Sep 17 00:00:00 2001 From: Xiaotong Lu Date: Fri, 1 Jun 2018 11:36:22 -0700 Subject: Input: add Spreadtrum vibrator driver This patch adds the Spreadtrum vibrator driver, which embedded in the Spreadtrum SC27xx series PMICs. Signed-off-by: Xiaotong Lu Signed-off-by: Baolin Wang Signed-off-by: Dmitry Torokhov --- .../bindings/input/sprd,sc27xx-vibra.txt | 23 +++ drivers/input/misc/Kconfig | 10 ++ drivers/input/misc/Makefile | 1 + drivers/input/misc/sc27xx-vibra.c | 154 +++++++++++++++++++++ 4 files changed, 188 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/sprd,sc27xx-vibra.txt create mode 100644 drivers/input/misc/sc27xx-vibra.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/input/sprd,sc27xx-vibra.txt b/Documentation/devicetree/bindings/input/sprd,sc27xx-vibra.txt new file mode 100644 index 000000000000..f2ec0d4f2dff --- /dev/null +++ b/Documentation/devicetree/bindings/input/sprd,sc27xx-vibra.txt @@ -0,0 +1,23 @@ +Spreadtrum SC27xx PMIC Vibrator + +Required properties: +- compatible: should be "sprd,sc2731-vibrator". +- reg: address of vibrator control register. + +Example : + + sc2731_pmic: pmic@0 { + compatible = "sprd,sc2731"; + reg = <0>; + spi-max-frequency = <26000000>; + interrupts = ; + interrupt-controller; + #interrupt-cells = <2>; + #address-cells = <1>; + #size-cells = <0>; + + vibrator@eb4 { + compatible = "sprd,sc2731-vibrator"; + reg = <0xeb4>; + }; + }; diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 572b15fa18c2..c761c0c4f2b8 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -841,4 +841,14 @@ config INPUT_RAVE_SP_PWRBUTTON To compile this driver as a module, choose M here: the module will be called rave-sp-pwrbutton. +config INPUT_SC27XX_VIBRA + tristate "Spreadtrum sc27xx vibrator support" + depends on MFD_SC27XX_PMIC || COMPILE_TEST + select INPUT_FF_MEMLESS + help + This option enables support for Spreadtrum sc27xx vibrator driver. + + To compile this driver as a module, choose M here. The module will + be called sc27xx_vibra. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 72cde28649e2..9d0f9d1ff68f 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o obj-$(CONFIG_INPUT_AXP20X_PEK) += axp20x-pek.o obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o obj-$(CONFIG_INPUT_RK805_PWRKEY) += rk805-pwrkey.o +obj-$(CONFIG_INPUT_SC27XX_VIBRA) += sc27xx-vibra.o obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o diff --git a/drivers/input/misc/sc27xx-vibra.c b/drivers/input/misc/sc27xx-vibra.c new file mode 100644 index 000000000000..295251abbdac --- /dev/null +++ b/drivers/input/misc/sc27xx-vibra.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Spreadtrum Communications Inc. + */ + +#include +#include +#include +#include +#include +#include + +#define CUR_DRV_CAL_SEL GENMASK(13, 12) +#define SLP_LDOVIBR_PD_EN BIT(9) +#define LDO_VIBR_PD BIT(8) + +struct vibra_info { + struct input_dev *input_dev; + struct work_struct play_work; + struct regmap *regmap; + u32 base; + u32 strength; + bool enabled; +}; + +static void sc27xx_vibra_set(struct vibra_info *info, bool on) +{ + if (on) { + regmap_update_bits(info->regmap, info->base, LDO_VIBR_PD, 0); + regmap_update_bits(info->regmap, info->base, + SLP_LDOVIBR_PD_EN, 0); + info->enabled = true; + } else { + regmap_update_bits(info->regmap, info->base, LDO_VIBR_PD, + LDO_VIBR_PD); + regmap_update_bits(info->regmap, info->base, + SLP_LDOVIBR_PD_EN, SLP_LDOVIBR_PD_EN); + info->enabled = false; + } +} + +static int sc27xx_vibra_hw_init(struct vibra_info *info) +{ + return regmap_update_bits(info->regmap, info->base, CUR_DRV_CAL_SEL, 0); +} + +static void sc27xx_vibra_play_work(struct work_struct *work) +{ + struct vibra_info *info = container_of(work, struct vibra_info, + play_work); + + if (info->strength && !info->enabled) + sc27xx_vibra_set(info, true); + else if (info->strength == 0 && info->enabled) + sc27xx_vibra_set(info, false); +} + +static int sc27xx_vibra_play(struct input_dev *input, void *data, + struct ff_effect *effect) +{ + struct vibra_info *info = input_get_drvdata(input); + + info->strength = effect->u.rumble.weak_magnitude; + schedule_work(&info->play_work); + + return 0; +} + +static void sc27xx_vibra_close(struct input_dev *input) +{ + struct vibra_info *info = input_get_drvdata(input); + + cancel_work_sync(&info->play_work); + if (info->enabled) + sc27xx_vibra_set(info, false); +} + +static int sc27xx_vibra_probe(struct platform_device *pdev) +{ + struct vibra_info *info; + int error; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!info->regmap) { + dev_err(&pdev->dev, "failed to get vibrator regmap.\n"); + return -ENODEV; + } + + error = device_property_read_u32(&pdev->dev, "reg", &info->base); + if (error) { + dev_err(&pdev->dev, "failed to get vibrator base address.\n"); + return error; + } + + info->input_dev = devm_input_allocate_device(&pdev->dev); + if (!info->input_dev) { + dev_err(&pdev->dev, "failed to allocate input device.\n"); + return -ENOMEM; + } + + info->input_dev->name = "sc27xx:vibrator"; + info->input_dev->id.version = 0; + info->input_dev->close = sc27xx_vibra_close; + + input_set_drvdata(info->input_dev, info); + input_set_capability(info->input_dev, EV_FF, FF_RUMBLE); + INIT_WORK(&info->play_work, sc27xx_vibra_play_work); + info->enabled = false; + + error = sc27xx_vibra_hw_init(info); + if (error) { + dev_err(&pdev->dev, "failed to initialize the vibrator.\n"); + return error; + } + + error = input_ff_create_memless(info->input_dev, NULL, + sc27xx_vibra_play); + if (error) { + dev_err(&pdev->dev, "failed to register vibrator to FF.\n"); + return error; + } + + error = input_register_device(info->input_dev); + if (error) { + dev_err(&pdev->dev, "failed to register input device.\n"); + return error; + } + + return 0; +} + +static const struct of_device_id sc27xx_vibra_of_match[] = { + { .compatible = "sprd,sc2731-vibrator", }, + {} +}; +MODULE_DEVICE_TABLE(of, sc27xx_vibra_of_match); + +static struct platform_driver sc27xx_vibra_driver = { + .driver = { + .name = "sc27xx-vibrator", + .of_match_table = sc27xx_vibra_of_match, + }, + .probe = sc27xx_vibra_probe, +}; + +module_platform_driver(sc27xx_vibra_driver); + +MODULE_DESCRIPTION("Spreadtrum SC27xx Vibrator Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Xiaotong Lu "); -- cgit v1.2.3 From 8f6a652a8c1379023775709e3db6c6f7c1bdb2ab Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Thu, 31 May 2018 17:03:33 -0700 Subject: Input: goldfish_events - fix checkpatch warnings Address issues pointed by checkpatch.pl Signed-off-by: Roman Kiryanov Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/goldfish_events.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c index f6e643b589b6..e8dae6195b30 100644 --- a/drivers/input/keyboard/goldfish_events.c +++ b/drivers/input/keyboard/goldfish_events.c @@ -45,7 +45,7 @@ struct event_dev { static irqreturn_t events_interrupt(int irq, void *dev_id) { struct event_dev *edev = dev_id; - unsigned type, code, value; + unsigned int type, code, value; type = __raw_readl(edev->addr + REG_READ); code = __raw_readl(edev->addr + REG_READ); @@ -57,7 +57,7 @@ static irqreturn_t events_interrupt(int irq, void *dev_id) } static void events_import_bits(struct event_dev *edev, - unsigned long bits[], unsigned type, size_t count) + unsigned long bits[], unsigned int type, size_t count) { void __iomem *addr = edev->addr; int i, j; @@ -99,6 +99,7 @@ static void events_import_abs_params(struct event_dev *edev) for (j = 0; j < ARRAY_SIZE(val); j++) { int offset = (i * ARRAY_SIZE(val) + j) * sizeof(u32); + val[j] = __raw_readl(edev->addr + REG_DATA + offset); } @@ -112,7 +113,7 @@ static int events_probe(struct platform_device *pdev) struct input_dev *input_dev; struct event_dev *edev; struct resource *res; - unsigned keymapnamelen; + unsigned int keymapnamelen; void __iomem *addr; int irq; int i; @@ -150,7 +151,7 @@ static int events_probe(struct platform_device *pdev) for (i = 0; i < keymapnamelen; i++) edev->name[i] = __raw_readb(edev->addr + REG_DATA + i); - pr_debug("events_probe() keymap=%s\n", edev->name); + pr_debug("%s: keymap=%s\n", __func__, edev->name); input_dev->name = edev->name; input_dev->id.bustype = BUS_HOST; -- cgit v1.2.3 From fc573af632b44f355f8fa15ab505f5593368078d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 5 Jun 2018 09:34:22 -0700 Subject: Input: silead - add MSSL0002 ACPI HID The Silead touchscreen on the Chuwi Vi8 tablet uses MSSL0002 as ACPI HID, rather then the usual MSSL1680 id. Signed-off-by: Hans de Goede Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/silead.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c index ff7043f74a3d..d196ac3d8b8c 100644 --- a/drivers/input/touchscreen/silead.c +++ b/drivers/input/touchscreen/silead.c @@ -603,6 +603,7 @@ static const struct acpi_device_id silead_ts_acpi_match[] = { { "GSL3692", 0 }, { "MSSL1680", 0 }, { "MSSL0001", 0 }, + { "MSSL0002", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, silead_ts_acpi_match); -- cgit v1.2.3 From 24d28e4f1271cb2f91613dada8f2acccd00eff56 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 5 Jun 2018 10:17:51 -0700 Subject: Input: synaptics-rmi4 - convert irq distribution to irq_domain Convert the RMI driver to use the standard mechanism for distributing IRQs to the various functions. Tested on: * S7300 (F11, F34, F54) * S7817 (F12, F34, F54) Signed-off-by: Nick Dyer Acked-by: Christopher Heiny Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/Kconfig | 1 + drivers/input/rmi4/rmi_bus.c | 50 ++++++++++++++++++++++++++++++++++++++- drivers/input/rmi4/rmi_bus.h | 10 +++++++- drivers/input/rmi4/rmi_driver.c | 52 ++++++++++++++++------------------------- drivers/input/rmi4/rmi_f01.c | 10 ++++---- drivers/input/rmi4/rmi_f03.c | 9 +++---- drivers/input/rmi4/rmi_f11.c | 42 +++++++++++++-------------------- drivers/input/rmi4/rmi_f12.c | 8 +++---- drivers/input/rmi4/rmi_f30.c | 9 +++---- drivers/input/rmi4/rmi_f34.c | 5 ++-- drivers/input/rmi4/rmi_f54.c | 6 ----- include/linux/rmi.h | 2 ++ 12 files changed, 119 insertions(+), 85 deletions(-) (limited to 'drivers') diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index 7172b88cd064..fad2eae4a118 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -3,6 +3,7 @@ # config RMI4_CORE tristate "Synaptics RMI4 bus support" + select IRQ_DOMAIN help Say Y here if you want to support the Synaptics RMI4 bus. This is required for all RMI4 device support. diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index c5fa53adba8d..bd0d5ff01b08 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include #include @@ -167,6 +169,39 @@ static inline void rmi_function_of_probe(struct rmi_function *fn) {} #endif +static struct irq_chip rmi_irq_chip = { + .name = "rmi4", +}; + +static int rmi_create_function_irq(struct rmi_function *fn, + struct rmi_function_handler *handler) +{ + struct rmi_driver_data *drvdata = dev_get_drvdata(&fn->rmi_dev->dev); + int i, error; + + for (i = 0; i < fn->num_of_irqs; i++) { + set_bit(fn->irq_pos + i, fn->irq_mask); + + fn->irq[i] = irq_create_mapping(drvdata->irqdomain, + fn->irq_pos + i); + + irq_set_chip_data(fn->irq[i], fn); + irq_set_chip_and_handler(fn->irq[i], &rmi_irq_chip, + handle_simple_irq); + irq_set_nested_thread(fn->irq[i], 1); + + error = devm_request_threaded_irq(&fn->dev, fn->irq[i], NULL, + handler->attention, IRQF_ONESHOT, + dev_name(&fn->dev), fn); + if (error) { + dev_err(&fn->dev, "Error %d registering IRQ\n", error); + return error; + } + } + + return 0; +} + static int rmi_function_probe(struct device *dev) { struct rmi_function *fn = to_rmi_function(dev); @@ -178,7 +213,14 @@ static int rmi_function_probe(struct device *dev) if (handler->probe) { error = handler->probe(fn); - return error; + if (error) + return error; + } + + if (fn->num_of_irqs && handler->attention) { + error = rmi_create_function_irq(fn, handler); + if (error) + return error; } return 0; @@ -230,12 +272,18 @@ err_put_device: void rmi_unregister_function(struct rmi_function *fn) { + int i; + rmi_dbg(RMI_DEBUG_CORE, &fn->dev, "Unregistering F%02X.\n", fn->fd.function_number); device_del(&fn->dev); of_node_put(fn->dev.of_node); put_device(&fn->dev); + + for (i = 0; i < fn->num_of_irqs; i++) + irq_dispose_mapping(fn->irq[i]); + } /** diff --git a/drivers/input/rmi4/rmi_bus.h b/drivers/input/rmi4/rmi_bus.h index b7625a9ac66a..96383eab41ba 100644 --- a/drivers/input/rmi4/rmi_bus.h +++ b/drivers/input/rmi4/rmi_bus.h @@ -14,6 +14,12 @@ struct rmi_device; +/* + * The interrupt source count in the function descriptor can represent up to + * 6 interrupt sources in the normal manner. + */ +#define RMI_FN_MAX_IRQS 6 + /** * struct rmi_function - represents the implementation of an RMI4 * function for a particular device (basically, a driver for that RMI4 function) @@ -26,6 +32,7 @@ struct rmi_device; * @irq_pos: The position in the irq bitfield this function holds * @irq_mask: For convenience, can be used to mask IRQ bits off during ATTN * interrupt handling. + * @irqs: assigned virq numbers (up to num_of_irqs) * * @node: entry in device's list of functions */ @@ -36,6 +43,7 @@ struct rmi_function { struct list_head node; unsigned int num_of_irqs; + int irq[RMI_FN_MAX_IRQS]; unsigned int irq_pos; unsigned long irq_mask[]; }; @@ -76,7 +84,7 @@ struct rmi_function_handler { void (*remove)(struct rmi_function *fn); int (*config)(struct rmi_function *fn); int (*reset)(struct rmi_function *fn); - int (*attention)(struct rmi_function *fn, unsigned long *irq_bits); + irqreturn_t (*attention)(int irq, void *ctx); int (*suspend)(struct rmi_function *fn); int (*resume)(struct rmi_function *fn); }; diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index f5954981e9ee..2fb0ae0bdfe3 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include "rmi_bus.h" @@ -127,28 +128,11 @@ static int rmi_driver_process_config_requests(struct rmi_device *rmi_dev) return 0; } -static void process_one_interrupt(struct rmi_driver_data *data, - struct rmi_function *fn) -{ - struct rmi_function_handler *fh; - - if (!fn || !fn->dev.driver) - return; - - fh = to_rmi_function_handler(fn->dev.driver); - if (fh->attention) { - bitmap_and(data->fn_irq_bits, data->irq_status, fn->irq_mask, - data->irq_count); - if (!bitmap_empty(data->fn_irq_bits, data->irq_count)) - fh->attention(fn, data->fn_irq_bits); - } -} - static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) { struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); struct device *dev = &rmi_dev->dev; - struct rmi_function *entry; + int i; int error; if (!data) @@ -173,16 +157,8 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) */ mutex_unlock(&data->irq_mutex); - /* - * It would be nice to be able to use irq_chip to handle these - * nested IRQs. Unfortunately, most of the current customers for - * this driver are using older kernels (3.0.x) that don't support - * the features required for that. Once they've shifted to more - * recent kernels (say, 3.3 and higher), this should be switched to - * use irq_chip. - */ - list_for_each_entry(entry, &data->function_list, node) - process_one_interrupt(data, entry); + for_each_set_bit(i, data->irq_status, data->irq_count) + handle_nested_irq(irq_find_mapping(data->irqdomain, i)); if (data->input) input_sync(data->input); @@ -1000,9 +976,13 @@ EXPORT_SYMBOL_GPL(rmi_driver_resume); static int rmi_driver_remove(struct device *dev) { struct rmi_device *rmi_dev = to_rmi_device(dev); + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); rmi_disable_irq(rmi_dev, false); + irq_domain_remove(data->irqdomain); + data->irqdomain = NULL; + rmi_f34_remove_sysfs(rmi_dev); rmi_free_function_list(rmi_dev); @@ -1034,7 +1014,8 @@ int rmi_probe_interrupts(struct rmi_driver_data *data) { struct rmi_device *rmi_dev = data->rmi_dev; struct device *dev = &rmi_dev->dev; - int irq_count; + struct fwnode_handle *fwnode = rmi_dev->xport->dev->fwnode; + int irq_count = 0; size_t size; int retval; @@ -1045,7 +1026,6 @@ int rmi_probe_interrupts(struct rmi_driver_data *data) * being accessed. */ rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Counting IRQs.\n", __func__); - irq_count = 0; data->bootloader_mode = false; retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs); @@ -1057,6 +1037,15 @@ int rmi_probe_interrupts(struct rmi_driver_data *data) if (data->bootloader_mode) dev_warn(dev, "Device in bootloader mode.\n"); + /* Allocate and register a linear revmap irq_domain */ + data->irqdomain = irq_domain_create_linear(fwnode, irq_count, + &irq_domain_simple_ops, + data); + if (!data->irqdomain) { + dev_err(&rmi_dev->dev, "Failed to create IRQ domain\n"); + return PTR_ERR(data->irqdomain); + } + data->irq_count = irq_count; data->num_of_irq_regs = (data->irq_count + 7) / 8; @@ -1079,10 +1068,9 @@ int rmi_init_functions(struct rmi_driver_data *data) { struct rmi_device *rmi_dev = data->rmi_dev; struct device *dev = &rmi_dev->dev; - int irq_count; + int irq_count = 0; int retval; - irq_count = 0; rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Creating functions.\n", __func__); retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function); if (retval < 0) { diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c index 8a07ae147df6..4edaa14fe878 100644 --- a/drivers/input/rmi4/rmi_f01.c +++ b/drivers/input/rmi4/rmi_f01.c @@ -681,9 +681,9 @@ static int rmi_f01_resume(struct rmi_function *fn) return 0; } -static int rmi_f01_attention(struct rmi_function *fn, - unsigned long *irq_bits) +static irqreturn_t rmi_f01_attention(int irq, void *ctx) { + struct rmi_function *fn = ctx; struct rmi_device *rmi_dev = fn->rmi_dev; int error; u8 device_status; @@ -692,7 +692,7 @@ static int rmi_f01_attention(struct rmi_function *fn, if (error) { dev_err(&fn->dev, "Failed to read device status: %d.\n", error); - return error; + return IRQ_RETVAL(error); } if (RMI_F01_STATUS_BOOTLOADER(device_status)) @@ -704,11 +704,11 @@ static int rmi_f01_attention(struct rmi_function *fn, error = rmi_dev->driver->reset_handler(rmi_dev); if (error) { dev_err(&fn->dev, "Device reset failed: %d\n", error); - return error; + return IRQ_RETVAL(error); } } - return 0; + return IRQ_HANDLED; } struct rmi_function_handler rmi_f01_handler = { diff --git a/drivers/input/rmi4/rmi_f03.c b/drivers/input/rmi4/rmi_f03.c index 88822196d6b7..aaa1edc95522 100644 --- a/drivers/input/rmi4/rmi_f03.c +++ b/drivers/input/rmi4/rmi_f03.c @@ -244,8 +244,9 @@ static int rmi_f03_config(struct rmi_function *fn) return 0; } -static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) +static irqreturn_t rmi_f03_attention(int irq, void *ctx) { + struct rmi_function *fn = ctx; struct rmi_device *rmi_dev = fn->rmi_dev; struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); struct f03_data *f03 = dev_get_drvdata(&fn->dev); @@ -262,7 +263,7 @@ static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) /* First grab the data passed by the transport device */ if (drvdata->attn_data.size < ob_len) { dev_warn(&fn->dev, "F03 interrupted, but data is missing!\n"); - return 0; + return IRQ_HANDLED; } memcpy(obs, drvdata->attn_data.data, ob_len); @@ -277,7 +278,7 @@ static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) "%s: Failed to read F03 output buffers: %d\n", __func__, error); serio_interrupt(f03->serio, 0, SERIO_TIMEOUT); - return error; + return IRQ_RETVAL(error); } } @@ -303,7 +304,7 @@ static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) serio_interrupt(f03->serio, ob_data, serio_flags); } - return 0; + return IRQ_HANDLED; } static void rmi_f03_remove(struct rmi_function *fn) diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c index bc5e37f30ac1..5f2e1ef5accc 100644 --- a/drivers/input/rmi4/rmi_f11.c +++ b/drivers/input/rmi4/rmi_f11.c @@ -570,9 +570,7 @@ static inline u8 rmi_f11_parse_finger_state(const u8 *f_state, u8 n_finger) } static void rmi_f11_finger_handler(struct f11_data *f11, - struct rmi_2d_sensor *sensor, - unsigned long *irq_bits, int num_irq_regs, - int size) + struct rmi_2d_sensor *sensor, int size) { const u8 *f_state = f11->data.f_state; u8 finger_state; @@ -581,12 +579,7 @@ static void rmi_f11_finger_handler(struct f11_data *f11, int rel_fingers; int abs_size = sensor->nbr_fingers * RMI_F11_ABS_BYTES; - int abs_bits = bitmap_and(f11->result_bits, irq_bits, f11->abs_mask, - num_irq_regs * 8); - int rel_bits = bitmap_and(f11->result_bits, irq_bits, f11->rel_mask, - num_irq_regs * 8); - - if (abs_bits) { + if (sensor->report_abs) { if (abs_size > size) abs_fingers = size / RMI_F11_ABS_BYTES; else @@ -604,19 +597,7 @@ static void rmi_f11_finger_handler(struct f11_data *f11, rmi_f11_abs_pos_process(f11, sensor, &sensor->objs[i], finger_state, i); } - } - if (rel_bits) { - if ((abs_size + sensor->nbr_fingers * RMI_F11_REL_BYTES) > size) - rel_fingers = (size - abs_size) / RMI_F11_REL_BYTES; - else - rel_fingers = sensor->nbr_fingers; - - for (i = 0; i < rel_fingers; i++) - rmi_f11_rel_pos_report(f11, i); - } - - if (abs_bits) { /* * the absolute part is made in 2 parts to allow the kernel * tracking to take place. @@ -638,7 +619,16 @@ static void rmi_f11_finger_handler(struct f11_data *f11, } input_mt_sync_frame(sensor->input); + } else if (sensor->report_rel) { + if ((abs_size + sensor->nbr_fingers * RMI_F11_REL_BYTES) > size) + rel_fingers = (size - abs_size) / RMI_F11_REL_BYTES; + else + rel_fingers = sensor->nbr_fingers; + + for (i = 0; i < rel_fingers; i++) + rmi_f11_rel_pos_report(f11, i); } + } static int f11_2d_construct_data(struct f11_data *f11) @@ -1275,8 +1265,9 @@ static int rmi_f11_config(struct rmi_function *fn) return 0; } -static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits) +static irqreturn_t rmi_f11_attention(int irq, void *ctx) { + struct rmi_function *fn = ctx; struct rmi_device *rmi_dev = fn->rmi_dev; struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); struct f11_data *f11 = dev_get_drvdata(&fn->dev); @@ -1302,13 +1293,12 @@ static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits) data_base_addr, f11->sensor.data_pkt, f11->sensor.pkt_size); if (error < 0) - return error; + return IRQ_RETVAL(error); } - rmi_f11_finger_handler(f11, &f11->sensor, irq_bits, - drvdata->num_of_irq_regs, valid_bytes); + rmi_f11_finger_handler(f11, &f11->sensor, valid_bytes); - return 0; + return IRQ_HANDLED; } static int rmi_f11_resume(struct rmi_function *fn) diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index 8b0db086d68a..e226def74d82 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -197,10 +197,10 @@ static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1, int size) rmi_2d_sensor_abs_report(sensor, &sensor->objs[i], i); } -static int rmi_f12_attention(struct rmi_function *fn, - unsigned long *irq_nr_regs) +static irqreturn_t rmi_f12_attention(int irq, void *ctx) { int retval; + struct rmi_function *fn = ctx; struct rmi_device *rmi_dev = fn->rmi_dev; struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); struct f12_data *f12 = dev_get_drvdata(&fn->dev); @@ -222,7 +222,7 @@ static int rmi_f12_attention(struct rmi_function *fn, if (retval < 0) { dev_err(&fn->dev, "Failed to read object data. Code: %d.\n", retval); - return retval; + return IRQ_RETVAL(retval); } } @@ -232,7 +232,7 @@ static int rmi_f12_attention(struct rmi_function *fn, input_mt_sync_frame(sensor->input); - return 0; + return IRQ_HANDLED; } static int rmi_f12_write_control_regs(struct rmi_function *fn) diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c index 82e0f0d43d55..5e3ed5ac0c3e 100644 --- a/drivers/input/rmi4/rmi_f30.c +++ b/drivers/input/rmi4/rmi_f30.c @@ -122,8 +122,9 @@ static void rmi_f30_report_button(struct rmi_function *fn, } } -static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) +static irqreturn_t rmi_f30_attention(int irq, void *ctx) { + struct rmi_function *fn = ctx; struct f30_data *f30 = dev_get_drvdata(&fn->dev); struct rmi_driver_data *drvdata = dev_get_drvdata(&fn->rmi_dev->dev); int error; @@ -134,7 +135,7 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) if (drvdata->attn_data.size < f30->register_count) { dev_warn(&fn->dev, "F30 interrupted, but data is missing\n"); - return 0; + return IRQ_HANDLED; } memcpy(f30->data_regs, drvdata->attn_data.data, f30->register_count); @@ -147,7 +148,7 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) dev_err(&fn->dev, "%s: Failed to read F30 data registers: %d\n", __func__, error); - return error; + return IRQ_RETVAL(error); } } @@ -159,7 +160,7 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) rmi_f03_commit_buttons(f30->f03); } - return 0; + return IRQ_HANDLED; } static int rmi_f30_config(struct rmi_function *fn) diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c index f1f5ac539d5d..87a7d4ba382d 100644 --- a/drivers/input/rmi4/rmi_f34.c +++ b/drivers/input/rmi4/rmi_f34.c @@ -100,8 +100,9 @@ static int rmi_f34_command(struct f34_data *f34, u8 command, return 0; } -static int rmi_f34_attention(struct rmi_function *fn, unsigned long *irq_bits) +static irqreturn_t rmi_f34_attention(int irq, void *ctx) { + struct rmi_function *fn = ctx; struct f34_data *f34 = dev_get_drvdata(&fn->dev); int ret; u8 status; @@ -126,7 +127,7 @@ static int rmi_f34_attention(struct rmi_function *fn, unsigned long *irq_bits) complete(&f34->v7.cmd_done); } - return 0; + return IRQ_HANDLED; } static int rmi_f34_write_blocks(struct f34_data *f34, const void *data, diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c index 5343f2c08f15..98a935efec8e 100644 --- a/drivers/input/rmi4/rmi_f54.c +++ b/drivers/input/rmi4/rmi_f54.c @@ -610,11 +610,6 @@ error: mutex_unlock(&f54->data_mutex); } -static int rmi_f54_attention(struct rmi_function *fn, unsigned long *irqbits) -{ - return 0; -} - static int rmi_f54_config(struct rmi_function *fn) { struct rmi_driver *drv = fn->rmi_dev->driver; @@ -756,6 +751,5 @@ struct rmi_function_handler rmi_f54_handler = { .func = 0x54, .probe = rmi_f54_probe, .config = rmi_f54_config, - .attention = rmi_f54_attention, .remove = rmi_f54_remove, }; diff --git a/include/linux/rmi.h b/include/linux/rmi.h index 64125443f8a6..5ef5c7c412a7 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -354,6 +354,8 @@ struct rmi_driver_data { struct mutex irq_mutex; struct input_dev *input; + struct irq_domain *irqdomain; + u8 pdt_props; u8 num_rx_electrodes; -- cgit v1.2.3 From 977d5ba4507dfe5b1346597ee57750262d8d2b19 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 5 Jun 2018 17:05:15 +0200 Subject: soc: renesas: rcar-sysc: Make PM domain initialization more robust The quirk for R-Car E3 ES1.0 added in commit 086b399965a7ee7e ("soc: renesas: r8a77990-sysc: Add workaround for 3DG-{A,B}") makes the 3DG-A PM domain a subdomain of the 3DG-B PM domain. However, registering 3DG-A with its parent fails silently, as the 3DG-B PM domain hasn't been registered yet, and such failures are never reported. Fix this by: 1. Splitting PM Domain initialization in two steps, so all PM domains are registered before any child-parent links are established, 2. Reporting any failures in establishing child-parent relations. Check for and report pm_genpd_init() failures, too, as that function gained a return value in commit 7eb231c337e00735 ("PM / Domains: Convert pm_genpd_init() to return an error code"). Fixes: 086b399965a7ee7e ("soc: renesas: r8a77990-sysc: Add workaround for 3DG-{A,B}") Signed-off-by: Geert Uytterhoeven Tested-by: Yoshihiro Shimoda Reviewed-by: Ulf Hansson Signed-off-by: Simon Horman --- drivers/soc/renesas/rcar-sysc.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 95120acc4d80..50d03d8b4f9a 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -194,11 +194,12 @@ static int rcar_sysc_pd_power_on(struct generic_pm_domain *genpd) static bool has_cpg_mstp; -static void __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd) +static int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd) { struct generic_pm_domain *genpd = &pd->genpd; const char *name = pd->genpd.name; struct dev_power_governor *gov = &simple_qos_governor; + int error; if (pd->flags & PD_CPU) { /* @@ -251,7 +252,11 @@ static void __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd) rcar_sysc_power_up(&pd->ch); finalize: - pm_genpd_init(genpd, gov, false); + error = pm_genpd_init(genpd, gov, false); + if (error) + pr_err("Failed to init PM domain %s: %d\n", name, error); + + return error; } static const struct of_device_id rcar_sysc_matches[] __initconst = { @@ -375,6 +380,9 @@ static int __init rcar_sysc_pd_init(void) pr_debug("%pOF: syscier = 0x%08x\n", np, syscier); iowrite32(syscier, base + SYSCIER); + /* + * First, create all PM domains + */ for (i = 0; i < info->num_areas; i++) { const struct rcar_sysc_area *area = &info->areas[i]; struct rcar_sysc_pd *pd; @@ -397,14 +405,29 @@ static int __init rcar_sysc_pd_init(void) pd->ch.isr_bit = area->isr_bit; pd->flags = area->flags; - rcar_sysc_pd_setup(pd); - if (area->parent >= 0) - pm_genpd_add_subdomain(domains->domains[area->parent], - &pd->genpd); + error = rcar_sysc_pd_setup(pd); + if (error) + goto out_put; domains->domains[area->isr_bit] = &pd->genpd; } + /* + * Second, link all PM domains to their parents + */ + for (i = 0; i < info->num_areas; i++) { + const struct rcar_sysc_area *area = &info->areas[i]; + + if (!area->name || area->parent < 0) + continue; + + error = pm_genpd_add_subdomain(domains->domains[area->parent], + domains->domains[area->isr_bit]); + if (error) + pr_warn("Failed to add PM subdomain %s to parent %u\n", + area->name, area->parent); + } + error = of_genpd_add_provider_onecell(np, &domains->onecell_data); out_put: -- cgit v1.2.3 From 3e84c7651dde7cca43c5cfd7385086599cce5a5d Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Fri, 8 Jun 2018 08:14:50 +0800 Subject: HID: google: Add support for whiskers Another device in the hammer class, with USB id 0x5030. Signed-off-by: Nicolas Boichat Acked-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-google-hammer.c | 2 ++ drivers/hid/hid-ids.h | 1 + 2 files changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-google-hammer.c b/drivers/hid/hid-google-hammer.c index 7b8e17b03cb8..6bf4da7ad63a 100644 --- a/drivers/hid/hid-google-hammer.c +++ b/drivers/hid/hid-google-hammer.c @@ -124,6 +124,8 @@ static const struct hid_device_id hammer_devices[] = { USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STAFF) }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_WAND) }, + { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, + USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_WHISKERS) }, { } }; MODULE_DEVICE_TABLE(hid, hammer_devices); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index a85634fe033f..c7981ddd8776 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -452,6 +452,7 @@ #define USB_DEVICE_ID_GOOGLE_TOUCH_ROSE 0x5028 #define USB_DEVICE_ID_GOOGLE_STAFF 0x502b #define USB_DEVICE_ID_GOOGLE_WAND 0x502d +#define USB_DEVICE_ID_GOOGLE_WHISKERS 0x5030 #define USB_VENDOR_ID_GOTOP 0x08f2 #define USB_DEVICE_ID_SUPER_Q2 0x007f -- cgit v1.2.3 From 828d810550abc1fffff9b20545fec4bc150d5e82 Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Thu, 7 Jun 2018 02:32:52 -0400 Subject: IB/rxe: avoid double kfree skb In rxe_send, when network_type is not RDMA_NETWORK_IPV4 or RDMA_NETWORK_IPV6, skb is freed and -EINVAL is returned. Then rxe_xmit_packet will return -EINVAL, too. In rxe_requester, this skb is double freed. In rxe_requester, kfree_skb is needed only after fill_packet fails. So kfree_skb is moved from label err to test fill_packet. Fixes: 5793b4652155 ("IB/rxe: remove unnecessary skb_clone in xmit") Reported-by: Dan Carpenter Signed-off-by: Zhu Yanjun Reviewed-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/sw/rxe/rxe_req.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/infiniband/sw/rxe/rxe_req.c b/drivers/infiniband/sw/rxe/rxe_req.c index f30eeba3f772..829ecb93661f 100644 --- a/drivers/infiniband/sw/rxe/rxe_req.c +++ b/drivers/infiniband/sw/rxe/rxe_req.c @@ -709,6 +709,7 @@ next_wqe: if (fill_packet(qp, wqe, &pkt, skb, payload)) { pr_debug("qp#%d Error during fill packet\n", qp_num(qp)); + kfree_skb(skb); goto err; } @@ -740,7 +741,6 @@ next_wqe: goto next_wqe; err: - kfree_skb(skb); wqe->status = IB_WC_LOC_PROT_ERR; wqe->state = wqe_state_error; __rxe_do_task(&qp->comp.task); -- cgit v1.2.3 From 299eafee39a22a9d9a7c19ae592b230bd199f259 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 7 Jun 2018 14:19:15 -0500 Subject: IB/mlx5: Fix memory leak in mlx5_ib_create_flow In case memory resources for *ucmd* were allocated, release them before return. Addresses-Coverity-ID: 1469857 ("Resource leak") Fixes: 3b3233fbf02e ("IB/mlx5: Add flow counters binding support") Signed-off-by: Gustavo A. R. Silva Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/main.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 3544150f3469..f93b30060ca7 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -3546,29 +3546,35 @@ static struct ib_flow *mlx5_ib_create_flow(struct ib_qp *qp, return ERR_PTR(-ENOMEM); err = ib_copy_from_udata(ucmd, udata, required_ucmd_sz); - if (err) { - kfree(ucmd); - return ERR_PTR(err); - } + if (err) + goto free_ucmd; } - if (flow_attr->priority > MLX5_IB_FLOW_LAST_PRIO) - return ERR_PTR(-ENOMEM); + if (flow_attr->priority > MLX5_IB_FLOW_LAST_PRIO) { + err = -ENOMEM; + goto free_ucmd; + } if (domain != IB_FLOW_DOMAIN_USER || flow_attr->port > dev->num_ports || (flow_attr->flags & ~(IB_FLOW_ATTR_FLAGS_DONT_TRAP | - IB_FLOW_ATTR_FLAGS_EGRESS))) - return ERR_PTR(-EINVAL); + IB_FLOW_ATTR_FLAGS_EGRESS))) { + err = -EINVAL; + goto free_ucmd; + } if (is_egress && (flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT || - flow_attr->type == IB_FLOW_ATTR_MC_DEFAULT)) - return ERR_PTR(-EINVAL); + flow_attr->type == IB_FLOW_ATTR_MC_DEFAULT)) { + err = -EINVAL; + goto free_ucmd; + } dst = kzalloc(sizeof(*dst), GFP_KERNEL); - if (!dst) - return ERR_PTR(-ENOMEM); + if (!dst) { + err = -ENOMEM; + goto free_ucmd; + } mutex_lock(&dev->flow_db->lock); @@ -3637,8 +3643,8 @@ destroy_ft: unlock: mutex_unlock(&dev->flow_db->lock); kfree(dst); +free_ucmd: kfree(ucmd); - kfree(handler); return ERR_PTR(err); } -- cgit v1.2.3 From e31abf76f4d4d3202ca16b9668b11178df23d473 Mon Sep 17 00:00:00 2001 From: "weiyongjun (A)" Date: Thu, 7 Jun 2018 01:47:41 +0000 Subject: IB/mlx5: Fix return value check in flow_counters_set_data() In case of error, the function mlx5_fc_create() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Fixes: 3b3233fbf02e ("IB/mlx5: Add flow counters binding support") Signed-off-by: Wei Yongjun Acked-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index f93b30060ca7..645fc69997bc 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -3199,8 +3199,8 @@ static int flow_counters_set_data(struct ib_counters *ibcounters, if (!mcounters->hw_cntrs_hndl) { mcounters->hw_cntrs_hndl = mlx5_fc_create( to_mdev(ibcounters->device)->mdev, false); - if (!mcounters->hw_cntrs_hndl) { - ret = -ENOMEM; + if (IS_ERR(mcounters->hw_cntrs_hndl)) { + ret = PTR_ERR(mcounters->hw_cntrs_hndl); goto free; } hw_hndl = true; -- cgit v1.2.3 From 425cf5c1350a98b81f3ddda160b99c3be613a213 Mon Sep 17 00:00:00 2001 From: "Kalderon, Michal" Date: Mon, 11 Jun 2018 10:20:20 +0300 Subject: RDMA/qedr: Fix NULL pointer dereference when running over iWARP without RDMA-CM Some RoCE specific code in qedr_modify_qp was run over an iWARP device when running perftest benchmarks without the -R option. The commit 3e44e0ee0893 ("IB/providers: Avoid null netdev check for RoCE") exposed this. Dropping the check for NULL pointer on ndev in qedr_modify_qp lead to a null pointer dereference when running over iWARP. Before the code would identify ndev as being NULL and return an error. Fixes: 3e44e0ee0893 ("IB/providers: Avoid null netdev check for RoCE") Signed-off-by: Ariel Elior Signed-off-by: Michal Kalderon Reviewed-by: Parav Pandit Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/qedr/verbs.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index 614a954d0757..f9b198455fc9 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -1957,6 +1957,9 @@ int qedr_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, } if (attr_mask & (IB_QP_AV | IB_QP_PATH_MTU)) { + if (rdma_protocol_iwarp(&dev->ibdev, 1)) + return -EINVAL; + if (attr_mask & IB_QP_PATH_MTU) { if (attr->path_mtu < IB_MTU_256 || attr->path_mtu > IB_MTU_4096) { -- cgit v1.2.3 From fd629294759b1e6e5dfc0c9bf892e63553dcc6fa Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 11 Jun 2018 10:09:15 -0700 Subject: Input: synaptics-rmi4 - fix the error return code in rmi_probe_interrupts() The error return code PTR_ERR(data->irqdomain) is always 0 since data->irqdomain is equal to NULL in this error handling case. Fixes: 24d28e4f1271 ("Input: synaptics-rmi4 - convert irq distribution to irq_domain") Signed-off-by: Wei Yongjun Reviewed-by: Lyude Paul Reviewed-by: Nick Desaulniers Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 2fb0ae0bdfe3..cb6d983e8033 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -1043,7 +1043,7 @@ int rmi_probe_interrupts(struct rmi_driver_data *data) data); if (!data->irqdomain) { dev_err(&rmi_dev->dev, "Failed to create IRQ domain\n"); - return PTR_ERR(data->irqdomain); + return -ENOMEM; } data->irq_count = irq_count; -- cgit v1.2.3 From 645a397d325db6e1bb36588095ae637738b37693 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Tue, 5 Jun 2018 10:34:54 -0700 Subject: Input: synaptics-rmi4 - fix axis-swap behavior The documentation for the touchscreen-swapped-x-y property states that swapping is done after inverting if both are used. RMI4 did it the other way around, leading to inconsistent behavior with regard to other touchscreens. Signed-off-by: Lucas Stach Tested-by: Nick Dyer Signed-off-by: Dmitry Torokhov --- drivers/input/rmi4/rmi_2d_sensor.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/input/rmi4/rmi_2d_sensor.c b/drivers/input/rmi4/rmi_2d_sensor.c index 8bb866c7b985..8eeffa066022 100644 --- a/drivers/input/rmi4/rmi_2d_sensor.c +++ b/drivers/input/rmi4/rmi_2d_sensor.c @@ -32,15 +32,15 @@ void rmi_2d_sensor_abs_process(struct rmi_2d_sensor *sensor, if (obj->type == RMI_2D_OBJECT_NONE) return; - if (axis_align->swap_axes) - swap(obj->x, obj->y); - if (axis_align->flip_x) obj->x = sensor->max_x - obj->x; if (axis_align->flip_y) obj->y = sensor->max_y - obj->y; + if (axis_align->swap_axes) + swap(obj->x, obj->y); + /* * Here checking if X offset or y offset are specified is * redundant. We just add the offsets or clip the values. @@ -120,15 +120,15 @@ void rmi_2d_sensor_rel_report(struct rmi_2d_sensor *sensor, int x, int y) x = min(RMI_2D_REL_POS_MAX, max(RMI_2D_REL_POS_MIN, (int)x)); y = min(RMI_2D_REL_POS_MAX, max(RMI_2D_REL_POS_MIN, (int)y)); - if (axis_align->swap_axes) - swap(x, y); - if (axis_align->flip_x) x = min(RMI_2D_REL_POS_MAX, -x); if (axis_align->flip_y) y = min(RMI_2D_REL_POS_MAX, -y); + if (axis_align->swap_axes) + swap(x, y); + if (x || y) { input_report_rel(sensor->input, REL_X, x); input_report_rel(sensor->input, REL_Y, y); @@ -141,17 +141,10 @@ static void rmi_2d_sensor_set_input_params(struct rmi_2d_sensor *sensor) struct input_dev *input = sensor->input; int res_x; int res_y; + int max_x, max_y; int input_flags = 0; if (sensor->report_abs) { - if (sensor->axis_align.swap_axes) { - swap(sensor->max_x, sensor->max_y); - swap(sensor->axis_align.clip_x_low, - sensor->axis_align.clip_y_low); - swap(sensor->axis_align.clip_x_high, - sensor->axis_align.clip_y_high); - } - sensor->min_x = sensor->axis_align.clip_x_low; if (sensor->axis_align.clip_x_high) sensor->max_x = min(sensor->max_x, @@ -163,14 +156,19 @@ static void rmi_2d_sensor_set_input_params(struct rmi_2d_sensor *sensor) sensor->axis_align.clip_y_high); set_bit(EV_ABS, input->evbit); - input_set_abs_params(input, ABS_MT_POSITION_X, 0, sensor->max_x, - 0, 0); - input_set_abs_params(input, ABS_MT_POSITION_Y, 0, sensor->max_y, - 0, 0); + + max_x = sensor->max_x; + max_y = sensor->max_y; + if (sensor->axis_align.swap_axes) + swap(max_x, max_y); + input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_x, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_y, 0, 0); if (sensor->x_mm && sensor->y_mm) { res_x = (sensor->max_x - sensor->min_x) / sensor->x_mm; res_y = (sensor->max_y - sensor->min_y) / sensor->y_mm; + if (sensor->axis_align.swap_axes) + swap(res_x, res_y); input_abs_set_res(input, ABS_X, res_x); input_abs_set_res(input, ABS_Y, res_y); -- cgit v1.2.3 From bf6247a70f2b7569180304041b63b59ab14217bc Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 5 Jun 2018 10:08:44 -0700 Subject: Input: make input_report_slot_state() return boolean Let's make input_report_slot_state() return boolean representing whether the contact is active or not. This will allow writing code like: if (input_mt_report_slot_state(input, obj->mt_tool, obj->type != RMI_2D_OBJECT_NONE) { input_event(sensor->input, EV_ABS, ABS_MT_POSITION_X, obj->x); input_event(sensor->input, EV_ABS, ABS_MT_POSITION_Y, obj->y); ... } instead of: input_mt_report_slot_state(input, obj->mt_tool, obj->type != RMI_2D_OBJECT_NONE); if (obj->type != RMI_2D_OBJECT_NONE) { input_event(sensor->input, EV_ABS, ABS_MT_POSITION_X, obj->x); input_event(sensor->input, EV_ABS, ABS_MT_POSITION_Y, obj->y); ... } Reviewed-by: Henrik Rydberg Acked-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/input-mt.c | 10 +++++++--- include/linux/input/mt.h | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index a1bbec9cda8d..8de52e3c00c0 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -131,8 +131,10 @@ EXPORT_SYMBOL(input_mt_destroy_slots); * inactive, or if the tool type is changed, a new tracking id is * assigned to the slot. The tool type is only reported if the * corresponding absbit field is set. + * + * Returns true if contact is active. */ -void input_mt_report_slot_state(struct input_dev *dev, +bool input_mt_report_slot_state(struct input_dev *dev, unsigned int tool_type, bool active) { struct input_mt *mt = dev->mt; @@ -140,14 +142,14 @@ void input_mt_report_slot_state(struct input_dev *dev, int id; if (!mt) - return; + return false; slot = &mt->slots[mt->slot]; slot->frame = mt->frame; if (!active) { input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); - return; + return false; } id = input_mt_get_value(slot, ABS_MT_TRACKING_ID); @@ -156,6 +158,8 @@ void input_mt_report_slot_state(struct input_dev *dev, input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id); input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type); + + return true; } EXPORT_SYMBOL(input_mt_report_slot_state); diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h index d7188de4db96..3f4bf60b0bb5 100644 --- a/include/linux/input/mt.h +++ b/include/linux/input/mt.h @@ -100,7 +100,7 @@ static inline bool input_is_mt_axis(int axis) return axis == ABS_MT_SLOT || input_is_mt_value(axis); } -void input_mt_report_slot_state(struct input_dev *dev, +bool input_mt_report_slot_state(struct input_dev *dev, unsigned int tool_type, bool active); void input_mt_report_finger_count(struct input_dev *dev, int count); -- cgit v1.2.3 From c258e84b180d38f76ab9962cf5bfc10720af1360 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 15 Aug 2017 16:00:42 -0700 Subject: Input: do not assign new tracking ID when changing tool type We allow changing tool type (from MT_TOOL_FINGER to MT_TOOL_PALM) so we should not be forcing new tracking ID for the slot. Acked-by: Benjamin Tissoires Reviewed-by: Peter Hutterer Signed-off-by: Dmitry Torokhov --- drivers/input/input-mt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index 8de52e3c00c0..4a69716e5461 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -153,7 +153,7 @@ bool input_mt_report_slot_state(struct input_dev *dev, } id = input_mt_get_value(slot, ABS_MT_TRACKING_ID); - if (id < 0 || input_mt_get_value(slot, ABS_MT_TOOL_TYPE) != tool_type) + if (id < 0) id = input_mt_new_trkid(mt); input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id); -- cgit v1.2.3 From 3dc7c7badb7502ec3e3aa817a8bdd9e53aa54c52 Mon Sep 17 00:00:00 2001 From: Christophe Jaillet Date: Mon, 11 Jun 2018 20:15:11 +0200 Subject: IB/mlx4: Fix an error handling path in 'mlx4_ib_rereg_user_mr()' Before returning -EPERM we should release some resources, as already done in the other error handling path of the function. Fixes: d8f9cc328c88 ("IB/mlx4: Mark user MR as writable if actual virtual memory is writable") Signed-off-by: Christophe JAILLET Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx4/mr.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index ed1f253faf97..c7c85c22e4e3 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -486,8 +486,11 @@ int mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags, } if (flags & IB_MR_REREG_ACCESS) { - if (ib_access_writable(mr_access_flags) && !mmr->umem->writable) - return -EPERM; + if (ib_access_writable(mr_access_flags) && + !mmr->umem->writable) { + err = -EPERM; + goto release_mpt_entry; + } err = mlx4_mr_hw_change_access(dev->dev, *pmpt_entry, convert_access(mr_access_flags)); -- cgit v1.2.3 From 5c618c0cf451f1d9746296b0d30c84af1bce3604 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Thu, 24 May 2018 12:18:58 +0200 Subject: s390/dasd: simplify locking in process_final_queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify locking in __dasd_device_process_final_queue to fix the following sparse warning: drivers/s390/block/dasd.c:1902:9: warning: context imbalance in '__dasd_device_process_final_queue' - different lock contexts for basic block Signed-off-by: Sebastian Ott Reviewed-by: Stefan Haberland Reviewed-by: Jan Höppner Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 59 +++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 73cce3ecb97f..790b1fa3fec0 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1885,6 +1885,33 @@ static void __dasd_device_process_ccw_queue(struct dasd_device *device, } } +static void __dasd_process_cqr(struct dasd_device *device, + struct dasd_ccw_req *cqr) +{ + char errorstring[ERRORLENGTH]; + + switch (cqr->status) { + case DASD_CQR_SUCCESS: + cqr->status = DASD_CQR_DONE; + break; + case DASD_CQR_ERROR: + cqr->status = DASD_CQR_NEED_ERP; + break; + case DASD_CQR_CLEARED: + cqr->status = DASD_CQR_TERMINATED; + break; + default: + /* internal error 12 - wrong cqr status*/ + snprintf(errorstring, ERRORLENGTH, "12 %p %x02", cqr, cqr->status); + dev_err(&device->cdev->dev, + "An error occurred in the DASD device driver, " + "reason=%s\n", errorstring); + BUG(); + } + if (cqr->callback) + cqr->callback(cqr, cqr->callback_data); +} + /* * the cqrs from the final queue are returned to the upper layer * by setting a dasd_block state and calling the callback function @@ -1895,40 +1922,18 @@ static void __dasd_device_process_final_queue(struct dasd_device *device, struct list_head *l, *n; struct dasd_ccw_req *cqr; struct dasd_block *block; - void (*callback)(struct dasd_ccw_req *, void *data); - void *callback_data; - char errorstring[ERRORLENGTH]; list_for_each_safe(l, n, final_queue) { cqr = list_entry(l, struct dasd_ccw_req, devlist); list_del_init(&cqr->devlist); block = cqr->block; - callback = cqr->callback; - callback_data = cqr->callback_data; - if (block) + if (!block) { + __dasd_process_cqr(device, cqr); + } else { spin_lock_bh(&block->queue_lock); - switch (cqr->status) { - case DASD_CQR_SUCCESS: - cqr->status = DASD_CQR_DONE; - break; - case DASD_CQR_ERROR: - cqr->status = DASD_CQR_NEED_ERP; - break; - case DASD_CQR_CLEARED: - cqr->status = DASD_CQR_TERMINATED; - break; - default: - /* internal error 12 - wrong cqr status*/ - snprintf(errorstring, ERRORLENGTH, "12 %p %x02", cqr, cqr->status); - dev_err(&device->cdev->dev, - "An error occurred in the DASD device driver, " - "reason=%s\n", errorstring); - BUG(); - } - if (cqr->callback != NULL) - (callback)(cqr, callback_data); - if (block) + __dasd_process_cqr(device, cqr); spin_unlock_bh(&block->queue_lock); + } } } -- cgit v1.2.3 From c5205f2ff2bec6acf398211aed66b3e6ac44eee6 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 4 Jun 2018 19:07:39 +0200 Subject: s390/dasd: move dasd_ccw_req to per request data Let the block layer allocate per request data to store struct dasd_ccw_req. We still need extra preallocated memory for usage by ccw programs (which vary in length) and for requests which don't originate from the block layer. Link: https://lkml.kernel.org/r/20180530074130.GA6927@infradead.org Signed-off-by: Sebastian Ott Reviewed-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 46 ++++++++++++++++++++++-------------------- drivers/s390/block/dasd_diag.c | 3 ++- drivers/s390/block/dasd_eckd.c | 46 +++++++++++++++++++++++------------------- drivers/s390/block/dasd_fba.c | 6 ++++-- drivers/s390/block/dasd_int.h | 3 ++- 5 files changed, 57 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 790b1fa3fec0..01a1d1dabb43 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1267,35 +1267,37 @@ struct dasd_ccw_req *dasd_kmalloc_request(int magic, int cplength, } EXPORT_SYMBOL(dasd_kmalloc_request); -struct dasd_ccw_req *dasd_smalloc_request(int magic, int cplength, - int datasize, - struct dasd_device *device) +struct dasd_ccw_req *dasd_smalloc_request(int magic, int cplength, int datasize, + struct dasd_device *device, + struct dasd_ccw_req *cqr) { unsigned long flags; - struct dasd_ccw_req *cqr; - char *data; - int size; + char *data, *chunk; + int size = 0; - size = (sizeof(struct dasd_ccw_req) + 7L) & -8L; if (cplength > 0) size += cplength * sizeof(struct ccw1); if (datasize > 0) size += datasize; + if (!cqr) + size += (sizeof(*cqr) + 7L) & -8L; + spin_lock_irqsave(&device->mem_lock, flags); - cqr = (struct dasd_ccw_req *) - dasd_alloc_chunk(&device->ccw_chunks, size); + data = chunk = dasd_alloc_chunk(&device->ccw_chunks, size); spin_unlock_irqrestore(&device->mem_lock, flags); - if (cqr == NULL) + if (!chunk) return ERR_PTR(-ENOMEM); - memset(cqr, 0, sizeof(struct dasd_ccw_req)); - data = (char *) cqr + ((sizeof(struct dasd_ccw_req) + 7L) & -8L); - cqr->cpaddr = NULL; + if (!cqr) { + cqr = (void *) data; + data += (sizeof(*cqr) + 7L) & -8L; + } + memset(cqr, 0, sizeof(*cqr)); + cqr->mem_chunk = chunk; if (cplength > 0) { - cqr->cpaddr = (struct ccw1 *) data; - data += cplength*sizeof(struct ccw1); - memset(cqr->cpaddr, 0, cplength*sizeof(struct ccw1)); + cqr->cpaddr = data; + data += cplength * sizeof(struct ccw1); + memset(cqr->cpaddr, 0, cplength * sizeof(struct ccw1)); } - cqr->data = NULL; if (datasize > 0) { cqr->data = data; memset(cqr->data, 0, datasize); @@ -1333,7 +1335,7 @@ void dasd_sfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device) unsigned long flags; spin_lock_irqsave(&device->mem_lock, flags); - dasd_free_chunk(&device->ccw_chunks, cqr); + dasd_free_chunk(&device->ccw_chunks, cqr->mem_chunk); spin_unlock_irqrestore(&device->mem_lock, flags); dasd_put_device(device); } @@ -3046,7 +3048,6 @@ static blk_status_t do_dasd_request(struct blk_mq_hw_ctx *hctx, cqr->callback_data = req; cqr->status = DASD_CQR_FILLED; cqr->dq = dq; - *((struct dasd_ccw_req **) blk_mq_rq_to_pdu(req)) = cqr; blk_mq_start_request(req); spin_lock(&block->queue_lock); @@ -3077,7 +3078,7 @@ enum blk_eh_timer_return dasd_times_out(struct request *req, bool reserved) unsigned long flags; int rc = 0; - cqr = *((struct dasd_ccw_req **) blk_mq_rq_to_pdu(req)); + cqr = blk_mq_rq_to_pdu(req); if (!cqr) return BLK_EH_DONE; @@ -3179,7 +3180,7 @@ static int dasd_alloc_queue(struct dasd_block *block) int rc; block->tag_set.ops = &dasd_mq_ops; - block->tag_set.cmd_size = sizeof(struct dasd_ccw_req *); + block->tag_set.cmd_size = sizeof(struct dasd_ccw_req); block->tag_set.nr_hw_queues = DASD_NR_HW_QUEUES; block->tag_set.queue_depth = DASD_MAX_LCU_DEV * DASD_REQ_PER_DEV; block->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; @@ -4043,7 +4044,8 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device, struct ccw1 *ccw; unsigned long *idaw; - cqr = dasd_smalloc_request(magic, 1 /* RDC */, rdc_buffer_size, device); + cqr = dasd_smalloc_request(magic, 1 /* RDC */, rdc_buffer_size, device, + NULL); if (IS_ERR(cqr)) { /* internal error 13 - Allocating the RDC request failed*/ diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 131f1989f6f3..e1fe02477ea8 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -536,7 +536,8 @@ static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev, /* Build the request */ datasize = sizeof(struct dasd_diag_req) + count*sizeof(struct dasd_diag_bio); - cqr = dasd_smalloc_request(DASD_DIAG_MAGIC, 0, datasize, memdev); + cqr = dasd_smalloc_request(DASD_DIAG_MAGIC, 0, datasize, memdev, + blk_mq_rq_to_pdu(req)); if (IS_ERR(cqr)) return cqr; diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index be208e7adcb4..bbf95b78ef5d 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -886,7 +886,7 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device, } cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */, 0, /* use rcd_buf as data ara */ - device); + device, NULL); if (IS_ERR(cqr)) { DBF_DEV_EVENT(DBF_WARNING, device, "%s", "Could not allocate RCD request"); @@ -1442,7 +1442,7 @@ static int dasd_eckd_read_features(struct dasd_device *device) cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, (sizeof(struct dasd_psf_prssd_data) + sizeof(struct dasd_rssd_features)), - device); + device, NULL); if (IS_ERR(cqr)) { DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "Could not " "allocate initialization request"); @@ -1504,7 +1504,7 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device, cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ , sizeof(struct dasd_psf_ssc_data), - device); + device, NULL); if (IS_ERR(cqr)) { DBF_DEV_EVENT(DBF_WARNING, device, "%s", @@ -1815,7 +1815,8 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) cplength = 8; datasize = sizeof(struct DE_eckd_data) + 2*sizeof(struct LO_eckd_data); - cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device, + NULL); if (IS_ERR(cqr)) return cqr; ccw = cqr->cpaddr; @@ -2092,7 +2093,8 @@ dasd_eckd_build_check_tcw(struct dasd_device *base, struct format_data_t *fdata, */ itcw_size = itcw_calc_size(0, count, 0); - cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev, + NULL); if (IS_ERR(cqr)) return cqr; @@ -2186,7 +2188,7 @@ dasd_eckd_build_check(struct dasd_device *base, struct format_data_t *fdata, cplength += count; cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, - startdev); + startdev, NULL); if (IS_ERR(cqr)) return cqr; @@ -2332,7 +2334,7 @@ dasd_eckd_build_format(struct dasd_device *base, } /* Allocate the format ccw request. */ fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, - datasize, startdev); + datasize, startdev, NULL); if (IS_ERR(fcp)) return fcp; @@ -3103,7 +3105,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( } /* Allocate the ccw request. */ cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, - startdev); + startdev, blk_mq_rq_to_pdu(req)); if (IS_ERR(cqr)) return cqr; ccw = cqr->cpaddr; @@ -3262,7 +3264,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( /* Allocate the ccw request. */ cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, - startdev); + startdev, blk_mq_rq_to_pdu(req)); if (IS_ERR(cqr)) return cqr; ccw = cqr->cpaddr; @@ -3595,7 +3597,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( /* Allocate the ccw request. */ itcw_size = itcw_calc_size(0, ctidaw, 0); - cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev, + blk_mq_rq_to_pdu(req)); if (IS_ERR(cqr)) return cqr; @@ -3862,7 +3865,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_raw(struct dasd_device *startdev, /* Allocate the ccw request. */ cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, - datasize, startdev); + datasize, startdev, blk_mq_rq_to_pdu(req)); if (IS_ERR(cqr)) return cqr; @@ -4102,7 +4105,7 @@ dasd_eckd_release(struct dasd_device *device) return -EACCES; useglobal = 0; - cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL); if (IS_ERR(cqr)) { mutex_lock(&dasd_reserve_mutex); useglobal = 1; @@ -4157,7 +4160,7 @@ dasd_eckd_reserve(struct dasd_device *device) return -EACCES; useglobal = 0; - cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL); if (IS_ERR(cqr)) { mutex_lock(&dasd_reserve_mutex); useglobal = 1; @@ -4211,7 +4214,7 @@ dasd_eckd_steal_lock(struct dasd_device *device) return -EACCES; useglobal = 0; - cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device, NULL); if (IS_ERR(cqr)) { mutex_lock(&dasd_reserve_mutex); useglobal = 1; @@ -4271,7 +4274,8 @@ static int dasd_eckd_snid(struct dasd_device *device, useglobal = 0; cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, - sizeof(struct dasd_snid_data), device); + sizeof(struct dasd_snid_data), device, + NULL); if (IS_ERR(cqr)) { mutex_lock(&dasd_reserve_mutex); useglobal = 1; @@ -4331,7 +4335,7 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp) cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, (sizeof(struct dasd_psf_prssd_data) + sizeof(struct dasd_rssd_perf_stats_t)), - device); + device, NULL); if (IS_ERR(cqr)) { DBF_DEV_EVENT(DBF_WARNING, device, "%s", "Could not allocate initialization request"); @@ -4477,7 +4481,7 @@ static int dasd_symm_io(struct dasd_device *device, void __user *argp) psf1 = psf_data[1]; /* setup CCWs for PSF + RSSD */ - cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2 , 0, device); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2, 0, device, NULL); if (IS_ERR(cqr)) { DBF_DEV_EVENT(DBF_WARNING, device, "%s", "Could not allocate initialization request"); @@ -5037,7 +5041,7 @@ static int dasd_eckd_read_message_buffer(struct dasd_device *device, cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, (sizeof(struct dasd_psf_prssd_data) + sizeof(struct dasd_rssd_messages)), - device); + device, NULL); if (IS_ERR(cqr)) { DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "Could not allocate read message buffer request"); @@ -5126,7 +5130,7 @@ static int dasd_eckd_query_host_access(struct dasd_device *device, cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, sizeof(struct dasd_psf_prssd_data) + 1, - device); + device, NULL); if (IS_ERR(cqr)) { DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "Could not allocate read message buffer request"); @@ -5284,8 +5288,8 @@ dasd_eckd_psf_cuir_response(struct dasd_device *device, int response, int rc; cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ , - sizeof(struct dasd_psf_cuir_response), - device); + sizeof(struct dasd_psf_cuir_response), + device, NULL); if (IS_ERR(cqr)) { DBF_DEV_EVENT(DBF_WARNING, device, "%s", diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index a6b132f7e869..56007a3e7f11 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -356,7 +356,8 @@ static struct dasd_ccw_req *dasd_fba_build_cp_discard( datasize = sizeof(struct DE_fba_data) + nr_ccws * (sizeof(struct LO_fba_data) + sizeof(struct ccw1)); - cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev); + cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev, + blk_mq_rq_to_pdu(req)); if (IS_ERR(cqr)) return cqr; @@ -490,7 +491,8 @@ static struct dasd_ccw_req *dasd_fba_build_cp_regular( datasize += (count - 1)*sizeof(struct LO_fba_data); } /* Allocate the ccw request. */ - cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev); + cqr = dasd_smalloc_request(DASD_FBA_MAGIC, cplength, datasize, memdev, + blk_mq_rq_to_pdu(req)); if (IS_ERR(cqr)) return cqr; ccw = cqr->cpaddr; diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 96709b1a7bf8..0844e5e2f566 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -184,6 +184,7 @@ struct dasd_ccw_req { struct irb irb; /* device status in case of an error */ struct dasd_ccw_req *refers; /* ERP-chain queueing. */ void *function; /* originating ERP action */ + void *mem_chunk; /* these are for statistics only */ unsigned long buildclk; /* TOD-clock of request generation */ @@ -716,7 +717,7 @@ extern struct kmem_cache *dasd_page_cache; struct dasd_ccw_req * dasd_kmalloc_request(int , int, int, struct dasd_device *); struct dasd_ccw_req * -dasd_smalloc_request(int , int, int, struct dasd_device *); +dasd_smalloc_request(int, int, int, struct dasd_device *, struct dasd_ccw_req *); void dasd_kfree_request(struct dasd_ccw_req *, struct dasd_device *); void dasd_sfree_request(struct dasd_ccw_req *, struct dasd_device *); void dasd_wakeup_cb(struct dasd_ccw_req *, void *); -- cgit v1.2.3 From d8a72d414baf217a2eea9c73f3aac11052161015 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 5 Jun 2018 13:34:42 +0200 Subject: s390/dasd: remove dasd_kmalloc_set_cda There is no user of this function. Just remove it. Signed-off-by: Sebastian Ott Reviewed-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_int.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 0844e5e2f566..885e7416c368 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -722,12 +722,6 @@ void dasd_kfree_request(struct dasd_ccw_req *, struct dasd_device *); void dasd_sfree_request(struct dasd_ccw_req *, struct dasd_device *); void dasd_wakeup_cb(struct dasd_ccw_req *, void *); -static inline int -dasd_kmalloc_set_cda(struct ccw1 *ccw, void *cda, struct dasd_device *device) -{ - return set_normalized_cda(ccw, cda); -} - struct dasd_device *dasd_alloc_device(void); void dasd_free_device(struct dasd_device *); -- cgit v1.2.3 From 61d388321032be9097935bbc5efdd6ac42691ed4 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 4 Jun 2018 19:39:38 +0200 Subject: s390/dasd: reshuffle struct dasd_ccw_req Move some members of struct dasd_ccw_req to get rid of padding bytes. This saves 16 bytes per dasd request. Signed-off-by: Sebastian Ott Reviewed-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_int.h | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 885e7416c368..55bcbbed1b1d 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -158,41 +158,33 @@ do { \ struct dasd_ccw_req { unsigned int magic; /* Eye catcher */ + int intrc; /* internal error, e.g. from start_IO */ struct list_head devlist; /* for dasd_device request queue */ struct list_head blocklist; /* for dasd_block request queue */ - - /* Where to execute what... */ struct dasd_block *block; /* the originating block device */ struct dasd_device *memdev; /* the device used to allocate this */ struct dasd_device *startdev; /* device the request is started on */ struct dasd_device *basedev; /* base device if no block->base */ void *cpaddr; /* address of ccw or tcw */ + short retries; /* A retry counter */ unsigned char cpmode; /* 0 = cmd mode, 1 = itcw */ char status; /* status of this request */ - short retries; /* A retry counter */ + char lpm; /* logical path mask */ unsigned long flags; /* flags of this request */ struct dasd_queue *dq; - - /* ... and how */ unsigned long starttime; /* jiffies time of request start */ unsigned long expires; /* expiration period in jiffies */ - char lpm; /* logical path mask */ void *data; /* pointer to data area */ - - /* these are important for recovering erroneous requests */ - int intrc; /* internal error, e.g. from start_IO */ struct irb irb; /* device status in case of an error */ struct dasd_ccw_req *refers; /* ERP-chain queueing. */ void *function; /* originating ERP action */ void *mem_chunk; - /* these are for statistics only */ unsigned long buildclk; /* TOD-clock of request generation */ unsigned long startclk; /* TOD-clock of request start */ unsigned long stopclk; /* TOD-clock of request interrupt */ unsigned long endclk; /* TOD-clock of request termination */ - /* Callback that is called after reaching final status. */ void (*callback)(struct dasd_ccw_req *, void *data); void *callback_data; }; -- cgit v1.2.3 From ec530174c43798099d305fbd6511e5d7fc7616d4 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 4 Jun 2018 19:18:03 +0200 Subject: s390/dasd: only use preallocated requests Change the remaining users of dasd_kmalloc_request to use preallocated memory and remove this function. Signed-off-by: Sebastian Ott Reviewed-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 66 ----------------------------------------- drivers/s390/block/dasd_alias.c | 6 ++-- drivers/s390/block/dasd_eer.c | 10 +++---- drivers/s390/block/dasd_int.h | 3 -- 4 files changed, 8 insertions(+), 77 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 01a1d1dabb43..d3a38c421503 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1222,51 +1222,6 @@ static void dasd_hosts_init(struct dentry *base_dentry, device->hosts_dentry = pde; } -/* - * Allocate memory for a channel program with 'cplength' channel - * command words and 'datasize' additional space. There are two - * variantes: 1) dasd_kmalloc_request uses kmalloc to get the needed - * memory and 2) dasd_smalloc_request uses the static ccw memory - * that gets allocated for each device. - */ -struct dasd_ccw_req *dasd_kmalloc_request(int magic, int cplength, - int datasize, - struct dasd_device *device) -{ - struct dasd_ccw_req *cqr; - - /* Sanity checks */ - BUG_ON(datasize > PAGE_SIZE || - (cplength*sizeof(struct ccw1)) > PAGE_SIZE); - - cqr = kzalloc(sizeof(struct dasd_ccw_req), GFP_ATOMIC); - if (cqr == NULL) - return ERR_PTR(-ENOMEM); - cqr->cpaddr = NULL; - if (cplength > 0) { - cqr->cpaddr = kcalloc(cplength, sizeof(struct ccw1), - GFP_ATOMIC | GFP_DMA); - if (cqr->cpaddr == NULL) { - kfree(cqr); - return ERR_PTR(-ENOMEM); - } - } - cqr->data = NULL; - if (datasize > 0) { - cqr->data = kzalloc(datasize, GFP_ATOMIC | GFP_DMA); - if (cqr->data == NULL) { - kfree(cqr->cpaddr); - kfree(cqr); - return ERR_PTR(-ENOMEM); - } - } - cqr->magic = magic; - set_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); - dasd_get_device(device); - return cqr; -} -EXPORT_SYMBOL(dasd_kmalloc_request); - struct dasd_ccw_req *dasd_smalloc_request(int magic, int cplength, int datasize, struct dasd_device *device, struct dasd_ccw_req *cqr) @@ -1309,27 +1264,6 @@ struct dasd_ccw_req *dasd_smalloc_request(int magic, int cplength, int datasize, } EXPORT_SYMBOL(dasd_smalloc_request); -/* - * Free memory of a channel program. This function needs to free all the - * idal lists that might have been created by dasd_set_cda and the - * struct dasd_ccw_req itself. - */ -void dasd_kfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device) -{ - struct ccw1 *ccw; - - /* Clear any idals used for the request. */ - ccw = cqr->cpaddr; - do { - clear_normalized_cda(ccw); - } while (ccw++->flags & (CCW_FLAG_CC | CCW_FLAG_DC)); - kfree(cqr->cpaddr); - kfree(cqr->data); - kfree(cqr); - dasd_put_device(device); -} -EXPORT_SYMBOL(dasd_kfree_request); - void dasd_sfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device) { unsigned long flags; diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c index 5e963fe0e38d..e36a114354fc 100644 --- a/drivers/s390/block/dasd_alias.c +++ b/drivers/s390/block/dasd_alias.c @@ -407,9 +407,9 @@ static int read_unit_address_configuration(struct dasd_device *device, int rc; unsigned long flags; - cqr = dasd_kmalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, (sizeof(struct dasd_psf_prssd_data)), - device); + device, NULL); if (IS_ERR(cqr)) return PTR_ERR(cqr); cqr->startdev = device; @@ -457,7 +457,7 @@ static int read_unit_address_configuration(struct dasd_device *device, lcu->flags |= NEED_UAC_UPDATE; spin_unlock_irqrestore(&lcu->lock, flags); } - dasd_kfree_request(cqr, cqr->memdev); + dasd_sfree_request(cqr, cqr->memdev); return rc; } diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c index fb2c3599d95c..6545342bd43f 100644 --- a/drivers/s390/block/dasd_eer.c +++ b/drivers/s390/block/dasd_eer.c @@ -447,7 +447,7 @@ static void dasd_eer_snss_cb(struct dasd_ccw_req *cqr, void *data) * is a new ccw in device->eer_cqr. Free the "old" * snss request now. */ - dasd_kfree_request(cqr, device); + dasd_sfree_request(cqr, device); } /* @@ -472,8 +472,8 @@ int dasd_eer_enable(struct dasd_device *device) if (rc) goto out; - cqr = dasd_kmalloc_request(DASD_ECKD_MAGIC, 1 /* SNSS */, - SNSS_DATA_SIZE, device); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* SNSS */, + SNSS_DATA_SIZE, device, NULL); if (IS_ERR(cqr)) { rc = -ENOMEM; cqr = NULL; @@ -505,7 +505,7 @@ out: spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); if (cqr) - dasd_kfree_request(cqr, device); + dasd_sfree_request(cqr, device); return rc; } @@ -528,7 +528,7 @@ void dasd_eer_disable(struct dasd_device *device) in_use = test_and_clear_bit(DASD_FLAG_EER_IN_USE, &device->flags); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); if (cqr && !in_use) - dasd_kfree_request(cqr, device); + dasd_sfree_request(cqr, device); } /* diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 55bcbbed1b1d..976b6bd4fb05 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -706,11 +706,8 @@ extern const struct block_device_operations dasd_device_operations; extern struct kmem_cache *dasd_page_cache; -struct dasd_ccw_req * -dasd_kmalloc_request(int , int, int, struct dasd_device *); struct dasd_ccw_req * dasd_smalloc_request(int, int, int, struct dasd_device *, struct dasd_ccw_req *); -void dasd_kfree_request(struct dasd_ccw_req *, struct dasd_device *); void dasd_sfree_request(struct dasd_ccw_req *, struct dasd_device *); void dasd_wakeup_cb(struct dasd_ccw_req *, void *); -- cgit v1.2.3 From 1eb9364ce81d9445ad6f9d44921a91d2a6597156 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Tue, 12 Jun 2018 09:40:23 -0600 Subject: IB/uverbs: Fix ordering of ucontext check in ib_uverbs_write During disassociation the ucontext will become NULL, however due to how the SRCU locking works the ucontext must only be examined after looking at the ib_dev, which governs the RCU control flow. With the wrong ordering userspace will see EINVAL instead of EIO for a disassociated uverbs FD, which breaks rdma-core. Cc: stable@vger.kernel.org Fixes: 491d5c6a3023 ("RDMA/uverbs: Move uncontext check before SRCU read lock") Reported-by: Mark Bloch Signed-off-by: Jason Gunthorpe Reviewed-by: Leon Romanovsky --- drivers/infiniband/core/uverbs_main.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index 3ae2339dd27a..2094d136513d 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -736,10 +736,6 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf, if (ret) return ret; - if (!file->ucontext && - (command != IB_USER_VERBS_CMD_GET_CONTEXT || extended)) - return -EINVAL; - if (extended) { if (count < (sizeof(hdr) + sizeof(ex_hdr))) return -EINVAL; @@ -759,6 +755,16 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf, goto out; } + /* + * Must be after the ib_dev check, as once the RCU clears ib_dev == + * NULL means ucontext == NULL + */ + if (!file->ucontext && + (command != IB_USER_VERBS_CMD_GET_CONTEXT || extended)) { + ret = -EINVAL; + goto out; + } + if (!verify_command_mask(ib_dev, command, extended)) { ret = -EOPNOTSUPP; goto out; -- cgit v1.2.3 From a26ed66c20f080c510fcf5bd448bce204f2c19d7 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 10 Jun 2018 16:24:15 +0200 Subject: clocksource/drivers/stm32: Fix error return code Return an error code on failure. Problem found using Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Thomas Gleixner Cc: Daniel Lezcano Cc: kernel-janitors@vger.kernel.org Cc: Maxime Coquelin Cc: Alexandre Torgue Cc: linux-arm-kernel@lists.infradead.org Link: https://lkml.kernel.org/r1528640655-18948-3-git-send-email-Julia.Lawall@lip6.fr --- drivers/clocksource/timer-stm32.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c index e5cdc3af684c..2717f88c7904 100644 --- a/drivers/clocksource/timer-stm32.c +++ b/drivers/clocksource/timer-stm32.c @@ -304,8 +304,10 @@ static int __init stm32_timer_init(struct device_node *node) to->private_data = kzalloc(sizeof(struct stm32_timer_private), GFP_KERNEL); - if (!to->private_data) + if (!to->private_data) { + ret = -ENOMEM; goto deinit; + } rstc = of_reset_control_get(node, NULL); if (!IS_ERR(rstc)) { -- cgit v1.2.3 From 8efaac07d7e6694f39521f9fb8a5c848b712ecee Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Fri, 8 Jun 2018 08:04:57 +0200 Subject: drm/bridge/sii8620: simplify hardware reset procedure There is no need to flip reset pin twice. Also delays can be changed to values present in vendor's code. Signed-off-by: Andrzej Hajda Tested-by: Marek Szyprowski Reviewed-by: Maciej Purski Link: https://patchwork.freedesktop.org/patch/msgid/20180608060457.18357-1-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 7ab36042a822..d1e780fba4b6 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -971,8 +971,17 @@ static int sii8620_hw_on(struct sii8620 *ctx) ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); if (ret) return ret; + usleep_range(10000, 20000); - return clk_prepare_enable(ctx->clk_xtal); + ret = clk_prepare_enable(ctx->clk_xtal); + if (ret) + return ret; + + msleep(100); + gpiod_set_value(ctx->gpio_reset, 0); + msleep(100); + + return 0; } static int sii8620_hw_off(struct sii8620 *ctx) @@ -982,17 +991,6 @@ static int sii8620_hw_off(struct sii8620 *ctx) return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); } -static void sii8620_hw_reset(struct sii8620 *ctx) -{ - usleep_range(10000, 20000); - gpiod_set_value(ctx->gpio_reset, 0); - usleep_range(5000, 20000); - gpiod_set_value(ctx->gpio_reset, 1); - usleep_range(10000, 20000); - gpiod_set_value(ctx->gpio_reset, 0); - msleep(300); -} - static void sii8620_cbus_reset(struct sii8620 *ctx) { sii8620_write(ctx, REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST @@ -2112,7 +2110,6 @@ static void sii8620_cable_in(struct sii8620 *ctx) dev_err(dev, "Error powering on, %d.\n", ret); return; } - sii8620_hw_reset(ctx); sii8620_read_buf(ctx, REG_VND_IDL, ver, ARRAY_SIZE(ver)); ret = sii8620_clear_error(ctx); -- cgit v1.2.3 From 8e627a1b1ce8feb3e1da4428b71b9b4905f04888 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Mon, 15 Jan 2018 18:33:57 +0100 Subject: drm/bridge/sii8620: fix loops in EDID fetch logic Function should constantly check if cable is connected and finish in finite time. Signed-off-by: Andrzej Hajda Tested-by: Marek Szyprowski Reviewed-by: Maciej Purski Link: https://patchwork.freedesktop.org/patch/msgid/20180115173357.31067-4-a.hajda@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index d1e780fba4b6..720bc7c325e0 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -807,6 +807,7 @@ static void sii8620_burst_rx_all(struct sii8620 *ctx) static void sii8620_fetch_edid(struct sii8620 *ctx) { u8 lm_ddc, ddc_cmd, int3, cbus; + unsigned long timeout; int fetched, i; int edid_len = EDID_LENGTH; u8 *edid; @@ -856,23 +857,31 @@ static void sii8620_fetch_edid(struct sii8620 *ctx) REG_DDC_CMD, ddc_cmd | VAL_DDC_CMD_ENH_DDC_READ_NO_ACK ); - do { - int3 = sii8620_readb(ctx, REG_INTR3); + int3 = 0; + timeout = jiffies + msecs_to_jiffies(200); + for (;;) { cbus = sii8620_readb(ctx, REG_CBUS_STATUS); - - if (int3 & BIT_DDC_CMD_DONE) - break; - - if (!(cbus & BIT_CBUS_STATUS_CBUS_CONNECTED)) { + if (~cbus & BIT_CBUS_STATUS_CBUS_CONNECTED) { + kfree(edid); + edid = NULL; + goto end; + } + if (int3 & BIT_DDC_CMD_DONE) { + if (sii8620_readb(ctx, REG_DDC_DOUT_CNT) + >= FETCH_SIZE) + break; + } else { + int3 = sii8620_readb(ctx, REG_INTR3); + } + if (time_is_before_jiffies(timeout)) { + ctx->error = -ETIMEDOUT; + dev_err(ctx->dev, "timeout during EDID read\n"); kfree(edid); edid = NULL; goto end; } - } while (1); - - sii8620_readb(ctx, REG_DDC_STATUS); - while (sii8620_readb(ctx, REG_DDC_DOUT_CNT) < FETCH_SIZE) usleep_range(10, 20); + } sii8620_read_buf(ctx, REG_DDC_DATA, edid + fetched, FETCH_SIZE); if (fetched + FETCH_SIZE == EDID_LENGTH) { -- cgit v1.2.3 From ecba7cfa3afbe489288f2c819158b7402afd7ee9 Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Fri, 2 Feb 2018 11:54:25 +0100 Subject: drm/bridge/sii8620: fix display modes validation Current implementation of mode_valid() and mode_fixup() callbacks handle packed pixel modes improperly. Fix it by using proper maximum clock values from the documentation. Signed-off-by: Maciej Purski Signed-off-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/1517568865-25219-1-git-send-email-m.purski@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 80 ++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 720bc7c325e0..5267dc6551af 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -36,8 +36,11 @@ #define SII8620_BURST_BUF_LEN 288 #define VAL_RX_HDMI_CTRL2_DEFVAL VAL_RX_HDMI_CTRL2_IDLE_CNT(3) -#define MHL1_MAX_LCLK 225000 -#define MHL3_MAX_LCLK 600000 + +#define MHL1_MAX_PCLK 75000 +#define MHL1_MAX_PCLK_PP_MODE 150000 +#define MHL3_MAX_PCLK 200000 +#define MHL3_MAX_PCLK_PP_MODE 300000 enum sii8620_mode { CM_DISCONNECTED, @@ -2274,17 +2277,43 @@ static void sii8620_detach(struct drm_bridge *bridge) rc_unregister_device(ctx->rc_dev); } +static int sii8620_is_packing_required(struct sii8620 *ctx, + const struct drm_display_mode *mode) +{ + int max_pclk, max_pclk_pp_mode; + + if (sii8620_is_mhl3(ctx)) { + max_pclk = MHL3_MAX_PCLK; + max_pclk_pp_mode = MHL3_MAX_PCLK_PP_MODE; + } else { + max_pclk = MHL1_MAX_PCLK; + max_pclk_pp_mode = MHL1_MAX_PCLK_PP_MODE; + } + + if (mode->clock < max_pclk) + return 0; + else if (mode->clock < max_pclk_pp_mode) + return 1; + else + return -1; +} + static enum drm_mode_status sii8620_mode_valid(struct drm_bridge *bridge, const struct drm_display_mode *mode) { struct sii8620 *ctx = bridge_to_sii8620(bridge); + int pack_required = sii8620_is_packing_required(ctx, mode); bool can_pack = ctx->devcap[MHL_DCAP_VID_LINK_MODE] & MHL_DCAP_VID_LINK_PPIXEL; - unsigned int max_pclk = sii8620_is_mhl3(ctx) ? MHL3_MAX_LCLK : - MHL1_MAX_LCLK; - max_pclk /= can_pack ? 2 : 3; - return (mode->clock > max_pclk) ? MODE_CLOCK_HIGH : MODE_OK; + switch (pack_required) { + case 0: + return MODE_OK; + case 1: + return (can_pack) ? MODE_OK : MODE_CLOCK_HIGH; + default: + return MODE_CLOCK_HIGH; + } } static bool sii8620_mode_fixup(struct drm_bridge *bridge, @@ -2292,43 +2321,16 @@ static bool sii8620_mode_fixup(struct drm_bridge *bridge, struct drm_display_mode *adjusted_mode) { struct sii8620 *ctx = bridge_to_sii8620(bridge); - int max_lclk; - bool ret = true; mutex_lock(&ctx->lock); - max_lclk = sii8620_is_mhl3(ctx) ? MHL3_MAX_LCLK : MHL1_MAX_LCLK; - if (max_lclk > 3 * adjusted_mode->clock) { - ctx->use_packed_pixel = 0; - goto end; - } - if ((ctx->devcap[MHL_DCAP_VID_LINK_MODE] & MHL_DCAP_VID_LINK_PPIXEL) && - max_lclk > 2 * adjusted_mode->clock) { - ctx->use_packed_pixel = 1; - goto end; - } - ret = false; -end: - if (ret) { - u8 vic = drm_match_cea_mode(adjusted_mode); - - if (!vic) { - union hdmi_infoframe frm; - u8 mhl_vic[] = { 0, 95, 94, 93, 98 }; - - /* FIXME: We need the connector here */ - drm_hdmi_vendor_infoframe_from_display_mode( - &frm.vendor.hdmi, NULL, adjusted_mode); - vic = frm.vendor.hdmi.vic; - if (vic >= ARRAY_SIZE(mhl_vic)) - vic = 0; - vic = mhl_vic[vic]; - } - ctx->video_code = vic; - ctx->pixel_clock = adjusted_mode->clock; - } + ctx->use_packed_pixel = sii8620_is_packing_required(ctx, adjusted_mode); + ctx->video_code = drm_match_cea_mode(adjusted_mode); + ctx->pixel_clock = adjusted_mode->clock; + mutex_unlock(&ctx->lock); - return ret; + + return true; } static const struct drm_bridge_funcs sii8620_bridge_funcs = { -- cgit v1.2.3 From 9378cecb1ce5d618b8aff4d65113ddcf72fc1011 Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Wed, 22 Nov 2017 10:08:38 +0100 Subject: drm/bridge/sii8620: fix potential buffer overflow Buffer overflow error should not occur, as mode_fixup() callback filters pixel clock value and it should never exceed 600000. However, current implementation is not obviously safe and relies on implementation of mode_fixup(). Make 'i' variable never reach unsafe value in order to avoid buffer overflow error. Reported-by: Dan Carpenter Fixes: bf1722ca ("drm/bridge/sii8620: rewrite hdmi start sequence") Signed-off-by: Maciej Purski Signed-off-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/1511341718-6974-1-git-send-email-m.purski@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 5267dc6551af..61fd3e0a4ba6 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -1226,7 +1226,7 @@ static void sii8620_start_video(struct sii8620 *ctx) int clk = ctx->pixel_clock * (ctx->use_packed_pixel ? 2 : 3); int i; - for (i = 0; i < ARRAY_SIZE(clk_spec); ++i) + for (i = 0; i < ARRAY_SIZE(clk_spec) - 1; ++i) if (clk < clk_spec[i].max_clk) break; -- cgit v1.2.3 From bbc05e172fad9affa388be35b78b9e5e5da76648 Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Wed, 29 Nov 2017 12:48:50 +0100 Subject: drm/bridge/sii8620: start MHL transmission after HDMI signal detection The vendor code waits for infoframe to detect video mode set by source. We do not need to follow this pattern, because video mode information is provided by drm core. As a result most of the infoframe handling code can be removed. Start transmission immediately after detecting stream on HDMI lines in irq_scdt() function without waiting for infoframe interrupt. Signed-off-by: Maciej Purski Signed-off-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/1511956130-24482-1-git-send-email-m.purski@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 53 ++---------------------------------- 1 file changed, 2 insertions(+), 51 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 61fd3e0a4ba6..853c4f97c7c9 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -1941,14 +1941,6 @@ static void sii8620_irq_edid(struct sii8620 *ctx) ctx->mt_state = MT_STATE_DONE; } -static void sii8620_scdt_high(struct sii8620 *ctx) -{ - sii8620_write_seq_static(ctx, - REG_INTR8_MASK, BIT_CEA_NEW_AVI | BIT_CEA_NEW_VSI, - REG_TPI_SC, BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI, - ); -} - static void sii8620_irq_scdt(struct sii8620 *ctx) { u8 stat = sii8620_readb(ctx, REG_INTR5); @@ -1956,53 +1948,13 @@ static void sii8620_irq_scdt(struct sii8620 *ctx) if (stat & BIT_INTR_SCDT_CHANGE) { u8 cstat = sii8620_readb(ctx, REG_TMDS_CSTAT_P3); - if (cstat & BIT_TMDS_CSTAT_P3_SCDT) { - if (ctx->sink_type == SINK_HDMI) - /* enable infoframe interrupt */ - sii8620_scdt_high(ctx); - else - sii8620_start_video(ctx); - } + if (cstat & BIT_TMDS_CSTAT_P3_SCDT) + sii8620_start_video(ctx); } sii8620_write(ctx, REG_INTR5, stat); } -static void sii8620_new_vsi(struct sii8620 *ctx) -{ - u8 vsif[11]; - - sii8620_write(ctx, REG_RX_HDMI_CTRL2, - VAL_RX_HDMI_CTRL2_DEFVAL | - BIT_RX_HDMI_CTRL2_VSI_MON_SEL_VSI); - sii8620_read_buf(ctx, REG_RX_HDMI_MON_PKT_HEADER1, vsif, - ARRAY_SIZE(vsif)); -} - -static void sii8620_new_avi(struct sii8620 *ctx) -{ - sii8620_write(ctx, REG_RX_HDMI_CTRL2, VAL_RX_HDMI_CTRL2_DEFVAL); - sii8620_read_buf(ctx, REG_RX_HDMI_MON_PKT_HEADER1, ctx->avif, - ARRAY_SIZE(ctx->avif)); -} - -static void sii8620_irq_infr(struct sii8620 *ctx) -{ - u8 stat = sii8620_readb(ctx, REG_INTR8) - & (BIT_CEA_NEW_VSI | BIT_CEA_NEW_AVI); - - sii8620_write(ctx, REG_INTR8, stat); - - if (stat & BIT_CEA_NEW_VSI) - sii8620_new_vsi(ctx); - - if (stat & BIT_CEA_NEW_AVI) - sii8620_new_avi(ctx); - - if (stat & (BIT_CEA_NEW_VSI | BIT_CEA_NEW_AVI)) - sii8620_start_video(ctx); -} - static void sii8620_got_xdevcap(struct sii8620 *ctx, int ret) { if (ret < 0) @@ -2084,7 +2036,6 @@ static irqreturn_t sii8620_irq_thread(int irq, void *data) { BIT_FAST_INTR_STAT_EDID, sii8620_irq_edid }, { BIT_FAST_INTR_STAT_DDC, sii8620_irq_ddc }, { BIT_FAST_INTR_STAT_SCDT, sii8620_irq_scdt }, - { BIT_FAST_INTR_STAT_INFR, sii8620_irq_infr }, }; struct sii8620 *ctx = data; u8 stats[LEN_FAST_INTR_STAT]; -- cgit v1.2.3 From 95e8522588c81fdef1b10777a874c4a1fe14bf88 Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Wed, 29 Nov 2017 16:12:47 +0100 Subject: drm/bridge/sii8620: remove HSIC initialization HSIC initialization was taken from the vendor code. HSIC in MHL circuit is not connected, so it is not possible to test it. Tests prove that without HSIC the device works well. Therefore it can be removed. Signed-off-by: Maciej Purski Signed-off-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/1511968368-30884-1-git-send-email-m.purski@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 38 ------------------------------------ 1 file changed, 38 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 853c4f97c7c9..ff041bec0cf4 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -511,50 +511,12 @@ static void sii8620_sink_detected(struct sii8620 *ctx, int ret) sink_str[ctx->sink_type], sink_name); } -static void sii8620_hsic_init(struct sii8620 *ctx) -{ - if (!sii8620_is_mhl3(ctx)) - return; - - sii8620_write(ctx, REG_FCGC, - BIT_FCGC_HSIC_HOSTMODE | BIT_FCGC_HSIC_ENABLE); - sii8620_setbits(ctx, REG_HRXCTRL3, - BIT_HRXCTRL3_HRX_STAY_RESET | BIT_HRXCTRL3_STATUS_EN, ~0); - sii8620_setbits(ctx, REG_TTXNUMB, MSK_TTXNUMB_TTX_NUMBPS, 4); - sii8620_setbits(ctx, REG_TRXCTRL, BIT_TRXCTRL_TRX_FROM_SE_COC, ~0); - sii8620_setbits(ctx, REG_HTXCTRL, BIT_HTXCTRL_HTX_DRVCONN1, 0); - sii8620_setbits(ctx, REG_KEEPER, MSK_KEEPER_MODE, VAL_KEEPER_MODE_HOST); - sii8620_write_seq_static(ctx, - REG_TDMLLCTL, 0, - REG_UTSRST, BIT_UTSRST_HRX_SRST | BIT_UTSRST_HTX_SRST | - BIT_UTSRST_KEEPER_SRST | BIT_UTSRST_FC_SRST, - REG_UTSRST, BIT_UTSRST_HRX_SRST | BIT_UTSRST_HTX_SRST, - REG_HRXINTL, 0xff, - REG_HRXINTH, 0xff, - REG_TTXINTL, 0xff, - REG_TTXINTH, 0xff, - REG_TRXINTL, 0xff, - REG_TRXINTH, 0xff, - REG_HTXINTL, 0xff, - REG_HTXINTH, 0xff, - REG_FCINTR0, 0xff, - REG_FCINTR1, 0xff, - REG_FCINTR2, 0xff, - REG_FCINTR3, 0xff, - REG_FCINTR4, 0xff, - REG_FCINTR5, 0xff, - REG_FCINTR6, 0xff, - REG_FCINTR7, 0xff - ); -} - static void sii8620_edid_read(struct sii8620 *ctx, int ret) { if (ret < 0) return; sii8620_set_upstream_edid(ctx); - sii8620_hsic_init(ctx); sii8620_enable_hpd(ctx); } -- cgit v1.2.3 From c7d6d511eb56495f065600c90336da0f0fe1b174 Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Tue, 23 Jan 2018 12:13:16 +0100 Subject: drm/bridge/sii8620: fix HDMI cable connection to dongle MHL bridge is usually connected to TV via MHL dongle. Currently plugging HDMI cable to dongle is handled improperly. Fix it by splitting connecting of a dongle and a HDMI cable. The driver should now handle unplugging a sink from a dongle and plugging a different sink with new edid. Tested on MHL1, MHL2 and MHL3 using various vendors' dongles both in DVI and HDMI mode. Signed-off-by: Maciej Purski Signed-off-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/1516705996-8928-1-git-send-email-m.purski@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 65 +++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index ff041bec0cf4..4a3deeda065c 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -83,6 +83,9 @@ struct sii8620 { u8 devcap[MHL_DCAP_SIZE]; u8 xdevcap[MHL_XDC_SIZE]; u8 avif[HDMI_INFOFRAME_SIZE(AVI)]; + bool feature_complete; + bool devcap_read; + bool sink_detected; struct edid *edid; unsigned int gen2_write_burst:1; enum sii8620_mt_state mt_state; @@ -479,7 +482,7 @@ static void sii8620_update_array(u8 *dst, u8 *src, int count) } } -static void sii8620_sink_detected(struct sii8620 *ctx, int ret) +static void sii8620_identify_sink(struct sii8620 *ctx) { static const char * const sink_str[] = { [SINK_NONE] = "NONE", @@ -490,7 +493,7 @@ static void sii8620_sink_detected(struct sii8620 *ctx, int ret) char sink_name[20]; struct device *dev = ctx->dev; - if (ret < 0) + if (!ctx->sink_detected || !ctx->devcap_read) return; sii8620_fetch_edid(ctx); @@ -499,6 +502,7 @@ static void sii8620_sink_detected(struct sii8620 *ctx, int ret) sii8620_mhl_disconnected(ctx); return; } + sii8620_set_upstream_edid(ctx); if (drm_detect_hdmi_monitor(ctx->edid)) ctx->sink_type = SINK_HDMI; @@ -511,15 +515,6 @@ static void sii8620_sink_detected(struct sii8620 *ctx, int ret) sink_str[ctx->sink_type], sink_name); } -static void sii8620_edid_read(struct sii8620 *ctx, int ret) -{ - if (ret < 0) - return; - - sii8620_set_upstream_edid(ctx); - sii8620_enable_hpd(ctx); -} - static void sii8620_mr_devcap(struct sii8620 *ctx) { u8 dcap[MHL_DCAP_SIZE]; @@ -535,6 +530,8 @@ static void sii8620_mr_devcap(struct sii8620 *ctx) dcap[MHL_DCAP_ADOPTER_ID_H], dcap[MHL_DCAP_ADOPTER_ID_L], dcap[MHL_DCAP_DEVICE_ID_H], dcap[MHL_DCAP_DEVICE_ID_L]); sii8620_update_array(ctx->devcap, dcap, MHL_DCAP_SIZE); + ctx->devcap_read = true; + sii8620_identify_sink(ctx); } static void sii8620_mr_xdevcap(struct sii8620 *ctx) @@ -1506,6 +1503,16 @@ static void sii8620_set_mode(struct sii8620 *ctx, enum sii8620_mode mode) ); } +static void sii8620_hpd_unplugged(struct sii8620 *ctx) +{ + sii8620_disable_hpd(ctx); + ctx->sink_type = SINK_NONE; + ctx->sink_detected = false; + ctx->feature_complete = false; + kfree(ctx->edid); + ctx->edid = NULL; +} + static void sii8620_disconnect(struct sii8620 *ctx) { sii8620_disable_gen2_write_burst(ctx); @@ -1533,7 +1540,7 @@ static void sii8620_disconnect(struct sii8620 *ctx) REG_MHL_DP_CTL6, 0x2A, REG_MHL_DP_CTL7, 0x03 ); - sii8620_disable_hpd(ctx); + sii8620_hpd_unplugged(ctx); sii8620_write_seq_static(ctx, REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE, REG_MHL_COC_CTL1, 0x07, @@ -1581,10 +1588,8 @@ static void sii8620_disconnect(struct sii8620 *ctx) memset(ctx->xstat, 0, sizeof(ctx->xstat)); memset(ctx->devcap, 0, sizeof(ctx->devcap)); memset(ctx->xdevcap, 0, sizeof(ctx->xdevcap)); + ctx->devcap_read = false; ctx->cbus_status = 0; - ctx->sink_type = SINK_NONE; - kfree(ctx->edid); - ctx->edid = NULL; sii8620_mt_cleanup(ctx); } @@ -1675,9 +1680,6 @@ static void sii8620_status_changed_path(struct sii8620 *ctx) sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), MHL_DST_LM_CLK_MODE_NORMAL | MHL_DST_LM_PATH_ENABLED); - if (!sii8620_is_mhl3(ctx)) - sii8620_mt_read_devcap(ctx, false); - sii8620_mt_set_cont(ctx, sii8620_sink_detected); } else { sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), MHL_DST_LM_CLK_MODE_NORMAL); @@ -1694,9 +1696,14 @@ static void sii8620_msc_mr_write_stat(struct sii8620 *ctx) sii8620_update_array(ctx->stat, st, MHL_DST_SIZE); sii8620_update_array(ctx->xstat, xst, MHL_XDS_SIZE); - if (ctx->stat[MHL_DST_CONNECTED_RDY] & MHL_DST_CONN_DCAP_RDY) + if (ctx->stat[MHL_DST_CONNECTED_RDY] & st[MHL_DST_CONNECTED_RDY] & + MHL_DST_CONN_DCAP_RDY) { sii8620_status_dcap_ready(ctx); + if (!sii8620_is_mhl3(ctx)) + sii8620_mt_read_devcap(ctx, false); + } + if (st[MHL_DST_LINK_MODE] & MHL_DST_LM_PATH_ENABLED) sii8620_status_changed_path(ctx); } @@ -1780,8 +1787,11 @@ static void sii8620_msc_mr_set_int(struct sii8620 *ctx) } if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_FEAT_REQ) sii8620_send_features(ctx); - if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_FEAT_COMPLETE) - sii8620_edid_read(ctx, 0); + if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_FEAT_COMPLETE) { + ctx->feature_complete = true; + if (ctx->edid) + sii8620_enable_hpd(ctx); + } } static struct sii8620_mt_msg *sii8620_msc_msg_first(struct sii8620 *ctx) @@ -1856,6 +1866,15 @@ static void sii8620_irq_msc(struct sii8620 *ctx) if (stat & BIT_CBUS_MSC_MR_WRITE_STAT) sii8620_msc_mr_write_stat(ctx); + if (stat & BIT_CBUS_HPD_CHG) { + if (ctx->cbus_status & BIT_CBUS_STATUS_CBUS_HPD) { + ctx->sink_detected = true; + sii8620_identify_sink(ctx); + } else { + sii8620_hpd_unplugged(ctx); + } + } + if (stat & BIT_CBUS_MSC_MR_SET_INT) sii8620_msc_mr_set_int(ctx); @@ -1967,11 +1986,11 @@ static void sii8620_irq_ddc(struct sii8620 *ctx) if (stat & BIT_DDC_CMD_DONE) { sii8620_write(ctx, REG_INTR3_MASK, 0); - if (sii8620_is_mhl3(ctx)) + if (sii8620_is_mhl3(ctx) && !ctx->feature_complete) sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE), MHL_INT_RC_FEAT_REQ); else - sii8620_edid_read(ctx, 0); + sii8620_enable_hpd(ctx); } sii8620_write(ctx, REG_INTR3, stat); } -- cgit v1.2.3 From cf65a0f6f6ff7631ba0ac0513a14ca5b65320d80 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 12 Jun 2018 19:01:45 +0200 Subject: dma-mapping: move all DMA mapping code to kernel/dma Currently the code is split over various files with dma- prefixes in the lib/ and drives/base directories, and the number of files keeps growing. Move them into a single directory to keep the code together and remove the file name prefixes. To match the irq infrastructure this directory is placed under the kernel/ directory. Signed-off-by: Christoph Hellwig --- Documentation/driver-api/infrastructure.rst | 4 +- MAINTAINERS | 9 +- drivers/base/Makefile | 3 - drivers/base/dma-coherent.c | 434 ------- drivers/base/dma-contiguous.c | 278 ----- drivers/base/dma-mapping.c | 345 ------ include/linux/dma-contiguous.h | 2 +- init/Kconfig | 4 - kernel/Makefile | 1 + kernel/dma/Kconfig | 50 + kernel/dma/Makefile | 11 + kernel/dma/coherent.c | 434 +++++++ kernel/dma/contiguous.c | 278 +++++ kernel/dma/debug.c | 1773 +++++++++++++++++++++++++++ kernel/dma/direct.c | 204 +++ kernel/dma/mapping.c | 345 ++++++ kernel/dma/noncoherent.c | 102 ++ kernel/dma/swiotlb.c | 1087 ++++++++++++++++ kernel/dma/virt.c | 59 + lib/Kconfig | 47 +- lib/Makefile | 6 - lib/dma-debug.c | 1773 --------------------------- lib/dma-direct.c | 204 --- lib/dma-noncoherent.c | 102 -- lib/dma-virt.c | 61 - lib/swiotlb.c | 1087 ---------------- 26 files changed, 4350 insertions(+), 4353 deletions(-) delete mode 100644 drivers/base/dma-coherent.c delete mode 100644 drivers/base/dma-contiguous.c delete mode 100644 drivers/base/dma-mapping.c create mode 100644 kernel/dma/Kconfig create mode 100644 kernel/dma/Makefile create mode 100644 kernel/dma/coherent.c create mode 100644 kernel/dma/contiguous.c create mode 100644 kernel/dma/debug.c create mode 100644 kernel/dma/direct.c create mode 100644 kernel/dma/mapping.c create mode 100644 kernel/dma/noncoherent.c create mode 100644 kernel/dma/swiotlb.c create mode 100644 kernel/dma/virt.c delete mode 100644 lib/dma-debug.c delete mode 100644 lib/dma-direct.c delete mode 100644 lib/dma-noncoherent.c delete mode 100644 lib/dma-virt.c delete mode 100644 lib/swiotlb.c (limited to 'drivers') diff --git a/Documentation/driver-api/infrastructure.rst b/Documentation/driver-api/infrastructure.rst index bee1b9a1702f..6172f3cc3d0b 100644 --- a/Documentation/driver-api/infrastructure.rst +++ b/Documentation/driver-api/infrastructure.rst @@ -49,10 +49,10 @@ Device Drivers Base Device Drivers DMA Management ----------------------------- -.. kernel-doc:: drivers/base/dma-coherent.c +.. kernel-doc:: kernel/dma/coherent.c :export: -.. kernel-doc:: drivers/base/dma-mapping.c +.. kernel-doc:: kernel/dma/mapping.c :export: Device drivers PnP support diff --git a/MAINTAINERS b/MAINTAINERS index c13b9fb3be0b..a6844a9e2f64 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4359,12 +4359,7 @@ L: iommu@lists.linux-foundation.org T: git git://git.infradead.org/users/hch/dma-mapping.git W: http://git.infradead.org/users/hch/dma-mapping.git S: Supported -F: lib/dma-debug.c -F: lib/dma-direct.c -F: lib/dma-noncoherent.c -F: lib/dma-virt.c -F: drivers/base/dma-mapping.c -F: drivers/base/dma-coherent.c +F: kernel/dma/ F: include/asm-generic/dma-mapping.h F: include/linux/dma-direct.h F: include/linux/dma-mapping.h @@ -13642,7 +13637,7 @@ M: Konrad Rzeszutek Wilk L: iommu@lists.linux-foundation.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/konrad/swiotlb.git S: Supported -F: lib/swiotlb.c +F: kernel/dma/swiotlb.c F: arch/*/kernel/pci-swiotlb.c F: include/linux/swiotlb.h diff --git a/drivers/base/Makefile b/drivers/base/Makefile index b074f242a435..704f44295810 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -8,10 +8,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \ topology.o container.o property.o cacheinfo.o \ devcon.o obj-$(CONFIG_DEVTMPFS) += devtmpfs.o -obj-$(CONFIG_DMA_CMA) += dma-contiguous.o obj-y += power/ -obj-$(CONFIG_HAS_DMA) += dma-mapping.o -obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o obj-$(CONFIG_ISA_BUS_API) += isa.o obj-y += firmware_loader/ obj-$(CONFIG_NUMA) += node.o diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c deleted file mode 100644 index 597d40893862..000000000000 --- a/drivers/base/dma-coherent.c +++ /dev/null @@ -1,434 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Coherent per-device memory handling. - * Borrowed from i386 - */ -#include -#include -#include -#include -#include - -struct dma_coherent_mem { - void *virt_base; - dma_addr_t device_base; - unsigned long pfn_base; - int size; - int flags; - unsigned long *bitmap; - spinlock_t spinlock; - bool use_dev_dma_pfn_offset; -}; - -static struct dma_coherent_mem *dma_coherent_default_memory __ro_after_init; - -static inline struct dma_coherent_mem *dev_get_coherent_memory(struct device *dev) -{ - if (dev && dev->dma_mem) - return dev->dma_mem; - return NULL; -} - -static inline dma_addr_t dma_get_device_base(struct device *dev, - struct dma_coherent_mem * mem) -{ - if (mem->use_dev_dma_pfn_offset) - return (mem->pfn_base - dev->dma_pfn_offset) << PAGE_SHIFT; - else - return mem->device_base; -} - -static int dma_init_coherent_memory( - phys_addr_t phys_addr, dma_addr_t device_addr, size_t size, int flags, - struct dma_coherent_mem **mem) -{ - struct dma_coherent_mem *dma_mem = NULL; - void __iomem *mem_base = NULL; - int pages = size >> PAGE_SHIFT; - int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long); - int ret; - - if (!size) { - ret = -EINVAL; - goto out; - } - - mem_base = memremap(phys_addr, size, MEMREMAP_WC); - if (!mem_base) { - ret = -EINVAL; - goto out; - } - dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL); - if (!dma_mem) { - ret = -ENOMEM; - goto out; - } - dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL); - if (!dma_mem->bitmap) { - ret = -ENOMEM; - goto out; - } - - dma_mem->virt_base = mem_base; - dma_mem->device_base = device_addr; - dma_mem->pfn_base = PFN_DOWN(phys_addr); - dma_mem->size = pages; - dma_mem->flags = flags; - spin_lock_init(&dma_mem->spinlock); - - *mem = dma_mem; - return 0; - -out: - kfree(dma_mem); - if (mem_base) - memunmap(mem_base); - return ret; -} - -static void dma_release_coherent_memory(struct dma_coherent_mem *mem) -{ - if (!mem) - return; - - memunmap(mem->virt_base); - kfree(mem->bitmap); - kfree(mem); -} - -static int dma_assign_coherent_memory(struct device *dev, - struct dma_coherent_mem *mem) -{ - if (!dev) - return -ENODEV; - - if (dev->dma_mem) - return -EBUSY; - - dev->dma_mem = mem; - return 0; -} - -int dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, - dma_addr_t device_addr, size_t size, int flags) -{ - struct dma_coherent_mem *mem; - int ret; - - ret = dma_init_coherent_memory(phys_addr, device_addr, size, flags, &mem); - if (ret) - return ret; - - ret = dma_assign_coherent_memory(dev, mem); - if (ret) - dma_release_coherent_memory(mem); - return ret; -} -EXPORT_SYMBOL(dma_declare_coherent_memory); - -void dma_release_declared_memory(struct device *dev) -{ - struct dma_coherent_mem *mem = dev->dma_mem; - - if (!mem) - return; - dma_release_coherent_memory(mem); - dev->dma_mem = NULL; -} -EXPORT_SYMBOL(dma_release_declared_memory); - -void *dma_mark_declared_memory_occupied(struct device *dev, - dma_addr_t device_addr, size_t size) -{ - struct dma_coherent_mem *mem = dev->dma_mem; - unsigned long flags; - int pos, err; - - size += device_addr & ~PAGE_MASK; - - if (!mem) - return ERR_PTR(-EINVAL); - - spin_lock_irqsave(&mem->spinlock, flags); - pos = PFN_DOWN(device_addr - dma_get_device_base(dev, mem)); - err = bitmap_allocate_region(mem->bitmap, pos, get_order(size)); - spin_unlock_irqrestore(&mem->spinlock, flags); - - if (err != 0) - return ERR_PTR(err); - return mem->virt_base + (pos << PAGE_SHIFT); -} -EXPORT_SYMBOL(dma_mark_declared_memory_occupied); - -static void *__dma_alloc_from_coherent(struct dma_coherent_mem *mem, - ssize_t size, dma_addr_t *dma_handle) -{ - int order = get_order(size); - unsigned long flags; - int pageno; - void *ret; - - spin_lock_irqsave(&mem->spinlock, flags); - - if (unlikely(size > (mem->size << PAGE_SHIFT))) - goto err; - - pageno = bitmap_find_free_region(mem->bitmap, mem->size, order); - if (unlikely(pageno < 0)) - goto err; - - /* - * Memory was found in the coherent area. - */ - *dma_handle = mem->device_base + (pageno << PAGE_SHIFT); - ret = mem->virt_base + (pageno << PAGE_SHIFT); - spin_unlock_irqrestore(&mem->spinlock, flags); - memset(ret, 0, size); - return ret; -err: - spin_unlock_irqrestore(&mem->spinlock, flags); - return NULL; -} - -/** - * dma_alloc_from_dev_coherent() - allocate memory from device coherent pool - * @dev: device from which we allocate memory - * @size: size of requested memory area - * @dma_handle: This will be filled with the correct dma handle - * @ret: This pointer will be filled with the virtual address - * to allocated area. - * - * This function should be only called from per-arch dma_alloc_coherent() - * to support allocation from per-device coherent memory pools. - * - * Returns 0 if dma_alloc_coherent should continue with allocating from - * generic memory areas, or !0 if dma_alloc_coherent should return @ret. - */ -int dma_alloc_from_dev_coherent(struct device *dev, ssize_t size, - dma_addr_t *dma_handle, void **ret) -{ - struct dma_coherent_mem *mem = dev_get_coherent_memory(dev); - - if (!mem) - return 0; - - *ret = __dma_alloc_from_coherent(mem, size, dma_handle); - if (*ret) - return 1; - - /* - * In the case where the allocation can not be satisfied from the - * per-device area, try to fall back to generic memory if the - * constraints allow it. - */ - return mem->flags & DMA_MEMORY_EXCLUSIVE; -} -EXPORT_SYMBOL(dma_alloc_from_dev_coherent); - -void *dma_alloc_from_global_coherent(ssize_t size, dma_addr_t *dma_handle) -{ - if (!dma_coherent_default_memory) - return NULL; - - return __dma_alloc_from_coherent(dma_coherent_default_memory, size, - dma_handle); -} - -static int __dma_release_from_coherent(struct dma_coherent_mem *mem, - int order, void *vaddr) -{ - if (mem && vaddr >= mem->virt_base && vaddr < - (mem->virt_base + (mem->size << PAGE_SHIFT))) { - int page = (vaddr - mem->virt_base) >> PAGE_SHIFT; - unsigned long flags; - - spin_lock_irqsave(&mem->spinlock, flags); - bitmap_release_region(mem->bitmap, page, order); - spin_unlock_irqrestore(&mem->spinlock, flags); - return 1; - } - return 0; -} - -/** - * dma_release_from_dev_coherent() - free memory to device coherent memory pool - * @dev: device from which the memory was allocated - * @order: the order of pages allocated - * @vaddr: virtual address of allocated pages - * - * This checks whether the memory was allocated from the per-device - * coherent memory pool and if so, releases that memory. - * - * Returns 1 if we correctly released the memory, or 0 if the caller should - * proceed with releasing memory from generic pools. - */ -int dma_release_from_dev_coherent(struct device *dev, int order, void *vaddr) -{ - struct dma_coherent_mem *mem = dev_get_coherent_memory(dev); - - return __dma_release_from_coherent(mem, order, vaddr); -} -EXPORT_SYMBOL(dma_release_from_dev_coherent); - -int dma_release_from_global_coherent(int order, void *vaddr) -{ - if (!dma_coherent_default_memory) - return 0; - - return __dma_release_from_coherent(dma_coherent_default_memory, order, - vaddr); -} - -static int __dma_mmap_from_coherent(struct dma_coherent_mem *mem, - struct vm_area_struct *vma, void *vaddr, size_t size, int *ret) -{ - if (mem && vaddr >= mem->virt_base && vaddr + size <= - (mem->virt_base + (mem->size << PAGE_SHIFT))) { - unsigned long off = vma->vm_pgoff; - int start = (vaddr - mem->virt_base) >> PAGE_SHIFT; - int user_count = vma_pages(vma); - int count = PAGE_ALIGN(size) >> PAGE_SHIFT; - - *ret = -ENXIO; - if (off < count && user_count <= count - off) { - unsigned long pfn = mem->pfn_base + start + off; - *ret = remap_pfn_range(vma, vma->vm_start, pfn, - user_count << PAGE_SHIFT, - vma->vm_page_prot); - } - return 1; - } - return 0; -} - -/** - * dma_mmap_from_dev_coherent() - mmap memory from the device coherent pool - * @dev: device from which the memory was allocated - * @vma: vm_area for the userspace memory - * @vaddr: cpu address returned by dma_alloc_from_dev_coherent - * @size: size of the memory buffer allocated - * @ret: result from remap_pfn_range() - * - * This checks whether the memory was allocated from the per-device - * coherent memory pool and if so, maps that memory to the provided vma. - * - * Returns 1 if @vaddr belongs to the device coherent pool and the caller - * should return @ret, or 0 if they should proceed with mapping memory from - * generic areas. - */ -int dma_mmap_from_dev_coherent(struct device *dev, struct vm_area_struct *vma, - void *vaddr, size_t size, int *ret) -{ - struct dma_coherent_mem *mem = dev_get_coherent_memory(dev); - - return __dma_mmap_from_coherent(mem, vma, vaddr, size, ret); -} -EXPORT_SYMBOL(dma_mmap_from_dev_coherent); - -int dma_mmap_from_global_coherent(struct vm_area_struct *vma, void *vaddr, - size_t size, int *ret) -{ - if (!dma_coherent_default_memory) - return 0; - - return __dma_mmap_from_coherent(dma_coherent_default_memory, vma, - vaddr, size, ret); -} - -/* - * Support for reserved memory regions defined in device tree - */ -#ifdef CONFIG_OF_RESERVED_MEM -#include -#include -#include - -static struct reserved_mem *dma_reserved_default_memory __initdata; - -static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev) -{ - struct dma_coherent_mem *mem = rmem->priv; - int ret; - - if (!mem) { - ret = dma_init_coherent_memory(rmem->base, rmem->base, - rmem->size, - DMA_MEMORY_EXCLUSIVE, &mem); - if (ret) { - pr_err("Reserved memory: failed to init DMA memory pool at %pa, size %ld MiB\n", - &rmem->base, (unsigned long)rmem->size / SZ_1M); - return ret; - } - } - mem->use_dev_dma_pfn_offset = true; - rmem->priv = mem; - dma_assign_coherent_memory(dev, mem); - return 0; -} - -static void rmem_dma_device_release(struct reserved_mem *rmem, - struct device *dev) -{ - if (dev) - dev->dma_mem = NULL; -} - -static const struct reserved_mem_ops rmem_dma_ops = { - .device_init = rmem_dma_device_init, - .device_release = rmem_dma_device_release, -}; - -static int __init rmem_dma_setup(struct reserved_mem *rmem) -{ - unsigned long node = rmem->fdt_node; - - if (of_get_flat_dt_prop(node, "reusable", NULL)) - return -EINVAL; - -#ifdef CONFIG_ARM - if (!of_get_flat_dt_prop(node, "no-map", NULL)) { - pr_err("Reserved memory: regions without no-map are not yet supported\n"); - return -EINVAL; - } - - if (of_get_flat_dt_prop(node, "linux,dma-default", NULL)) { - WARN(dma_reserved_default_memory, - "Reserved memory: region for default DMA coherent area is redefined\n"); - dma_reserved_default_memory = rmem; - } -#endif - - rmem->ops = &rmem_dma_ops; - pr_info("Reserved memory: created DMA memory pool at %pa, size %ld MiB\n", - &rmem->base, (unsigned long)rmem->size / SZ_1M); - return 0; -} - -static int __init dma_init_reserved_memory(void) -{ - const struct reserved_mem_ops *ops; - int ret; - - if (!dma_reserved_default_memory) - return -ENOMEM; - - ops = dma_reserved_default_memory->ops; - - /* - * We rely on rmem_dma_device_init() does not propagate error of - * dma_assign_coherent_memory() for "NULL" device. - */ - ret = ops->device_init(dma_reserved_default_memory, NULL); - - if (!ret) { - dma_coherent_default_memory = dma_reserved_default_memory->priv; - pr_info("DMA: default coherent area is set\n"); - } - - return ret; -} - -core_initcall(dma_init_reserved_memory); - -RESERVEDMEM_OF_DECLARE(dma, "shared-dma-pool", rmem_dma_setup); -#endif diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c deleted file mode 100644 index d987dcd1bd56..000000000000 --- a/drivers/base/dma-contiguous.c +++ /dev/null @@ -1,278 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Contiguous Memory Allocator for DMA mapping framework - * Copyright (c) 2010-2011 by Samsung Electronics. - * Written by: - * Marek Szyprowski - * Michal Nazarewicz - */ - -#define pr_fmt(fmt) "cma: " fmt - -#ifdef CONFIG_CMA_DEBUG -#ifndef DEBUG -# define DEBUG -#endif -#endif - -#include -#include - -#include -#include -#include -#include -#include - -#ifdef CONFIG_CMA_SIZE_MBYTES -#define CMA_SIZE_MBYTES CONFIG_CMA_SIZE_MBYTES -#else -#define CMA_SIZE_MBYTES 0 -#endif - -struct cma *dma_contiguous_default_area; - -/* - * Default global CMA area size can be defined in kernel's .config. - * This is useful mainly for distro maintainers to create a kernel - * that works correctly for most supported systems. - * The size can be set in bytes or as a percentage of the total memory - * in the system. - * - * Users, who want to set the size of global CMA area for their system - * should use cma= kernel parameter. - */ -static const phys_addr_t size_bytes = (phys_addr_t)CMA_SIZE_MBYTES * SZ_1M; -static phys_addr_t size_cmdline = -1; -static phys_addr_t base_cmdline; -static phys_addr_t limit_cmdline; - -static int __init early_cma(char *p) -{ - pr_debug("%s(%s)\n", __func__, p); - size_cmdline = memparse(p, &p); - if (*p != '@') - return 0; - base_cmdline = memparse(p + 1, &p); - if (*p != '-') { - limit_cmdline = base_cmdline + size_cmdline; - return 0; - } - limit_cmdline = memparse(p + 1, &p); - - return 0; -} -early_param("cma", early_cma); - -#ifdef CONFIG_CMA_SIZE_PERCENTAGE - -static phys_addr_t __init __maybe_unused cma_early_percent_memory(void) -{ - struct memblock_region *reg; - unsigned long total_pages = 0; - - /* - * We cannot use memblock_phys_mem_size() here, because - * memblock_analyze() has not been called yet. - */ - for_each_memblock(memory, reg) - total_pages += memblock_region_memory_end_pfn(reg) - - memblock_region_memory_base_pfn(reg); - - return (total_pages * CONFIG_CMA_SIZE_PERCENTAGE / 100) << PAGE_SHIFT; -} - -#else - -static inline __maybe_unused phys_addr_t cma_early_percent_memory(void) -{ - return 0; -} - -#endif - -/** - * dma_contiguous_reserve() - reserve area(s) for contiguous memory handling - * @limit: End address of the reserved memory (optional, 0 for any). - * - * This function reserves memory from early allocator. It should be - * called by arch specific code once the early allocator (memblock or bootmem) - * has been activated and all other subsystems have already allocated/reserved - * memory. - */ -void __init dma_contiguous_reserve(phys_addr_t limit) -{ - phys_addr_t selected_size = 0; - phys_addr_t selected_base = 0; - phys_addr_t selected_limit = limit; - bool fixed = false; - - pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit); - - if (size_cmdline != -1) { - selected_size = size_cmdline; - selected_base = base_cmdline; - selected_limit = min_not_zero(limit_cmdline, limit); - if (base_cmdline + size_cmdline == limit_cmdline) - fixed = true; - } else { -#ifdef CONFIG_CMA_SIZE_SEL_MBYTES - selected_size = size_bytes; -#elif defined(CONFIG_CMA_SIZE_SEL_PERCENTAGE) - selected_size = cma_early_percent_memory(); -#elif defined(CONFIG_CMA_SIZE_SEL_MIN) - selected_size = min(size_bytes, cma_early_percent_memory()); -#elif defined(CONFIG_CMA_SIZE_SEL_MAX) - selected_size = max(size_bytes, cma_early_percent_memory()); -#endif - } - - if (selected_size && !dma_contiguous_default_area) { - pr_debug("%s: reserving %ld MiB for global area\n", __func__, - (unsigned long)selected_size / SZ_1M); - - dma_contiguous_reserve_area(selected_size, selected_base, - selected_limit, - &dma_contiguous_default_area, - fixed); - } -} - -/** - * dma_contiguous_reserve_area() - reserve custom contiguous area - * @size: Size of the reserved area (in bytes), - * @base: Base address of the reserved area optional, use 0 for any - * @limit: End address of the reserved memory (optional, 0 for any). - * @res_cma: Pointer to store the created cma region. - * @fixed: hint about where to place the reserved area - * - * This function reserves memory from early allocator. It should be - * called by arch specific code once the early allocator (memblock or bootmem) - * has been activated and all other subsystems have already allocated/reserved - * memory. This function allows to create custom reserved areas for specific - * devices. - * - * If @fixed is true, reserve contiguous area at exactly @base. If false, - * reserve in range from @base to @limit. - */ -int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, - phys_addr_t limit, struct cma **res_cma, - bool fixed) -{ - int ret; - - ret = cma_declare_contiguous(base, size, limit, 0, 0, fixed, - "reserved", res_cma); - if (ret) - return ret; - - /* Architecture specific contiguous memory fixup. */ - dma_contiguous_early_fixup(cma_get_base(*res_cma), - cma_get_size(*res_cma)); - - return 0; -} - -/** - * dma_alloc_from_contiguous() - allocate pages from contiguous area - * @dev: Pointer to device for which the allocation is performed. - * @count: Requested number of pages. - * @align: Requested alignment of pages (in PAGE_SIZE order). - * @gfp_mask: GFP flags to use for this allocation. - * - * This function allocates memory buffer for specified device. It uses - * device specific contiguous memory area if available or the default - * global one. Requires architecture specific dev_get_cma_area() helper - * function. - */ -struct page *dma_alloc_from_contiguous(struct device *dev, size_t count, - unsigned int align, gfp_t gfp_mask) -{ - if (align > CONFIG_CMA_ALIGNMENT) - align = CONFIG_CMA_ALIGNMENT; - - return cma_alloc(dev_get_cma_area(dev), count, align, gfp_mask); -} - -/** - * dma_release_from_contiguous() - release allocated pages - * @dev: Pointer to device for which the pages were allocated. - * @pages: Allocated pages. - * @count: Number of allocated pages. - * - * This function releases memory allocated by dma_alloc_from_contiguous(). - * It returns false when provided pages do not belong to contiguous area and - * true otherwise. - */ -bool dma_release_from_contiguous(struct device *dev, struct page *pages, - int count) -{ - return cma_release(dev_get_cma_area(dev), pages, count); -} - -/* - * Support for reserved memory regions defined in device tree - */ -#ifdef CONFIG_OF_RESERVED_MEM -#include -#include -#include - -#undef pr_fmt -#define pr_fmt(fmt) fmt - -static int rmem_cma_device_init(struct reserved_mem *rmem, struct device *dev) -{ - dev_set_cma_area(dev, rmem->priv); - return 0; -} - -static void rmem_cma_device_release(struct reserved_mem *rmem, - struct device *dev) -{ - dev_set_cma_area(dev, NULL); -} - -static const struct reserved_mem_ops rmem_cma_ops = { - .device_init = rmem_cma_device_init, - .device_release = rmem_cma_device_release, -}; - -static int __init rmem_cma_setup(struct reserved_mem *rmem) -{ - phys_addr_t align = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order); - phys_addr_t mask = align - 1; - unsigned long node = rmem->fdt_node; - struct cma *cma; - int err; - - if (!of_get_flat_dt_prop(node, "reusable", NULL) || - of_get_flat_dt_prop(node, "no-map", NULL)) - return -EINVAL; - - if ((rmem->base & mask) || (rmem->size & mask)) { - pr_err("Reserved memory: incorrect alignment of CMA region\n"); - return -EINVAL; - } - - err = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name, &cma); - if (err) { - pr_err("Reserved memory: unable to setup CMA region\n"); - return err; - } - /* Architecture specific contiguous memory fixup. */ - dma_contiguous_early_fixup(rmem->base, rmem->size); - - if (of_get_flat_dt_prop(node, "linux,cma-default", NULL)) - dma_contiguous_set_default(cma); - - rmem->ops = &rmem_cma_ops; - rmem->priv = cma; - - pr_info("Reserved memory: created CMA memory pool at %pa, size %ld MiB\n", - &rmem->base, (unsigned long)rmem->size / SZ_1M); - - return 0; -} -RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", rmem_cma_setup); -#endif diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c deleted file mode 100644 index f831a582209c..000000000000 --- a/drivers/base/dma-mapping.c +++ /dev/null @@ -1,345 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * drivers/base/dma-mapping.c - arch-independent dma-mapping routines - * - * Copyright (c) 2006 SUSE Linux Products GmbH - * Copyright (c) 2006 Tejun Heo - */ - -#include -#include -#include -#include -#include -#include -#include - -/* - * Managed DMA API - */ -struct dma_devres { - size_t size; - void *vaddr; - dma_addr_t dma_handle; - unsigned long attrs; -}; - -static void dmam_release(struct device *dev, void *res) -{ - struct dma_devres *this = res; - - dma_free_attrs(dev, this->size, this->vaddr, this->dma_handle, - this->attrs); -} - -static int dmam_match(struct device *dev, void *res, void *match_data) -{ - struct dma_devres *this = res, *match = match_data; - - if (this->vaddr == match->vaddr) { - WARN_ON(this->size != match->size || - this->dma_handle != match->dma_handle); - return 1; - } - return 0; -} - -/** - * dmam_alloc_coherent - Managed dma_alloc_coherent() - * @dev: Device to allocate coherent memory for - * @size: Size of allocation - * @dma_handle: Out argument for allocated DMA handle - * @gfp: Allocation flags - * - * Managed dma_alloc_coherent(). Memory allocated using this function - * will be automatically released on driver detach. - * - * RETURNS: - * Pointer to allocated memory on success, NULL on failure. - */ -void *dmam_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp) -{ - struct dma_devres *dr; - void *vaddr; - - dr = devres_alloc(dmam_release, sizeof(*dr), gfp); - if (!dr) - return NULL; - - vaddr = dma_alloc_coherent(dev, size, dma_handle, gfp); - if (!vaddr) { - devres_free(dr); - return NULL; - } - - dr->vaddr = vaddr; - dr->dma_handle = *dma_handle; - dr->size = size; - - devres_add(dev, dr); - - return vaddr; -} -EXPORT_SYMBOL(dmam_alloc_coherent); - -/** - * dmam_free_coherent - Managed dma_free_coherent() - * @dev: Device to free coherent memory for - * @size: Size of allocation - * @vaddr: Virtual address of the memory to free - * @dma_handle: DMA handle of the memory to free - * - * Managed dma_free_coherent(). - */ -void dmam_free_coherent(struct device *dev, size_t size, void *vaddr, - dma_addr_t dma_handle) -{ - struct dma_devres match_data = { size, vaddr, dma_handle }; - - dma_free_coherent(dev, size, vaddr, dma_handle); - WARN_ON(devres_destroy(dev, dmam_release, dmam_match, &match_data)); -} -EXPORT_SYMBOL(dmam_free_coherent); - -/** - * dmam_alloc_attrs - Managed dma_alloc_attrs() - * @dev: Device to allocate non_coherent memory for - * @size: Size of allocation - * @dma_handle: Out argument for allocated DMA handle - * @gfp: Allocation flags - * @attrs: Flags in the DMA_ATTR_* namespace. - * - * Managed dma_alloc_attrs(). Memory allocated using this function will be - * automatically released on driver detach. - * - * RETURNS: - * Pointer to allocated memory on success, NULL on failure. - */ -void *dmam_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle, - gfp_t gfp, unsigned long attrs) -{ - struct dma_devres *dr; - void *vaddr; - - dr = devres_alloc(dmam_release, sizeof(*dr), gfp); - if (!dr) - return NULL; - - vaddr = dma_alloc_attrs(dev, size, dma_handle, gfp, attrs); - if (!vaddr) { - devres_free(dr); - return NULL; - } - - dr->vaddr = vaddr; - dr->dma_handle = *dma_handle; - dr->size = size; - dr->attrs = attrs; - - devres_add(dev, dr); - - return vaddr; -} -EXPORT_SYMBOL(dmam_alloc_attrs); - -#ifdef CONFIG_HAVE_GENERIC_DMA_COHERENT - -static void dmam_coherent_decl_release(struct device *dev, void *res) -{ - dma_release_declared_memory(dev); -} - -/** - * dmam_declare_coherent_memory - Managed dma_declare_coherent_memory() - * @dev: Device to declare coherent memory for - * @phys_addr: Physical address of coherent memory to be declared - * @device_addr: Device address of coherent memory to be declared - * @size: Size of coherent memory to be declared - * @flags: Flags - * - * Managed dma_declare_coherent_memory(). - * - * RETURNS: - * 0 on success, -errno on failure. - */ -int dmam_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, - dma_addr_t device_addr, size_t size, int flags) -{ - void *res; - int rc; - - res = devres_alloc(dmam_coherent_decl_release, 0, GFP_KERNEL); - if (!res) - return -ENOMEM; - - rc = dma_declare_coherent_memory(dev, phys_addr, device_addr, size, - flags); - if (!rc) - devres_add(dev, res); - else - devres_free(res); - - return rc; -} -EXPORT_SYMBOL(dmam_declare_coherent_memory); - -/** - * dmam_release_declared_memory - Managed dma_release_declared_memory(). - * @dev: Device to release declared coherent memory for - * - * Managed dmam_release_declared_memory(). - */ -void dmam_release_declared_memory(struct device *dev) -{ - WARN_ON(devres_destroy(dev, dmam_coherent_decl_release, NULL, NULL)); -} -EXPORT_SYMBOL(dmam_release_declared_memory); - -#endif - -/* - * Create scatter-list for the already allocated DMA buffer. - */ -int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt, - void *cpu_addr, dma_addr_t handle, size_t size) -{ - struct page *page = virt_to_page(cpu_addr); - int ret; - - ret = sg_alloc_table(sgt, 1, GFP_KERNEL); - if (unlikely(ret)) - return ret; - - sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0); - return 0; -} -EXPORT_SYMBOL(dma_common_get_sgtable); - -/* - * Create userspace mapping for the DMA-coherent memory. - */ -int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, - void *cpu_addr, dma_addr_t dma_addr, size_t size) -{ - int ret = -ENXIO; -#ifndef CONFIG_ARCH_NO_COHERENT_DMA_MMAP - unsigned long user_count = vma_pages(vma); - unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT; - unsigned long off = vma->vm_pgoff; - - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - - if (dma_mmap_from_dev_coherent(dev, vma, cpu_addr, size, &ret)) - return ret; - - if (off < count && user_count <= (count - off)) - ret = remap_pfn_range(vma, vma->vm_start, - page_to_pfn(virt_to_page(cpu_addr)) + off, - user_count << PAGE_SHIFT, - vma->vm_page_prot); -#endif /* !CONFIG_ARCH_NO_COHERENT_DMA_MMAP */ - - return ret; -} -EXPORT_SYMBOL(dma_common_mmap); - -#ifdef CONFIG_MMU -static struct vm_struct *__dma_common_pages_remap(struct page **pages, - size_t size, unsigned long vm_flags, pgprot_t prot, - const void *caller) -{ - struct vm_struct *area; - - area = get_vm_area_caller(size, vm_flags, caller); - if (!area) - return NULL; - - if (map_vm_area(area, prot, pages)) { - vunmap(area->addr); - return NULL; - } - - return area; -} - -/* - * remaps an array of PAGE_SIZE pages into another vm_area - * Cannot be used in non-sleeping contexts - */ -void *dma_common_pages_remap(struct page **pages, size_t size, - unsigned long vm_flags, pgprot_t prot, - const void *caller) -{ - struct vm_struct *area; - - area = __dma_common_pages_remap(pages, size, vm_flags, prot, caller); - if (!area) - return NULL; - - area->pages = pages; - - return area->addr; -} - -/* - * remaps an allocated contiguous region into another vm_area. - * Cannot be used in non-sleeping contexts - */ - -void *dma_common_contiguous_remap(struct page *page, size_t size, - unsigned long vm_flags, - pgprot_t prot, const void *caller) -{ - int i; - struct page **pages; - struct vm_struct *area; - - pages = kmalloc(sizeof(struct page *) << get_order(size), GFP_KERNEL); - if (!pages) - return NULL; - - for (i = 0; i < (size >> PAGE_SHIFT); i++) - pages[i] = nth_page(page, i); - - area = __dma_common_pages_remap(pages, size, vm_flags, prot, caller); - - kfree(pages); - - if (!area) - return NULL; - return area->addr; -} - -/* - * unmaps a range previously mapped by dma_common_*_remap - */ -void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags) -{ - struct vm_struct *area = find_vm_area(cpu_addr); - - if (!area || (area->flags & vm_flags) != vm_flags) { - WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr); - return; - } - - unmap_kernel_range((unsigned long)cpu_addr, PAGE_ALIGN(size)); - vunmap(cpu_addr); -} -#endif - -/* - * enables DMA API use for a device - */ -int dma_configure(struct device *dev) -{ - if (dev->bus->dma_configure) - return dev->bus->dma_configure(dev); - return 0; -} - -void dma_deconfigure(struct device *dev) -{ - of_dma_deconfigure(dev); - acpi_dma_deconfigure(dev); -} diff --git a/include/linux/dma-contiguous.h b/include/linux/dma-contiguous.h index b67bf6ac907d..3c5a4cb3eb95 100644 --- a/include/linux/dma-contiguous.h +++ b/include/linux/dma-contiguous.h @@ -48,7 +48,7 @@ * CMA should not be used by the device drivers directly. It is * only a helper framework for dma-mapping subsystem. * - * For more information, see kernel-docs in drivers/base/dma-contiguous.c + * For more information, see kernel-docs in kernel/dma/contiguous.c */ #ifdef __KERNEL__ diff --git a/init/Kconfig b/init/Kconfig index 5a52f07259a2..fde3d09e8b27 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1719,10 +1719,6 @@ source "arch/Kconfig" endmenu # General setup -config HAVE_GENERIC_DMA_COHERENT - bool - default n - config RT_MUTEXES bool diff --git a/kernel/Makefile b/kernel/Makefile index d2001624fe7a..04bc07c2b42a 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -41,6 +41,7 @@ obj-y += printk/ obj-y += irq/ obj-y += rcu/ obj-y += livepatch/ +obj-y += dma/ obj-$(CONFIG_CHECKPOINT_RESTORE) += kcmp.o obj-$(CONFIG_FREEZER) += freezer.o diff --git a/kernel/dma/Kconfig b/kernel/dma/Kconfig new file mode 100644 index 000000000000..9bd54304446f --- /dev/null +++ b/kernel/dma/Kconfig @@ -0,0 +1,50 @@ + +config HAS_DMA + bool + depends on !NO_DMA + default y + +config NEED_SG_DMA_LENGTH + bool + +config NEED_DMA_MAP_STATE + bool + +config ARCH_DMA_ADDR_T_64BIT + def_bool 64BIT || PHYS_ADDR_T_64BIT + +config HAVE_GENERIC_DMA_COHERENT + bool + +config ARCH_HAS_SYNC_DMA_FOR_DEVICE + bool + +config ARCH_HAS_SYNC_DMA_FOR_CPU + bool + select NEED_DMA_MAP_STATE + +config DMA_DIRECT_OPS + bool + depends on HAS_DMA + +config DMA_NONCOHERENT_OPS + bool + depends on HAS_DMA + select DMA_DIRECT_OPS + +config DMA_NONCOHERENT_MMAP + bool + depends on DMA_NONCOHERENT_OPS + +config DMA_NONCOHERENT_CACHE_SYNC + bool + depends on DMA_NONCOHERENT_OPS + +config DMA_VIRT_OPS + bool + depends on HAS_DMA + +config SWIOTLB + bool + select DMA_DIRECT_OPS + select NEED_DMA_MAP_STATE diff --git a/kernel/dma/Makefile b/kernel/dma/Makefile new file mode 100644 index 000000000000..6de44e4eb454 --- /dev/null +++ b/kernel/dma/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_HAS_DMA) += mapping.o +obj-$(CONFIG_DMA_CMA) += contiguous.o +obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += coherent.o +obj-$(CONFIG_DMA_DIRECT_OPS) += direct.o +obj-$(CONFIG_DMA_NONCOHERENT_OPS) += noncoherent.o +obj-$(CONFIG_DMA_VIRT_OPS) += virt.o +obj-$(CONFIG_DMA_API_DEBUG) += debug.o +obj-$(CONFIG_SWIOTLB) += swiotlb.o + diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c new file mode 100644 index 000000000000..597d40893862 --- /dev/null +++ b/kernel/dma/coherent.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Coherent per-device memory handling. + * Borrowed from i386 + */ +#include +#include +#include +#include +#include + +struct dma_coherent_mem { + void *virt_base; + dma_addr_t device_base; + unsigned long pfn_base; + int size; + int flags; + unsigned long *bitmap; + spinlock_t spinlock; + bool use_dev_dma_pfn_offset; +}; + +static struct dma_coherent_mem *dma_coherent_default_memory __ro_after_init; + +static inline struct dma_coherent_mem *dev_get_coherent_memory(struct device *dev) +{ + if (dev && dev->dma_mem) + return dev->dma_mem; + return NULL; +} + +static inline dma_addr_t dma_get_device_base(struct device *dev, + struct dma_coherent_mem * mem) +{ + if (mem->use_dev_dma_pfn_offset) + return (mem->pfn_base - dev->dma_pfn_offset) << PAGE_SHIFT; + else + return mem->device_base; +} + +static int dma_init_coherent_memory( + phys_addr_t phys_addr, dma_addr_t device_addr, size_t size, int flags, + struct dma_coherent_mem **mem) +{ + struct dma_coherent_mem *dma_mem = NULL; + void __iomem *mem_base = NULL; + int pages = size >> PAGE_SHIFT; + int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long); + int ret; + + if (!size) { + ret = -EINVAL; + goto out; + } + + mem_base = memremap(phys_addr, size, MEMREMAP_WC); + if (!mem_base) { + ret = -EINVAL; + goto out; + } + dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL); + if (!dma_mem) { + ret = -ENOMEM; + goto out; + } + dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL); + if (!dma_mem->bitmap) { + ret = -ENOMEM; + goto out; + } + + dma_mem->virt_base = mem_base; + dma_mem->device_base = device_addr; + dma_mem->pfn_base = PFN_DOWN(phys_addr); + dma_mem->size = pages; + dma_mem->flags = flags; + spin_lock_init(&dma_mem->spinlock); + + *mem = dma_mem; + return 0; + +out: + kfree(dma_mem); + if (mem_base) + memunmap(mem_base); + return ret; +} + +static void dma_release_coherent_memory(struct dma_coherent_mem *mem) +{ + if (!mem) + return; + + memunmap(mem->virt_base); + kfree(mem->bitmap); + kfree(mem); +} + +static int dma_assign_coherent_memory(struct device *dev, + struct dma_coherent_mem *mem) +{ + if (!dev) + return -ENODEV; + + if (dev->dma_mem) + return -EBUSY; + + dev->dma_mem = mem; + return 0; +} + +int dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, + dma_addr_t device_addr, size_t size, int flags) +{ + struct dma_coherent_mem *mem; + int ret; + + ret = dma_init_coherent_memory(phys_addr, device_addr, size, flags, &mem); + if (ret) + return ret; + + ret = dma_assign_coherent_memory(dev, mem); + if (ret) + dma_release_coherent_memory(mem); + return ret; +} +EXPORT_SYMBOL(dma_declare_coherent_memory); + +void dma_release_declared_memory(struct device *dev) +{ + struct dma_coherent_mem *mem = dev->dma_mem; + + if (!mem) + return; + dma_release_coherent_memory(mem); + dev->dma_mem = NULL; +} +EXPORT_SYMBOL(dma_release_declared_memory); + +void *dma_mark_declared_memory_occupied(struct device *dev, + dma_addr_t device_addr, size_t size) +{ + struct dma_coherent_mem *mem = dev->dma_mem; + unsigned long flags; + int pos, err; + + size += device_addr & ~PAGE_MASK; + + if (!mem) + return ERR_PTR(-EINVAL); + + spin_lock_irqsave(&mem->spinlock, flags); + pos = PFN_DOWN(device_addr - dma_get_device_base(dev, mem)); + err = bitmap_allocate_region(mem->bitmap, pos, get_order(size)); + spin_unlock_irqrestore(&mem->spinlock, flags); + + if (err != 0) + return ERR_PTR(err); + return mem->virt_base + (pos << PAGE_SHIFT); +} +EXPORT_SYMBOL(dma_mark_declared_memory_occupied); + +static void *__dma_alloc_from_coherent(struct dma_coherent_mem *mem, + ssize_t size, dma_addr_t *dma_handle) +{ + int order = get_order(size); + unsigned long flags; + int pageno; + void *ret; + + spin_lock_irqsave(&mem->spinlock, flags); + + if (unlikely(size > (mem->size << PAGE_SHIFT))) + goto err; + + pageno = bitmap_find_free_region(mem->bitmap, mem->size, order); + if (unlikely(pageno < 0)) + goto err; + + /* + * Memory was found in the coherent area. + */ + *dma_handle = mem->device_base + (pageno << PAGE_SHIFT); + ret = mem->virt_base + (pageno << PAGE_SHIFT); + spin_unlock_irqrestore(&mem->spinlock, flags); + memset(ret, 0, size); + return ret; +err: + spin_unlock_irqrestore(&mem->spinlock, flags); + return NULL; +} + +/** + * dma_alloc_from_dev_coherent() - allocate memory from device coherent pool + * @dev: device from which we allocate memory + * @size: size of requested memory area + * @dma_handle: This will be filled with the correct dma handle + * @ret: This pointer will be filled with the virtual address + * to allocated area. + * + * This function should be only called from per-arch dma_alloc_coherent() + * to support allocation from per-device coherent memory pools. + * + * Returns 0 if dma_alloc_coherent should continue with allocating from + * generic memory areas, or !0 if dma_alloc_coherent should return @ret. + */ +int dma_alloc_from_dev_coherent(struct device *dev, ssize_t size, + dma_addr_t *dma_handle, void **ret) +{ + struct dma_coherent_mem *mem = dev_get_coherent_memory(dev); + + if (!mem) + return 0; + + *ret = __dma_alloc_from_coherent(mem, size, dma_handle); + if (*ret) + return 1; + + /* + * In the case where the allocation can not be satisfied from the + * per-device area, try to fall back to generic memory if the + * constraints allow it. + */ + return mem->flags & DMA_MEMORY_EXCLUSIVE; +} +EXPORT_SYMBOL(dma_alloc_from_dev_coherent); + +void *dma_alloc_from_global_coherent(ssize_t size, dma_addr_t *dma_handle) +{ + if (!dma_coherent_default_memory) + return NULL; + + return __dma_alloc_from_coherent(dma_coherent_default_memory, size, + dma_handle); +} + +static int __dma_release_from_coherent(struct dma_coherent_mem *mem, + int order, void *vaddr) +{ + if (mem && vaddr >= mem->virt_base && vaddr < + (mem->virt_base + (mem->size << PAGE_SHIFT))) { + int page = (vaddr - mem->virt_base) >> PAGE_SHIFT; + unsigned long flags; + + spin_lock_irqsave(&mem->spinlock, flags); + bitmap_release_region(mem->bitmap, page, order); + spin_unlock_irqrestore(&mem->spinlock, flags); + return 1; + } + return 0; +} + +/** + * dma_release_from_dev_coherent() - free memory to device coherent memory pool + * @dev: device from which the memory was allocated + * @order: the order of pages allocated + * @vaddr: virtual address of allocated pages + * + * This checks whether the memory was allocated from the per-device + * coherent memory pool and if so, releases that memory. + * + * Returns 1 if we correctly released the memory, or 0 if the caller should + * proceed with releasing memory from generic pools. + */ +int dma_release_from_dev_coherent(struct device *dev, int order, void *vaddr) +{ + struct dma_coherent_mem *mem = dev_get_coherent_memory(dev); + + return __dma_release_from_coherent(mem, order, vaddr); +} +EXPORT_SYMBOL(dma_release_from_dev_coherent); + +int dma_release_from_global_coherent(int order, void *vaddr) +{ + if (!dma_coherent_default_memory) + return 0; + + return __dma_release_from_coherent(dma_coherent_default_memory, order, + vaddr); +} + +static int __dma_mmap_from_coherent(struct dma_coherent_mem *mem, + struct vm_area_struct *vma, void *vaddr, size_t size, int *ret) +{ + if (mem && vaddr >= mem->virt_base && vaddr + size <= + (mem->virt_base + (mem->size << PAGE_SHIFT))) { + unsigned long off = vma->vm_pgoff; + int start = (vaddr - mem->virt_base) >> PAGE_SHIFT; + int user_count = vma_pages(vma); + int count = PAGE_ALIGN(size) >> PAGE_SHIFT; + + *ret = -ENXIO; + if (off < count && user_count <= count - off) { + unsigned long pfn = mem->pfn_base + start + off; + *ret = remap_pfn_range(vma, vma->vm_start, pfn, + user_count << PAGE_SHIFT, + vma->vm_page_prot); + } + return 1; + } + return 0; +} + +/** + * dma_mmap_from_dev_coherent() - mmap memory from the device coherent pool + * @dev: device from which the memory was allocated + * @vma: vm_area for the userspace memory + * @vaddr: cpu address returned by dma_alloc_from_dev_coherent + * @size: size of the memory buffer allocated + * @ret: result from remap_pfn_range() + * + * This checks whether the memory was allocated from the per-device + * coherent memory pool and if so, maps that memory to the provided vma. + * + * Returns 1 if @vaddr belongs to the device coherent pool and the caller + * should return @ret, or 0 if they should proceed with mapping memory from + * generic areas. + */ +int dma_mmap_from_dev_coherent(struct device *dev, struct vm_area_struct *vma, + void *vaddr, size_t size, int *ret) +{ + struct dma_coherent_mem *mem = dev_get_coherent_memory(dev); + + return __dma_mmap_from_coherent(mem, vma, vaddr, size, ret); +} +EXPORT_SYMBOL(dma_mmap_from_dev_coherent); + +int dma_mmap_from_global_coherent(struct vm_area_struct *vma, void *vaddr, + size_t size, int *ret) +{ + if (!dma_coherent_default_memory) + return 0; + + return __dma_mmap_from_coherent(dma_coherent_default_memory, vma, + vaddr, size, ret); +} + +/* + * Support for reserved memory regions defined in device tree + */ +#ifdef CONFIG_OF_RESERVED_MEM +#include +#include +#include + +static struct reserved_mem *dma_reserved_default_memory __initdata; + +static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev) +{ + struct dma_coherent_mem *mem = rmem->priv; + int ret; + + if (!mem) { + ret = dma_init_coherent_memory(rmem->base, rmem->base, + rmem->size, + DMA_MEMORY_EXCLUSIVE, &mem); + if (ret) { + pr_err("Reserved memory: failed to init DMA memory pool at %pa, size %ld MiB\n", + &rmem->base, (unsigned long)rmem->size / SZ_1M); + return ret; + } + } + mem->use_dev_dma_pfn_offset = true; + rmem->priv = mem; + dma_assign_coherent_memory(dev, mem); + return 0; +} + +static void rmem_dma_device_release(struct reserved_mem *rmem, + struct device *dev) +{ + if (dev) + dev->dma_mem = NULL; +} + +static const struct reserved_mem_ops rmem_dma_ops = { + .device_init = rmem_dma_device_init, + .device_release = rmem_dma_device_release, +}; + +static int __init rmem_dma_setup(struct reserved_mem *rmem) +{ + unsigned long node = rmem->fdt_node; + + if (of_get_flat_dt_prop(node, "reusable", NULL)) + return -EINVAL; + +#ifdef CONFIG_ARM + if (!of_get_flat_dt_prop(node, "no-map", NULL)) { + pr_err("Reserved memory: regions without no-map are not yet supported\n"); + return -EINVAL; + } + + if (of_get_flat_dt_prop(node, "linux,dma-default", NULL)) { + WARN(dma_reserved_default_memory, + "Reserved memory: region for default DMA coherent area is redefined\n"); + dma_reserved_default_memory = rmem; + } +#endif + + rmem->ops = &rmem_dma_ops; + pr_info("Reserved memory: created DMA memory pool at %pa, size %ld MiB\n", + &rmem->base, (unsigned long)rmem->size / SZ_1M); + return 0; +} + +static int __init dma_init_reserved_memory(void) +{ + const struct reserved_mem_ops *ops; + int ret; + + if (!dma_reserved_default_memory) + return -ENOMEM; + + ops = dma_reserved_default_memory->ops; + + /* + * We rely on rmem_dma_device_init() does not propagate error of + * dma_assign_coherent_memory() for "NULL" device. + */ + ret = ops->device_init(dma_reserved_default_memory, NULL); + + if (!ret) { + dma_coherent_default_memory = dma_reserved_default_memory->priv; + pr_info("DMA: default coherent area is set\n"); + } + + return ret; +} + +core_initcall(dma_init_reserved_memory); + +RESERVEDMEM_OF_DECLARE(dma, "shared-dma-pool", rmem_dma_setup); +#endif diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c new file mode 100644 index 000000000000..d987dcd1bd56 --- /dev/null +++ b/kernel/dma/contiguous.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Contiguous Memory Allocator for DMA mapping framework + * Copyright (c) 2010-2011 by Samsung Electronics. + * Written by: + * Marek Szyprowski + * Michal Nazarewicz + */ + +#define pr_fmt(fmt) "cma: " fmt + +#ifdef CONFIG_CMA_DEBUG +#ifndef DEBUG +# define DEBUG +#endif +#endif + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_CMA_SIZE_MBYTES +#define CMA_SIZE_MBYTES CONFIG_CMA_SIZE_MBYTES +#else +#define CMA_SIZE_MBYTES 0 +#endif + +struct cma *dma_contiguous_default_area; + +/* + * Default global CMA area size can be defined in kernel's .config. + * This is useful mainly for distro maintainers to create a kernel + * that works correctly for most supported systems. + * The size can be set in bytes or as a percentage of the total memory + * in the system. + * + * Users, who want to set the size of global CMA area for their system + * should use cma= kernel parameter. + */ +static const phys_addr_t size_bytes = (phys_addr_t)CMA_SIZE_MBYTES * SZ_1M; +static phys_addr_t size_cmdline = -1; +static phys_addr_t base_cmdline; +static phys_addr_t limit_cmdline; + +static int __init early_cma(char *p) +{ + pr_debug("%s(%s)\n", __func__, p); + size_cmdline = memparse(p, &p); + if (*p != '@') + return 0; + base_cmdline = memparse(p + 1, &p); + if (*p != '-') { + limit_cmdline = base_cmdline + size_cmdline; + return 0; + } + limit_cmdline = memparse(p + 1, &p); + + return 0; +} +early_param("cma", early_cma); + +#ifdef CONFIG_CMA_SIZE_PERCENTAGE + +static phys_addr_t __init __maybe_unused cma_early_percent_memory(void) +{ + struct memblock_region *reg; + unsigned long total_pages = 0; + + /* + * We cannot use memblock_phys_mem_size() here, because + * memblock_analyze() has not been called yet. + */ + for_each_memblock(memory, reg) + total_pages += memblock_region_memory_end_pfn(reg) - + memblock_region_memory_base_pfn(reg); + + return (total_pages * CONFIG_CMA_SIZE_PERCENTAGE / 100) << PAGE_SHIFT; +} + +#else + +static inline __maybe_unused phys_addr_t cma_early_percent_memory(void) +{ + return 0; +} + +#endif + +/** + * dma_contiguous_reserve() - reserve area(s) for contiguous memory handling + * @limit: End address of the reserved memory (optional, 0 for any). + * + * This function reserves memory from early allocator. It should be + * called by arch specific code once the early allocator (memblock or bootmem) + * has been activated and all other subsystems have already allocated/reserved + * memory. + */ +void __init dma_contiguous_reserve(phys_addr_t limit) +{ + phys_addr_t selected_size = 0; + phys_addr_t selected_base = 0; + phys_addr_t selected_limit = limit; + bool fixed = false; + + pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit); + + if (size_cmdline != -1) { + selected_size = size_cmdline; + selected_base = base_cmdline; + selected_limit = min_not_zero(limit_cmdline, limit); + if (base_cmdline + size_cmdline == limit_cmdline) + fixed = true; + } else { +#ifdef CONFIG_CMA_SIZE_SEL_MBYTES + selected_size = size_bytes; +#elif defined(CONFIG_CMA_SIZE_SEL_PERCENTAGE) + selected_size = cma_early_percent_memory(); +#elif defined(CONFIG_CMA_SIZE_SEL_MIN) + selected_size = min(size_bytes, cma_early_percent_memory()); +#elif defined(CONFIG_CMA_SIZE_SEL_MAX) + selected_size = max(size_bytes, cma_early_percent_memory()); +#endif + } + + if (selected_size && !dma_contiguous_default_area) { + pr_debug("%s: reserving %ld MiB for global area\n", __func__, + (unsigned long)selected_size / SZ_1M); + + dma_contiguous_reserve_area(selected_size, selected_base, + selected_limit, + &dma_contiguous_default_area, + fixed); + } +} + +/** + * dma_contiguous_reserve_area() - reserve custom contiguous area + * @size: Size of the reserved area (in bytes), + * @base: Base address of the reserved area optional, use 0 for any + * @limit: End address of the reserved memory (optional, 0 for any). + * @res_cma: Pointer to store the created cma region. + * @fixed: hint about where to place the reserved area + * + * This function reserves memory from early allocator. It should be + * called by arch specific code once the early allocator (memblock or bootmem) + * has been activated and all other subsystems have already allocated/reserved + * memory. This function allows to create custom reserved areas for specific + * devices. + * + * If @fixed is true, reserve contiguous area at exactly @base. If false, + * reserve in range from @base to @limit. + */ +int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, + phys_addr_t limit, struct cma **res_cma, + bool fixed) +{ + int ret; + + ret = cma_declare_contiguous(base, size, limit, 0, 0, fixed, + "reserved", res_cma); + if (ret) + return ret; + + /* Architecture specific contiguous memory fixup. */ + dma_contiguous_early_fixup(cma_get_base(*res_cma), + cma_get_size(*res_cma)); + + return 0; +} + +/** + * dma_alloc_from_contiguous() - allocate pages from contiguous area + * @dev: Pointer to device for which the allocation is performed. + * @count: Requested number of pages. + * @align: Requested alignment of pages (in PAGE_SIZE order). + * @gfp_mask: GFP flags to use for this allocation. + * + * This function allocates memory buffer for specified device. It uses + * device specific contiguous memory area if available or the default + * global one. Requires architecture specific dev_get_cma_area() helper + * function. + */ +struct page *dma_alloc_from_contiguous(struct device *dev, size_t count, + unsigned int align, gfp_t gfp_mask) +{ + if (align > CONFIG_CMA_ALIGNMENT) + align = CONFIG_CMA_ALIGNMENT; + + return cma_alloc(dev_get_cma_area(dev), count, align, gfp_mask); +} + +/** + * dma_release_from_contiguous() - release allocated pages + * @dev: Pointer to device for which the pages were allocated. + * @pages: Allocated pages. + * @count: Number of allocated pages. + * + * This function releases memory allocated by dma_alloc_from_contiguous(). + * It returns false when provided pages do not belong to contiguous area and + * true otherwise. + */ +bool dma_release_from_contiguous(struct device *dev, struct page *pages, + int count) +{ + return cma_release(dev_get_cma_area(dev), pages, count); +} + +/* + * Support for reserved memory regions defined in device tree + */ +#ifdef CONFIG_OF_RESERVED_MEM +#include +#include +#include + +#undef pr_fmt +#define pr_fmt(fmt) fmt + +static int rmem_cma_device_init(struct reserved_mem *rmem, struct device *dev) +{ + dev_set_cma_area(dev, rmem->priv); + return 0; +} + +static void rmem_cma_device_release(struct reserved_mem *rmem, + struct device *dev) +{ + dev_set_cma_area(dev, NULL); +} + +static const struct reserved_mem_ops rmem_cma_ops = { + .device_init = rmem_cma_device_init, + .device_release = rmem_cma_device_release, +}; + +static int __init rmem_cma_setup(struct reserved_mem *rmem) +{ + phys_addr_t align = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order); + phys_addr_t mask = align - 1; + unsigned long node = rmem->fdt_node; + struct cma *cma; + int err; + + if (!of_get_flat_dt_prop(node, "reusable", NULL) || + of_get_flat_dt_prop(node, "no-map", NULL)) + return -EINVAL; + + if ((rmem->base & mask) || (rmem->size & mask)) { + pr_err("Reserved memory: incorrect alignment of CMA region\n"); + return -EINVAL; + } + + err = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name, &cma); + if (err) { + pr_err("Reserved memory: unable to setup CMA region\n"); + return err; + } + /* Architecture specific contiguous memory fixup. */ + dma_contiguous_early_fixup(rmem->base, rmem->size); + + if (of_get_flat_dt_prop(node, "linux,cma-default", NULL)) + dma_contiguous_set_default(cma); + + rmem->ops = &rmem_cma_ops; + rmem->priv = cma; + + pr_info("Reserved memory: created CMA memory pool at %pa, size %ld MiB\n", + &rmem->base, (unsigned long)rmem->size / SZ_1M); + + return 0; +} +RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", rmem_cma_setup); +#endif diff --git a/kernel/dma/debug.c b/kernel/dma/debug.c new file mode 100644 index 000000000000..c007d25bee09 --- /dev/null +++ b/kernel/dma/debug.c @@ -0,0 +1,1773 @@ +/* + * Copyright (C) 2008 Advanced Micro Devices, Inc. + * + * Author: Joerg Roedel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define HASH_SIZE 1024ULL +#define HASH_FN_SHIFT 13 +#define HASH_FN_MASK (HASH_SIZE - 1) + +/* allow architectures to override this if absolutely required */ +#ifndef PREALLOC_DMA_DEBUG_ENTRIES +#define PREALLOC_DMA_DEBUG_ENTRIES (1 << 16) +#endif + +enum { + dma_debug_single, + dma_debug_page, + dma_debug_sg, + dma_debug_coherent, + dma_debug_resource, +}; + +enum map_err_types { + MAP_ERR_CHECK_NOT_APPLICABLE, + MAP_ERR_NOT_CHECKED, + MAP_ERR_CHECKED, +}; + +#define DMA_DEBUG_STACKTRACE_ENTRIES 5 + +/** + * struct dma_debug_entry - track a dma_map* or dma_alloc_coherent mapping + * @list: node on pre-allocated free_entries list + * @dev: 'dev' argument to dma_map_{page|single|sg} or dma_alloc_coherent + * @type: single, page, sg, coherent + * @pfn: page frame of the start address + * @offset: offset of mapping relative to pfn + * @size: length of the mapping + * @direction: enum dma_data_direction + * @sg_call_ents: 'nents' from dma_map_sg + * @sg_mapped_ents: 'mapped_ents' from dma_map_sg + * @map_err_type: track whether dma_mapping_error() was checked + * @stacktrace: support backtraces when a violation is detected + */ +struct dma_debug_entry { + struct list_head list; + struct device *dev; + int type; + unsigned long pfn; + size_t offset; + u64 dev_addr; + u64 size; + int direction; + int sg_call_ents; + int sg_mapped_ents; + enum map_err_types map_err_type; +#ifdef CONFIG_STACKTRACE + struct stack_trace stacktrace; + unsigned long st_entries[DMA_DEBUG_STACKTRACE_ENTRIES]; +#endif +}; + +typedef bool (*match_fn)(struct dma_debug_entry *, struct dma_debug_entry *); + +struct hash_bucket { + struct list_head list; + spinlock_t lock; +} ____cacheline_aligned_in_smp; + +/* Hash list to save the allocated dma addresses */ +static struct hash_bucket dma_entry_hash[HASH_SIZE]; +/* List of pre-allocated dma_debug_entry's */ +static LIST_HEAD(free_entries); +/* Lock for the list above */ +static DEFINE_SPINLOCK(free_entries_lock); + +/* Global disable flag - will be set in case of an error */ +static bool global_disable __read_mostly; + +/* Early initialization disable flag, set at the end of dma_debug_init */ +static bool dma_debug_initialized __read_mostly; + +static inline bool dma_debug_disabled(void) +{ + return global_disable || !dma_debug_initialized; +} + +/* Global error count */ +static u32 error_count; + +/* Global error show enable*/ +static u32 show_all_errors __read_mostly; +/* Number of errors to show */ +static u32 show_num_errors = 1; + +static u32 num_free_entries; +static u32 min_free_entries; +static u32 nr_total_entries; + +/* number of preallocated entries requested by kernel cmdline */ +static u32 nr_prealloc_entries = PREALLOC_DMA_DEBUG_ENTRIES; + +/* debugfs dentry's for the stuff above */ +static struct dentry *dma_debug_dent __read_mostly; +static struct dentry *global_disable_dent __read_mostly; +static struct dentry *error_count_dent __read_mostly; +static struct dentry *show_all_errors_dent __read_mostly; +static struct dentry *show_num_errors_dent __read_mostly; +static struct dentry *num_free_entries_dent __read_mostly; +static struct dentry *min_free_entries_dent __read_mostly; +static struct dentry *filter_dent __read_mostly; + +/* per-driver filter related state */ + +#define NAME_MAX_LEN 64 + +static char current_driver_name[NAME_MAX_LEN] __read_mostly; +static struct device_driver *current_driver __read_mostly; + +static DEFINE_RWLOCK(driver_name_lock); + +static const char *const maperr2str[] = { + [MAP_ERR_CHECK_NOT_APPLICABLE] = "dma map error check not applicable", + [MAP_ERR_NOT_CHECKED] = "dma map error not checked", + [MAP_ERR_CHECKED] = "dma map error checked", +}; + +static const char *type2name[5] = { "single", "page", + "scather-gather", "coherent", + "resource" }; + +static const char *dir2name[4] = { "DMA_BIDIRECTIONAL", "DMA_TO_DEVICE", + "DMA_FROM_DEVICE", "DMA_NONE" }; + +/* + * The access to some variables in this macro is racy. We can't use atomic_t + * here because all these variables are exported to debugfs. Some of them even + * writeable. This is also the reason why a lock won't help much. But anyway, + * the races are no big deal. Here is why: + * + * error_count: the addition is racy, but the worst thing that can happen is + * that we don't count some errors + * show_num_errors: the subtraction is racy. Also no big deal because in + * worst case this will result in one warning more in the + * system log than the user configured. This variable is + * writeable via debugfs. + */ +static inline void dump_entry_trace(struct dma_debug_entry *entry) +{ +#ifdef CONFIG_STACKTRACE + if (entry) { + pr_warning("Mapped at:\n"); + print_stack_trace(&entry->stacktrace, 0); + } +#endif +} + +static bool driver_filter(struct device *dev) +{ + struct device_driver *drv; + unsigned long flags; + bool ret; + + /* driver filter off */ + if (likely(!current_driver_name[0])) + return true; + + /* driver filter on and initialized */ + if (current_driver && dev && dev->driver == current_driver) + return true; + + /* driver filter on, but we can't filter on a NULL device... */ + if (!dev) + return false; + + if (current_driver || !current_driver_name[0]) + return false; + + /* driver filter on but not yet initialized */ + drv = dev->driver; + if (!drv) + return false; + + /* lock to protect against change of current_driver_name */ + read_lock_irqsave(&driver_name_lock, flags); + + ret = false; + if (drv->name && + strncmp(current_driver_name, drv->name, NAME_MAX_LEN - 1) == 0) { + current_driver = drv; + ret = true; + } + + read_unlock_irqrestore(&driver_name_lock, flags); + + return ret; +} + +#define err_printk(dev, entry, format, arg...) do { \ + error_count += 1; \ + if (driver_filter(dev) && \ + (show_all_errors || show_num_errors > 0)) { \ + WARN(1, "%s %s: " format, \ + dev ? dev_driver_string(dev) : "NULL", \ + dev ? dev_name(dev) : "NULL", ## arg); \ + dump_entry_trace(entry); \ + } \ + if (!show_all_errors && show_num_errors > 0) \ + show_num_errors -= 1; \ + } while (0); + +/* + * Hash related functions + * + * Every DMA-API request is saved into a struct dma_debug_entry. To + * have quick access to these structs they are stored into a hash. + */ +static int hash_fn(struct dma_debug_entry *entry) +{ + /* + * Hash function is based on the dma address. + * We use bits 20-27 here as the index into the hash + */ + return (entry->dev_addr >> HASH_FN_SHIFT) & HASH_FN_MASK; +} + +/* + * Request exclusive access to a hash bucket for a given dma_debug_entry. + */ +static struct hash_bucket *get_hash_bucket(struct dma_debug_entry *entry, + unsigned long *flags) + __acquires(&dma_entry_hash[idx].lock) +{ + int idx = hash_fn(entry); + unsigned long __flags; + + spin_lock_irqsave(&dma_entry_hash[idx].lock, __flags); + *flags = __flags; + return &dma_entry_hash[idx]; +} + +/* + * Give up exclusive access to the hash bucket + */ +static void put_hash_bucket(struct hash_bucket *bucket, + unsigned long *flags) + __releases(&bucket->lock) +{ + unsigned long __flags = *flags; + + spin_unlock_irqrestore(&bucket->lock, __flags); +} + +static bool exact_match(struct dma_debug_entry *a, struct dma_debug_entry *b) +{ + return ((a->dev_addr == b->dev_addr) && + (a->dev == b->dev)) ? true : false; +} + +static bool containing_match(struct dma_debug_entry *a, + struct dma_debug_entry *b) +{ + if (a->dev != b->dev) + return false; + + if ((b->dev_addr <= a->dev_addr) && + ((b->dev_addr + b->size) >= (a->dev_addr + a->size))) + return true; + + return false; +} + +/* + * Search a given entry in the hash bucket list + */ +static struct dma_debug_entry *__hash_bucket_find(struct hash_bucket *bucket, + struct dma_debug_entry *ref, + match_fn match) +{ + struct dma_debug_entry *entry, *ret = NULL; + int matches = 0, match_lvl, last_lvl = -1; + + list_for_each_entry(entry, &bucket->list, list) { + if (!match(ref, entry)) + continue; + + /* + * Some drivers map the same physical address multiple + * times. Without a hardware IOMMU this results in the + * same device addresses being put into the dma-debug + * hash multiple times too. This can result in false + * positives being reported. Therefore we implement a + * best-fit algorithm here which returns the entry from + * the hash which fits best to the reference value + * instead of the first-fit. + */ + matches += 1; + match_lvl = 0; + entry->size == ref->size ? ++match_lvl : 0; + entry->type == ref->type ? ++match_lvl : 0; + entry->direction == ref->direction ? ++match_lvl : 0; + entry->sg_call_ents == ref->sg_call_ents ? ++match_lvl : 0; + + if (match_lvl == 4) { + /* perfect-fit - return the result */ + return entry; + } else if (match_lvl > last_lvl) { + /* + * We found an entry that fits better then the + * previous one or it is the 1st match. + */ + last_lvl = match_lvl; + ret = entry; + } + } + + /* + * If we have multiple matches but no perfect-fit, just return + * NULL. + */ + ret = (matches == 1) ? ret : NULL; + + return ret; +} + +static struct dma_debug_entry *bucket_find_exact(struct hash_bucket *bucket, + struct dma_debug_entry *ref) +{ + return __hash_bucket_find(bucket, ref, exact_match); +} + +static struct dma_debug_entry *bucket_find_contain(struct hash_bucket **bucket, + struct dma_debug_entry *ref, + unsigned long *flags) +{ + + unsigned int max_range = dma_get_max_seg_size(ref->dev); + struct dma_debug_entry *entry, index = *ref; + unsigned int range = 0; + + while (range <= max_range) { + entry = __hash_bucket_find(*bucket, ref, containing_match); + + if (entry) + return entry; + + /* + * Nothing found, go back a hash bucket + */ + put_hash_bucket(*bucket, flags); + range += (1 << HASH_FN_SHIFT); + index.dev_addr -= (1 << HASH_FN_SHIFT); + *bucket = get_hash_bucket(&index, flags); + } + + return NULL; +} + +/* + * Add an entry to a hash bucket + */ +static void hash_bucket_add(struct hash_bucket *bucket, + struct dma_debug_entry *entry) +{ + list_add_tail(&entry->list, &bucket->list); +} + +/* + * Remove entry from a hash bucket list + */ +static void hash_bucket_del(struct dma_debug_entry *entry) +{ + list_del(&entry->list); +} + +static unsigned long long phys_addr(struct dma_debug_entry *entry) +{ + if (entry->type == dma_debug_resource) + return __pfn_to_phys(entry->pfn) + entry->offset; + + return page_to_phys(pfn_to_page(entry->pfn)) + entry->offset; +} + +/* + * Dump mapping entries for debugging purposes + */ +void debug_dma_dump_mappings(struct device *dev) +{ + int idx; + + for (idx = 0; idx < HASH_SIZE; idx++) { + struct hash_bucket *bucket = &dma_entry_hash[idx]; + struct dma_debug_entry *entry; + unsigned long flags; + + spin_lock_irqsave(&bucket->lock, flags); + + list_for_each_entry(entry, &bucket->list, list) { + if (!dev || dev == entry->dev) { + dev_info(entry->dev, + "%s idx %d P=%Lx N=%lx D=%Lx L=%Lx %s %s\n", + type2name[entry->type], idx, + phys_addr(entry), entry->pfn, + entry->dev_addr, entry->size, + dir2name[entry->direction], + maperr2str[entry->map_err_type]); + } + } + + spin_unlock_irqrestore(&bucket->lock, flags); + } +} + +/* + * For each mapping (initial cacheline in the case of + * dma_alloc_coherent/dma_map_page, initial cacheline in each page of a + * scatterlist, or the cacheline specified in dma_map_single) insert + * into this tree using the cacheline as the key. At + * dma_unmap_{single|sg|page} or dma_free_coherent delete the entry. If + * the entry already exists at insertion time add a tag as a reference + * count for the overlapping mappings. For now, the overlap tracking + * just ensures that 'unmaps' balance 'maps' before marking the + * cacheline idle, but we should also be flagging overlaps as an API + * violation. + * + * Memory usage is mostly constrained by the maximum number of available + * dma-debug entries in that we need a free dma_debug_entry before + * inserting into the tree. In the case of dma_map_page and + * dma_alloc_coherent there is only one dma_debug_entry and one + * dma_active_cacheline entry to track per event. dma_map_sg(), on the + * other hand, consumes a single dma_debug_entry, but inserts 'nents' + * entries into the tree. + * + * At any time debug_dma_assert_idle() can be called to trigger a + * warning if any cachelines in the given page are in the active set. + */ +static RADIX_TREE(dma_active_cacheline, GFP_NOWAIT); +static DEFINE_SPINLOCK(radix_lock); +#define ACTIVE_CACHELINE_MAX_OVERLAP ((1 << RADIX_TREE_MAX_TAGS) - 1) +#define CACHELINE_PER_PAGE_SHIFT (PAGE_SHIFT - L1_CACHE_SHIFT) +#define CACHELINES_PER_PAGE (1 << CACHELINE_PER_PAGE_SHIFT) + +static phys_addr_t to_cacheline_number(struct dma_debug_entry *entry) +{ + return (entry->pfn << CACHELINE_PER_PAGE_SHIFT) + + (entry->offset >> L1_CACHE_SHIFT); +} + +static int active_cacheline_read_overlap(phys_addr_t cln) +{ + int overlap = 0, i; + + for (i = RADIX_TREE_MAX_TAGS - 1; i >= 0; i--) + if (radix_tree_tag_get(&dma_active_cacheline, cln, i)) + overlap |= 1 << i; + return overlap; +} + +static int active_cacheline_set_overlap(phys_addr_t cln, int overlap) +{ + int i; + + if (overlap > ACTIVE_CACHELINE_MAX_OVERLAP || overlap < 0) + return overlap; + + for (i = RADIX_TREE_MAX_TAGS - 1; i >= 0; i--) + if (overlap & 1 << i) + radix_tree_tag_set(&dma_active_cacheline, cln, i); + else + radix_tree_tag_clear(&dma_active_cacheline, cln, i); + + return overlap; +} + +static void active_cacheline_inc_overlap(phys_addr_t cln) +{ + int overlap = active_cacheline_read_overlap(cln); + + overlap = active_cacheline_set_overlap(cln, ++overlap); + + /* If we overflowed the overlap counter then we're potentially + * leaking dma-mappings. Otherwise, if maps and unmaps are + * balanced then this overflow may cause false negatives in + * debug_dma_assert_idle() as the cacheline may be marked idle + * prematurely. + */ + WARN_ONCE(overlap > ACTIVE_CACHELINE_MAX_OVERLAP, + "DMA-API: exceeded %d overlapping mappings of cacheline %pa\n", + ACTIVE_CACHELINE_MAX_OVERLAP, &cln); +} + +static int active_cacheline_dec_overlap(phys_addr_t cln) +{ + int overlap = active_cacheline_read_overlap(cln); + + return active_cacheline_set_overlap(cln, --overlap); +} + +static int active_cacheline_insert(struct dma_debug_entry *entry) +{ + phys_addr_t cln = to_cacheline_number(entry); + unsigned long flags; + int rc; + + /* If the device is not writing memory then we don't have any + * concerns about the cpu consuming stale data. This mitigates + * legitimate usages of overlapping mappings. + */ + if (entry->direction == DMA_TO_DEVICE) + return 0; + + spin_lock_irqsave(&radix_lock, flags); + rc = radix_tree_insert(&dma_active_cacheline, cln, entry); + if (rc == -EEXIST) + active_cacheline_inc_overlap(cln); + spin_unlock_irqrestore(&radix_lock, flags); + + return rc; +} + +static void active_cacheline_remove(struct dma_debug_entry *entry) +{ + phys_addr_t cln = to_cacheline_number(entry); + unsigned long flags; + + /* ...mirror the insert case */ + if (entry->direction == DMA_TO_DEVICE) + return; + + spin_lock_irqsave(&radix_lock, flags); + /* since we are counting overlaps the final put of the + * cacheline will occur when the overlap count is 0. + * active_cacheline_dec_overlap() returns -1 in that case + */ + if (active_cacheline_dec_overlap(cln) < 0) + radix_tree_delete(&dma_active_cacheline, cln); + spin_unlock_irqrestore(&radix_lock, flags); +} + +/** + * debug_dma_assert_idle() - assert that a page is not undergoing dma + * @page: page to lookup in the dma_active_cacheline tree + * + * Place a call to this routine in cases where the cpu touching the page + * before the dma completes (page is dma_unmapped) will lead to data + * corruption. + */ +void debug_dma_assert_idle(struct page *page) +{ + static struct dma_debug_entry *ents[CACHELINES_PER_PAGE]; + struct dma_debug_entry *entry = NULL; + void **results = (void **) &ents; + unsigned int nents, i; + unsigned long flags; + phys_addr_t cln; + + if (dma_debug_disabled()) + return; + + if (!page) + return; + + cln = (phys_addr_t) page_to_pfn(page) << CACHELINE_PER_PAGE_SHIFT; + spin_lock_irqsave(&radix_lock, flags); + nents = radix_tree_gang_lookup(&dma_active_cacheline, results, cln, + CACHELINES_PER_PAGE); + for (i = 0; i < nents; i++) { + phys_addr_t ent_cln = to_cacheline_number(ents[i]); + + if (ent_cln == cln) { + entry = ents[i]; + break; + } else if (ent_cln >= cln + CACHELINES_PER_PAGE) + break; + } + spin_unlock_irqrestore(&radix_lock, flags); + + if (!entry) + return; + + cln = to_cacheline_number(entry); + err_printk(entry->dev, entry, + "DMA-API: cpu touching an active dma mapped cacheline [cln=%pa]\n", + &cln); +} + +/* + * Wrapper function for adding an entry to the hash. + * This function takes care of locking itself. + */ +static void add_dma_entry(struct dma_debug_entry *entry) +{ + struct hash_bucket *bucket; + unsigned long flags; + int rc; + + bucket = get_hash_bucket(entry, &flags); + hash_bucket_add(bucket, entry); + put_hash_bucket(bucket, &flags); + + rc = active_cacheline_insert(entry); + if (rc == -ENOMEM) { + pr_err("DMA-API: cacheline tracking ENOMEM, dma-debug disabled\n"); + global_disable = true; + } + + /* TODO: report -EEXIST errors here as overlapping mappings are + * not supported by the DMA API + */ +} + +static struct dma_debug_entry *__dma_entry_alloc(void) +{ + struct dma_debug_entry *entry; + + entry = list_entry(free_entries.next, struct dma_debug_entry, list); + list_del(&entry->list); + memset(entry, 0, sizeof(*entry)); + + num_free_entries -= 1; + if (num_free_entries < min_free_entries) + min_free_entries = num_free_entries; + + return entry; +} + +/* struct dma_entry allocator + * + * The next two functions implement the allocator for + * struct dma_debug_entries. + */ +static struct dma_debug_entry *dma_entry_alloc(void) +{ + struct dma_debug_entry *entry; + unsigned long flags; + + spin_lock_irqsave(&free_entries_lock, flags); + + if (list_empty(&free_entries)) { + global_disable = true; + spin_unlock_irqrestore(&free_entries_lock, flags); + pr_err("DMA-API: debugging out of memory - disabling\n"); + return NULL; + } + + entry = __dma_entry_alloc(); + + spin_unlock_irqrestore(&free_entries_lock, flags); + +#ifdef CONFIG_STACKTRACE + entry->stacktrace.max_entries = DMA_DEBUG_STACKTRACE_ENTRIES; + entry->stacktrace.entries = entry->st_entries; + entry->stacktrace.skip = 2; + save_stack_trace(&entry->stacktrace); +#endif + + return entry; +} + +static void dma_entry_free(struct dma_debug_entry *entry) +{ + unsigned long flags; + + active_cacheline_remove(entry); + + /* + * add to beginning of the list - this way the entries are + * more likely cache hot when they are reallocated. + */ + spin_lock_irqsave(&free_entries_lock, flags); + list_add(&entry->list, &free_entries); + num_free_entries += 1; + spin_unlock_irqrestore(&free_entries_lock, flags); +} + +int dma_debug_resize_entries(u32 num_entries) +{ + int i, delta, ret = 0; + unsigned long flags; + struct dma_debug_entry *entry; + LIST_HEAD(tmp); + + spin_lock_irqsave(&free_entries_lock, flags); + + if (nr_total_entries < num_entries) { + delta = num_entries - nr_total_entries; + + spin_unlock_irqrestore(&free_entries_lock, flags); + + for (i = 0; i < delta; i++) { + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + break; + + list_add_tail(&entry->list, &tmp); + } + + spin_lock_irqsave(&free_entries_lock, flags); + + list_splice(&tmp, &free_entries); + nr_total_entries += i; + num_free_entries += i; + } else { + delta = nr_total_entries - num_entries; + + for (i = 0; i < delta && !list_empty(&free_entries); i++) { + entry = __dma_entry_alloc(); + kfree(entry); + } + + nr_total_entries -= i; + } + + if (nr_total_entries != num_entries) + ret = 1; + + spin_unlock_irqrestore(&free_entries_lock, flags); + + return ret; +} + +/* + * DMA-API debugging init code + * + * The init code does two things: + * 1. Initialize core data structures + * 2. Preallocate a given number of dma_debug_entry structs + */ + +static int prealloc_memory(u32 num_entries) +{ + struct dma_debug_entry *entry, *next_entry; + int i; + + for (i = 0; i < num_entries; ++i) { + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + goto out_err; + + list_add_tail(&entry->list, &free_entries); + } + + num_free_entries = num_entries; + min_free_entries = num_entries; + + pr_info("DMA-API: preallocated %d debug entries\n", num_entries); + + return 0; + +out_err: + + list_for_each_entry_safe(entry, next_entry, &free_entries, list) { + list_del(&entry->list); + kfree(entry); + } + + return -ENOMEM; +} + +static ssize_t filter_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[NAME_MAX_LEN + 1]; + unsigned long flags; + int len; + + if (!current_driver_name[0]) + return 0; + + /* + * We can't copy to userspace directly because current_driver_name can + * only be read under the driver_name_lock with irqs disabled. So + * create a temporary copy first. + */ + read_lock_irqsave(&driver_name_lock, flags); + len = scnprintf(buf, NAME_MAX_LEN + 1, "%s\n", current_driver_name); + read_unlock_irqrestore(&driver_name_lock, flags); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t filter_write(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + char buf[NAME_MAX_LEN]; + unsigned long flags; + size_t len; + int i; + + /* + * We can't copy from userspace directly. Access to + * current_driver_name is protected with a write_lock with irqs + * disabled. Since copy_from_user can fault and may sleep we + * need to copy to temporary buffer first + */ + len = min(count, (size_t)(NAME_MAX_LEN - 1)); + if (copy_from_user(buf, userbuf, len)) + return -EFAULT; + + buf[len] = 0; + + write_lock_irqsave(&driver_name_lock, flags); + + /* + * Now handle the string we got from userspace very carefully. + * The rules are: + * - only use the first token we got + * - token delimiter is everything looking like a space + * character (' ', '\n', '\t' ...) + * + */ + if (!isalnum(buf[0])) { + /* + * If the first character userspace gave us is not + * alphanumerical then assume the filter should be + * switched off. + */ + if (current_driver_name[0]) + pr_info("DMA-API: switching off dma-debug driver filter\n"); + current_driver_name[0] = 0; + current_driver = NULL; + goto out_unlock; + } + + /* + * Now parse out the first token and use it as the name for the + * driver to filter for. + */ + for (i = 0; i < NAME_MAX_LEN - 1; ++i) { + current_driver_name[i] = buf[i]; + if (isspace(buf[i]) || buf[i] == ' ' || buf[i] == 0) + break; + } + current_driver_name[i] = 0; + current_driver = NULL; + + pr_info("DMA-API: enable driver filter for driver [%s]\n", + current_driver_name); + +out_unlock: + write_unlock_irqrestore(&driver_name_lock, flags); + + return count; +} + +static const struct file_operations filter_fops = { + .read = filter_read, + .write = filter_write, + .llseek = default_llseek, +}; + +static int dma_debug_fs_init(void) +{ + dma_debug_dent = debugfs_create_dir("dma-api", NULL); + if (!dma_debug_dent) { + pr_err("DMA-API: can not create debugfs directory\n"); + return -ENOMEM; + } + + global_disable_dent = debugfs_create_bool("disabled", 0444, + dma_debug_dent, + &global_disable); + if (!global_disable_dent) + goto out_err; + + error_count_dent = debugfs_create_u32("error_count", 0444, + dma_debug_dent, &error_count); + if (!error_count_dent) + goto out_err; + + show_all_errors_dent = debugfs_create_u32("all_errors", 0644, + dma_debug_dent, + &show_all_errors); + if (!show_all_errors_dent) + goto out_err; + + show_num_errors_dent = debugfs_create_u32("num_errors", 0644, + dma_debug_dent, + &show_num_errors); + if (!show_num_errors_dent) + goto out_err; + + num_free_entries_dent = debugfs_create_u32("num_free_entries", 0444, + dma_debug_dent, + &num_free_entries); + if (!num_free_entries_dent) + goto out_err; + + min_free_entries_dent = debugfs_create_u32("min_free_entries", 0444, + dma_debug_dent, + &min_free_entries); + if (!min_free_entries_dent) + goto out_err; + + filter_dent = debugfs_create_file("driver_filter", 0644, + dma_debug_dent, NULL, &filter_fops); + if (!filter_dent) + goto out_err; + + return 0; + +out_err: + debugfs_remove_recursive(dma_debug_dent); + + return -ENOMEM; +} + +static int device_dma_allocations(struct device *dev, struct dma_debug_entry **out_entry) +{ + struct dma_debug_entry *entry; + unsigned long flags; + int count = 0, i; + + for (i = 0; i < HASH_SIZE; ++i) { + spin_lock_irqsave(&dma_entry_hash[i].lock, flags); + list_for_each_entry(entry, &dma_entry_hash[i].list, list) { + if (entry->dev == dev) { + count += 1; + *out_entry = entry; + } + } + spin_unlock_irqrestore(&dma_entry_hash[i].lock, flags); + } + + return count; +} + +static int dma_debug_device_change(struct notifier_block *nb, unsigned long action, void *data) +{ + struct device *dev = data; + struct dma_debug_entry *uninitialized_var(entry); + int count; + + if (dma_debug_disabled()) + return 0; + + switch (action) { + case BUS_NOTIFY_UNBOUND_DRIVER: + count = device_dma_allocations(dev, &entry); + if (count == 0) + break; + err_printk(dev, entry, "DMA-API: device driver has pending " + "DMA allocations while released from device " + "[count=%d]\n" + "One of leaked entries details: " + "[device address=0x%016llx] [size=%llu bytes] " + "[mapped with %s] [mapped as %s]\n", + count, entry->dev_addr, entry->size, + dir2name[entry->direction], type2name[entry->type]); + break; + default: + break; + } + + return 0; +} + +void dma_debug_add_bus(struct bus_type *bus) +{ + struct notifier_block *nb; + + if (dma_debug_disabled()) + return; + + nb = kzalloc(sizeof(struct notifier_block), GFP_KERNEL); + if (nb == NULL) { + pr_err("dma_debug_add_bus: out of memory\n"); + return; + } + + nb->notifier_call = dma_debug_device_change; + + bus_register_notifier(bus, nb); +} + +static int dma_debug_init(void) +{ + int i; + + /* Do not use dma_debug_initialized here, since we really want to be + * called to set dma_debug_initialized + */ + if (global_disable) + return 0; + + for (i = 0; i < HASH_SIZE; ++i) { + INIT_LIST_HEAD(&dma_entry_hash[i].list); + spin_lock_init(&dma_entry_hash[i].lock); + } + + if (dma_debug_fs_init() != 0) { + pr_err("DMA-API: error creating debugfs entries - disabling\n"); + global_disable = true; + + return 0; + } + + if (prealloc_memory(nr_prealloc_entries) != 0) { + pr_err("DMA-API: debugging out of memory error - disabled\n"); + global_disable = true; + + return 0; + } + + nr_total_entries = num_free_entries; + + dma_debug_initialized = true; + + pr_info("DMA-API: debugging enabled by kernel config\n"); + return 0; +} +core_initcall(dma_debug_init); + +static __init int dma_debug_cmdline(char *str) +{ + if (!str) + return -EINVAL; + + if (strncmp(str, "off", 3) == 0) { + pr_info("DMA-API: debugging disabled on kernel command line\n"); + global_disable = true; + } + + return 0; +} + +static __init int dma_debug_entries_cmdline(char *str) +{ + if (!str) + return -EINVAL; + if (!get_option(&str, &nr_prealloc_entries)) + nr_prealloc_entries = PREALLOC_DMA_DEBUG_ENTRIES; + return 0; +} + +__setup("dma_debug=", dma_debug_cmdline); +__setup("dma_debug_entries=", dma_debug_entries_cmdline); + +static void check_unmap(struct dma_debug_entry *ref) +{ + struct dma_debug_entry *entry; + struct hash_bucket *bucket; + unsigned long flags; + + bucket = get_hash_bucket(ref, &flags); + entry = bucket_find_exact(bucket, ref); + + if (!entry) { + /* must drop lock before calling dma_mapping_error */ + put_hash_bucket(bucket, &flags); + + if (dma_mapping_error(ref->dev, ref->dev_addr)) { + err_printk(ref->dev, NULL, + "DMA-API: device driver tries to free an " + "invalid DMA memory address\n"); + } else { + err_printk(ref->dev, NULL, + "DMA-API: device driver tries to free DMA " + "memory it has not allocated [device " + "address=0x%016llx] [size=%llu bytes]\n", + ref->dev_addr, ref->size); + } + return; + } + + if (ref->size != entry->size) { + err_printk(ref->dev, entry, "DMA-API: device driver frees " + "DMA memory with different size " + "[device address=0x%016llx] [map size=%llu bytes] " + "[unmap size=%llu bytes]\n", + ref->dev_addr, entry->size, ref->size); + } + + if (ref->type != entry->type) { + err_printk(ref->dev, entry, "DMA-API: device driver frees " + "DMA memory with wrong function " + "[device address=0x%016llx] [size=%llu bytes] " + "[mapped as %s] [unmapped as %s]\n", + ref->dev_addr, ref->size, + type2name[entry->type], type2name[ref->type]); + } else if ((entry->type == dma_debug_coherent) && + (phys_addr(ref) != phys_addr(entry))) { + err_printk(ref->dev, entry, "DMA-API: device driver frees " + "DMA memory with different CPU address " + "[device address=0x%016llx] [size=%llu bytes] " + "[cpu alloc address=0x%016llx] " + "[cpu free address=0x%016llx]", + ref->dev_addr, ref->size, + phys_addr(entry), + phys_addr(ref)); + } + + if (ref->sg_call_ents && ref->type == dma_debug_sg && + ref->sg_call_ents != entry->sg_call_ents) { + err_printk(ref->dev, entry, "DMA-API: device driver frees " + "DMA sg list with different entry count " + "[map count=%d] [unmap count=%d]\n", + entry->sg_call_ents, ref->sg_call_ents); + } + + /* + * This may be no bug in reality - but most implementations of the + * DMA API don't handle this properly, so check for it here + */ + if (ref->direction != entry->direction) { + err_printk(ref->dev, entry, "DMA-API: device driver frees " + "DMA memory with different direction " + "[device address=0x%016llx] [size=%llu bytes] " + "[mapped with %s] [unmapped with %s]\n", + ref->dev_addr, ref->size, + dir2name[entry->direction], + dir2name[ref->direction]); + } + + /* + * Drivers should use dma_mapping_error() to check the returned + * addresses of dma_map_single() and dma_map_page(). + * If not, print this warning message. See Documentation/DMA-API.txt. + */ + if (entry->map_err_type == MAP_ERR_NOT_CHECKED) { + err_printk(ref->dev, entry, + "DMA-API: device driver failed to check map error" + "[device address=0x%016llx] [size=%llu bytes] " + "[mapped as %s]", + ref->dev_addr, ref->size, + type2name[entry->type]); + } + + hash_bucket_del(entry); + dma_entry_free(entry); + + put_hash_bucket(bucket, &flags); +} + +static void check_for_stack(struct device *dev, + struct page *page, size_t offset) +{ + void *addr; + struct vm_struct *stack_vm_area = task_stack_vm_area(current); + + if (!stack_vm_area) { + /* Stack is direct-mapped. */ + if (PageHighMem(page)) + return; + addr = page_address(page) + offset; + if (object_is_on_stack(addr)) + err_printk(dev, NULL, "DMA-API: device driver maps memory from stack [addr=%p]\n", addr); + } else { + /* Stack is vmalloced. */ + int i; + + for (i = 0; i < stack_vm_area->nr_pages; i++) { + if (page != stack_vm_area->pages[i]) + continue; + + addr = (u8 *)current->stack + i * PAGE_SIZE + offset; + err_printk(dev, NULL, "DMA-API: device driver maps memory from stack [probable addr=%p]\n", addr); + break; + } + } +} + +static inline bool overlap(void *addr, unsigned long len, void *start, void *end) +{ + unsigned long a1 = (unsigned long)addr; + unsigned long b1 = a1 + len; + unsigned long a2 = (unsigned long)start; + unsigned long b2 = (unsigned long)end; + + return !(b1 <= a2 || a1 >= b2); +} + +static void check_for_illegal_area(struct device *dev, void *addr, unsigned long len) +{ + if (overlap(addr, len, _stext, _etext) || + overlap(addr, len, __start_rodata, __end_rodata)) + err_printk(dev, NULL, "DMA-API: device driver maps memory from kernel text or rodata [addr=%p] [len=%lu]\n", addr, len); +} + +static void check_sync(struct device *dev, + struct dma_debug_entry *ref, + bool to_cpu) +{ + struct dma_debug_entry *entry; + struct hash_bucket *bucket; + unsigned long flags; + + bucket = get_hash_bucket(ref, &flags); + + entry = bucket_find_contain(&bucket, ref, &flags); + + if (!entry) { + err_printk(dev, NULL, "DMA-API: device driver tries " + "to sync DMA memory it has not allocated " + "[device address=0x%016llx] [size=%llu bytes]\n", + (unsigned long long)ref->dev_addr, ref->size); + goto out; + } + + if (ref->size > entry->size) { + err_printk(dev, entry, "DMA-API: device driver syncs" + " DMA memory outside allocated range " + "[device address=0x%016llx] " + "[allocation size=%llu bytes] " + "[sync offset+size=%llu]\n", + entry->dev_addr, entry->size, + ref->size); + } + + if (entry->direction == DMA_BIDIRECTIONAL) + goto out; + + if (ref->direction != entry->direction) { + err_printk(dev, entry, "DMA-API: device driver syncs " + "DMA memory with different direction " + "[device address=0x%016llx] [size=%llu bytes] " + "[mapped with %s] [synced with %s]\n", + (unsigned long long)ref->dev_addr, entry->size, + dir2name[entry->direction], + dir2name[ref->direction]); + } + + if (to_cpu && !(entry->direction == DMA_FROM_DEVICE) && + !(ref->direction == DMA_TO_DEVICE)) + err_printk(dev, entry, "DMA-API: device driver syncs " + "device read-only DMA memory for cpu " + "[device address=0x%016llx] [size=%llu bytes] " + "[mapped with %s] [synced with %s]\n", + (unsigned long long)ref->dev_addr, entry->size, + dir2name[entry->direction], + dir2name[ref->direction]); + + if (!to_cpu && !(entry->direction == DMA_TO_DEVICE) && + !(ref->direction == DMA_FROM_DEVICE)) + err_printk(dev, entry, "DMA-API: device driver syncs " + "device write-only DMA memory to device " + "[device address=0x%016llx] [size=%llu bytes] " + "[mapped with %s] [synced with %s]\n", + (unsigned long long)ref->dev_addr, entry->size, + dir2name[entry->direction], + dir2name[ref->direction]); + + if (ref->sg_call_ents && ref->type == dma_debug_sg && + ref->sg_call_ents != entry->sg_call_ents) { + err_printk(ref->dev, entry, "DMA-API: device driver syncs " + "DMA sg list with different entry count " + "[map count=%d] [sync count=%d]\n", + entry->sg_call_ents, ref->sg_call_ents); + } + +out: + put_hash_bucket(bucket, &flags); +} + +static void check_sg_segment(struct device *dev, struct scatterlist *sg) +{ +#ifdef CONFIG_DMA_API_DEBUG_SG + unsigned int max_seg = dma_get_max_seg_size(dev); + u64 start, end, boundary = dma_get_seg_boundary(dev); + + /* + * Either the driver forgot to set dma_parms appropriately, or + * whoever generated the list forgot to check them. + */ + if (sg->length > max_seg) + err_printk(dev, NULL, "DMA-API: mapping sg segment longer than device claims to support [len=%u] [max=%u]\n", + sg->length, max_seg); + /* + * In some cases this could potentially be the DMA API + * implementation's fault, but it would usually imply that + * the scatterlist was built inappropriately to begin with. + */ + start = sg_dma_address(sg); + end = start + sg_dma_len(sg) - 1; + if ((start ^ end) & ~boundary) + err_printk(dev, NULL, "DMA-API: mapping sg segment across boundary [start=0x%016llx] [end=0x%016llx] [boundary=0x%016llx]\n", + start, end, boundary); +#endif +} + +void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, + size_t size, int direction, dma_addr_t dma_addr, + bool map_single) +{ + struct dma_debug_entry *entry; + + if (unlikely(dma_debug_disabled())) + return; + + if (dma_mapping_error(dev, dma_addr)) + return; + + entry = dma_entry_alloc(); + if (!entry) + return; + + entry->dev = dev; + entry->type = dma_debug_page; + entry->pfn = page_to_pfn(page); + entry->offset = offset, + entry->dev_addr = dma_addr; + entry->size = size; + entry->direction = direction; + entry->map_err_type = MAP_ERR_NOT_CHECKED; + + if (map_single) + entry->type = dma_debug_single; + + check_for_stack(dev, page, offset); + + if (!PageHighMem(page)) { + void *addr = page_address(page) + offset; + + check_for_illegal_area(dev, addr, size); + } + + add_dma_entry(entry); +} +EXPORT_SYMBOL(debug_dma_map_page); + +void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ + struct dma_debug_entry ref; + struct dma_debug_entry *entry; + struct hash_bucket *bucket; + unsigned long flags; + + if (unlikely(dma_debug_disabled())) + return; + + ref.dev = dev; + ref.dev_addr = dma_addr; + bucket = get_hash_bucket(&ref, &flags); + + list_for_each_entry(entry, &bucket->list, list) { + if (!exact_match(&ref, entry)) + continue; + + /* + * The same physical address can be mapped multiple + * times. Without a hardware IOMMU this results in the + * same device addresses being put into the dma-debug + * hash multiple times too. This can result in false + * positives being reported. Therefore we implement a + * best-fit algorithm here which updates the first entry + * from the hash which fits the reference value and is + * not currently listed as being checked. + */ + if (entry->map_err_type == MAP_ERR_NOT_CHECKED) { + entry->map_err_type = MAP_ERR_CHECKED; + break; + } + } + + put_hash_bucket(bucket, &flags); +} +EXPORT_SYMBOL(debug_dma_mapping_error); + +void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, + size_t size, int direction, bool map_single) +{ + struct dma_debug_entry ref = { + .type = dma_debug_page, + .dev = dev, + .dev_addr = addr, + .size = size, + .direction = direction, + }; + + if (unlikely(dma_debug_disabled())) + return; + + if (map_single) + ref.type = dma_debug_single; + + check_unmap(&ref); +} +EXPORT_SYMBOL(debug_dma_unmap_page); + +void debug_dma_map_sg(struct device *dev, struct scatterlist *sg, + int nents, int mapped_ents, int direction) +{ + struct dma_debug_entry *entry; + struct scatterlist *s; + int i; + + if (unlikely(dma_debug_disabled())) + return; + + for_each_sg(sg, s, mapped_ents, i) { + entry = dma_entry_alloc(); + if (!entry) + return; + + entry->type = dma_debug_sg; + entry->dev = dev; + entry->pfn = page_to_pfn(sg_page(s)); + entry->offset = s->offset, + entry->size = sg_dma_len(s); + entry->dev_addr = sg_dma_address(s); + entry->direction = direction; + entry->sg_call_ents = nents; + entry->sg_mapped_ents = mapped_ents; + + check_for_stack(dev, sg_page(s), s->offset); + + if (!PageHighMem(sg_page(s))) { + check_for_illegal_area(dev, sg_virt(s), sg_dma_len(s)); + } + + check_sg_segment(dev, s); + + add_dma_entry(entry); + } +} +EXPORT_SYMBOL(debug_dma_map_sg); + +static int get_nr_mapped_entries(struct device *dev, + struct dma_debug_entry *ref) +{ + struct dma_debug_entry *entry; + struct hash_bucket *bucket; + unsigned long flags; + int mapped_ents; + + bucket = get_hash_bucket(ref, &flags); + entry = bucket_find_exact(bucket, ref); + mapped_ents = 0; + + if (entry) + mapped_ents = entry->sg_mapped_ents; + put_hash_bucket(bucket, &flags); + + return mapped_ents; +} + +void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, + int nelems, int dir) +{ + struct scatterlist *s; + int mapped_ents = 0, i; + + if (unlikely(dma_debug_disabled())) + return; + + for_each_sg(sglist, s, nelems, i) { + + struct dma_debug_entry ref = { + .type = dma_debug_sg, + .dev = dev, + .pfn = page_to_pfn(sg_page(s)), + .offset = s->offset, + .dev_addr = sg_dma_address(s), + .size = sg_dma_len(s), + .direction = dir, + .sg_call_ents = nelems, + }; + + if (mapped_ents && i >= mapped_ents) + break; + + if (!i) + mapped_ents = get_nr_mapped_entries(dev, &ref); + + check_unmap(&ref); + } +} +EXPORT_SYMBOL(debug_dma_unmap_sg); + +void debug_dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t dma_addr, void *virt) +{ + struct dma_debug_entry *entry; + + if (unlikely(dma_debug_disabled())) + return; + + if (unlikely(virt == NULL)) + return; + + /* handle vmalloc and linear addresses */ + if (!is_vmalloc_addr(virt) && !virt_addr_valid(virt)) + return; + + entry = dma_entry_alloc(); + if (!entry) + return; + + entry->type = dma_debug_coherent; + entry->dev = dev; + entry->offset = offset_in_page(virt); + entry->size = size; + entry->dev_addr = dma_addr; + entry->direction = DMA_BIDIRECTIONAL; + + if (is_vmalloc_addr(virt)) + entry->pfn = vmalloc_to_pfn(virt); + else + entry->pfn = page_to_pfn(virt_to_page(virt)); + + add_dma_entry(entry); +} +EXPORT_SYMBOL(debug_dma_alloc_coherent); + +void debug_dma_free_coherent(struct device *dev, size_t size, + void *virt, dma_addr_t addr) +{ + struct dma_debug_entry ref = { + .type = dma_debug_coherent, + .dev = dev, + .offset = offset_in_page(virt), + .dev_addr = addr, + .size = size, + .direction = DMA_BIDIRECTIONAL, + }; + + /* handle vmalloc and linear addresses */ + if (!is_vmalloc_addr(virt) && !virt_addr_valid(virt)) + return; + + if (is_vmalloc_addr(virt)) + ref.pfn = vmalloc_to_pfn(virt); + else + ref.pfn = page_to_pfn(virt_to_page(virt)); + + if (unlikely(dma_debug_disabled())) + return; + + check_unmap(&ref); +} +EXPORT_SYMBOL(debug_dma_free_coherent); + +void debug_dma_map_resource(struct device *dev, phys_addr_t addr, size_t size, + int direction, dma_addr_t dma_addr) +{ + struct dma_debug_entry *entry; + + if (unlikely(dma_debug_disabled())) + return; + + entry = dma_entry_alloc(); + if (!entry) + return; + + entry->type = dma_debug_resource; + entry->dev = dev; + entry->pfn = PHYS_PFN(addr); + entry->offset = offset_in_page(addr); + entry->size = size; + entry->dev_addr = dma_addr; + entry->direction = direction; + entry->map_err_type = MAP_ERR_NOT_CHECKED; + + add_dma_entry(entry); +} +EXPORT_SYMBOL(debug_dma_map_resource); + +void debug_dma_unmap_resource(struct device *dev, dma_addr_t dma_addr, + size_t size, int direction) +{ + struct dma_debug_entry ref = { + .type = dma_debug_resource, + .dev = dev, + .dev_addr = dma_addr, + .size = size, + .direction = direction, + }; + + if (unlikely(dma_debug_disabled())) + return; + + check_unmap(&ref); +} +EXPORT_SYMBOL(debug_dma_unmap_resource); + +void debug_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, + size_t size, int direction) +{ + struct dma_debug_entry ref; + + if (unlikely(dma_debug_disabled())) + return; + + ref.type = dma_debug_single; + ref.dev = dev; + ref.dev_addr = dma_handle; + ref.size = size; + ref.direction = direction; + ref.sg_call_ents = 0; + + check_sync(dev, &ref, true); +} +EXPORT_SYMBOL(debug_dma_sync_single_for_cpu); + +void debug_dma_sync_single_for_device(struct device *dev, + dma_addr_t dma_handle, size_t size, + int direction) +{ + struct dma_debug_entry ref; + + if (unlikely(dma_debug_disabled())) + return; + + ref.type = dma_debug_single; + ref.dev = dev; + ref.dev_addr = dma_handle; + ref.size = size; + ref.direction = direction; + ref.sg_call_ents = 0; + + check_sync(dev, &ref, false); +} +EXPORT_SYMBOL(debug_dma_sync_single_for_device); + +void debug_dma_sync_single_range_for_cpu(struct device *dev, + dma_addr_t dma_handle, + unsigned long offset, size_t size, + int direction) +{ + struct dma_debug_entry ref; + + if (unlikely(dma_debug_disabled())) + return; + + ref.type = dma_debug_single; + ref.dev = dev; + ref.dev_addr = dma_handle; + ref.size = offset + size; + ref.direction = direction; + ref.sg_call_ents = 0; + + check_sync(dev, &ref, true); +} +EXPORT_SYMBOL(debug_dma_sync_single_range_for_cpu); + +void debug_dma_sync_single_range_for_device(struct device *dev, + dma_addr_t dma_handle, + unsigned long offset, + size_t size, int direction) +{ + struct dma_debug_entry ref; + + if (unlikely(dma_debug_disabled())) + return; + + ref.type = dma_debug_single; + ref.dev = dev; + ref.dev_addr = dma_handle; + ref.size = offset + size; + ref.direction = direction; + ref.sg_call_ents = 0; + + check_sync(dev, &ref, false); +} +EXPORT_SYMBOL(debug_dma_sync_single_range_for_device); + +void debug_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, + int nelems, int direction) +{ + struct scatterlist *s; + int mapped_ents = 0, i; + + if (unlikely(dma_debug_disabled())) + return; + + for_each_sg(sg, s, nelems, i) { + + struct dma_debug_entry ref = { + .type = dma_debug_sg, + .dev = dev, + .pfn = page_to_pfn(sg_page(s)), + .offset = s->offset, + .dev_addr = sg_dma_address(s), + .size = sg_dma_len(s), + .direction = direction, + .sg_call_ents = nelems, + }; + + if (!i) + mapped_ents = get_nr_mapped_entries(dev, &ref); + + if (i >= mapped_ents) + break; + + check_sync(dev, &ref, true); + } +} +EXPORT_SYMBOL(debug_dma_sync_sg_for_cpu); + +void debug_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, + int nelems, int direction) +{ + struct scatterlist *s; + int mapped_ents = 0, i; + + if (unlikely(dma_debug_disabled())) + return; + + for_each_sg(sg, s, nelems, i) { + + struct dma_debug_entry ref = { + .type = dma_debug_sg, + .dev = dev, + .pfn = page_to_pfn(sg_page(s)), + .offset = s->offset, + .dev_addr = sg_dma_address(s), + .size = sg_dma_len(s), + .direction = direction, + .sg_call_ents = nelems, + }; + if (!i) + mapped_ents = get_nr_mapped_entries(dev, &ref); + + if (i >= mapped_ents) + break; + + check_sync(dev, &ref, false); + } +} +EXPORT_SYMBOL(debug_dma_sync_sg_for_device); + +static int __init dma_debug_driver_setup(char *str) +{ + int i; + + for (i = 0; i < NAME_MAX_LEN - 1; ++i, ++str) { + current_driver_name[i] = *str; + if (*str == 0) + break; + } + + if (current_driver_name[0]) + pr_info("DMA-API: enable driver filter for driver [%s]\n", + current_driver_name); + + + return 1; +} +__setup("dma_debug_driver=", dma_debug_driver_setup); diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c new file mode 100644 index 000000000000..8be8106270c2 --- /dev/null +++ b/kernel/dma/direct.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DMA operations that map physical memory directly without using an IOMMU or + * flushing caches. + */ +#include +#include +#include +#include +#include +#include +#include + +#define DIRECT_MAPPING_ERROR 0 + +/* + * Most architectures use ZONE_DMA for the first 16 Megabytes, but + * some use it for entirely different regions: + */ +#ifndef ARCH_ZONE_DMA_BITS +#define ARCH_ZONE_DMA_BITS 24 +#endif + +/* + * For AMD SEV all DMA must be to unencrypted addresses. + */ +static inline bool force_dma_unencrypted(void) +{ + return sev_active(); +} + +static bool +check_addr(struct device *dev, dma_addr_t dma_addr, size_t size, + const char *caller) +{ + if (unlikely(dev && !dma_capable(dev, dma_addr, size))) { + if (!dev->dma_mask) { + dev_err(dev, + "%s: call on device without dma_mask\n", + caller); + return false; + } + + if (*dev->dma_mask >= DMA_BIT_MASK(32)) { + dev_err(dev, + "%s: overflow %pad+%zu of device mask %llx\n", + caller, &dma_addr, size, *dev->dma_mask); + } + return false; + } + return true; +} + +static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size) +{ + dma_addr_t addr = force_dma_unencrypted() ? + __phys_to_dma(dev, phys) : phys_to_dma(dev, phys); + return addr + size - 1 <= dev->coherent_dma_mask; +} + +void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, + gfp_t gfp, unsigned long attrs) +{ + unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT; + int page_order = get_order(size); + struct page *page = NULL; + void *ret; + + /* we always manually zero the memory once we are done: */ + gfp &= ~__GFP_ZERO; + + /* GFP_DMA32 and GFP_DMA are no ops without the corresponding zones: */ + if (dev->coherent_dma_mask <= DMA_BIT_MASK(ARCH_ZONE_DMA_BITS)) + gfp |= GFP_DMA; + if (dev->coherent_dma_mask <= DMA_BIT_MASK(32) && !(gfp & GFP_DMA)) + gfp |= GFP_DMA32; + +again: + /* CMA can be used only in the context which permits sleeping */ + if (gfpflags_allow_blocking(gfp)) { + page = dma_alloc_from_contiguous(dev, count, page_order, gfp); + if (page && !dma_coherent_ok(dev, page_to_phys(page), size)) { + dma_release_from_contiguous(dev, page, count); + page = NULL; + } + } + if (!page) + page = alloc_pages_node(dev_to_node(dev), gfp, page_order); + + if (page && !dma_coherent_ok(dev, page_to_phys(page), size)) { + __free_pages(page, page_order); + page = NULL; + + if (IS_ENABLED(CONFIG_ZONE_DMA32) && + dev->coherent_dma_mask < DMA_BIT_MASK(64) && + !(gfp & (GFP_DMA32 | GFP_DMA))) { + gfp |= GFP_DMA32; + goto again; + } + + if (IS_ENABLED(CONFIG_ZONE_DMA) && + dev->coherent_dma_mask < DMA_BIT_MASK(32) && + !(gfp & GFP_DMA)) { + gfp = (gfp & ~GFP_DMA32) | GFP_DMA; + goto again; + } + } + + if (!page) + return NULL; + ret = page_address(page); + if (force_dma_unencrypted()) { + set_memory_decrypted((unsigned long)ret, 1 << page_order); + *dma_handle = __phys_to_dma(dev, page_to_phys(page)); + } else { + *dma_handle = phys_to_dma(dev, page_to_phys(page)); + } + memset(ret, 0, size); + return ret; +} + +/* + * NOTE: this function must never look at the dma_addr argument, because we want + * to be able to use it as a helper for iommu implementations as well. + */ +void dma_direct_free(struct device *dev, size_t size, void *cpu_addr, + dma_addr_t dma_addr, unsigned long attrs) +{ + unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT; + unsigned int page_order = get_order(size); + + if (force_dma_unencrypted()) + set_memory_encrypted((unsigned long)cpu_addr, 1 << page_order); + if (!dma_release_from_contiguous(dev, virt_to_page(cpu_addr), count)) + free_pages((unsigned long)cpu_addr, page_order); +} + +dma_addr_t dma_direct_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, enum dma_data_direction dir, + unsigned long attrs) +{ + dma_addr_t dma_addr = phys_to_dma(dev, page_to_phys(page)) + offset; + + if (!check_addr(dev, dma_addr, size, __func__)) + return DIRECT_MAPPING_ERROR; + return dma_addr; +} + +int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents, + enum dma_data_direction dir, unsigned long attrs) +{ + int i; + struct scatterlist *sg; + + for_each_sg(sgl, sg, nents, i) { + BUG_ON(!sg_page(sg)); + + sg_dma_address(sg) = phys_to_dma(dev, sg_phys(sg)); + if (!check_addr(dev, sg_dma_address(sg), sg->length, __func__)) + return 0; + sg_dma_len(sg) = sg->length; + } + + return nents; +} + +int dma_direct_supported(struct device *dev, u64 mask) +{ +#ifdef CONFIG_ZONE_DMA + if (mask < DMA_BIT_MASK(ARCH_ZONE_DMA_BITS)) + return 0; +#else + /* + * Because 32-bit DMA masks are so common we expect every architecture + * to be able to satisfy them - either by not supporting more physical + * memory, or by providing a ZONE_DMA32. If neither is the case, the + * architecture needs to use an IOMMU instead of the direct mapping. + */ + if (mask < DMA_BIT_MASK(32)) + return 0; +#endif + /* + * Various PCI/PCIe bridges have broken support for > 32bit DMA even + * if the device itself might support it. + */ + if (dev->dma_32bit_limit && mask > DMA_BIT_MASK(32)) + return 0; + return 1; +} + +int dma_direct_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ + return dma_addr == DIRECT_MAPPING_ERROR; +} + +const struct dma_map_ops dma_direct_ops = { + .alloc = dma_direct_alloc, + .free = dma_direct_free, + .map_page = dma_direct_map_page, + .map_sg = dma_direct_map_sg, + .dma_supported = dma_direct_supported, + .mapping_error = dma_direct_mapping_error, +}; +EXPORT_SYMBOL(dma_direct_ops); diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c new file mode 100644 index 000000000000..d2a92ddaac4d --- /dev/null +++ b/kernel/dma/mapping.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * arch-independent dma-mapping routines + * + * Copyright (c) 2006 SUSE Linux Products GmbH + * Copyright (c) 2006 Tejun Heo + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Managed DMA API + */ +struct dma_devres { + size_t size; + void *vaddr; + dma_addr_t dma_handle; + unsigned long attrs; +}; + +static void dmam_release(struct device *dev, void *res) +{ + struct dma_devres *this = res; + + dma_free_attrs(dev, this->size, this->vaddr, this->dma_handle, + this->attrs); +} + +static int dmam_match(struct device *dev, void *res, void *match_data) +{ + struct dma_devres *this = res, *match = match_data; + + if (this->vaddr == match->vaddr) { + WARN_ON(this->size != match->size || + this->dma_handle != match->dma_handle); + return 1; + } + return 0; +} + +/** + * dmam_alloc_coherent - Managed dma_alloc_coherent() + * @dev: Device to allocate coherent memory for + * @size: Size of allocation + * @dma_handle: Out argument for allocated DMA handle + * @gfp: Allocation flags + * + * Managed dma_alloc_coherent(). Memory allocated using this function + * will be automatically released on driver detach. + * + * RETURNS: + * Pointer to allocated memory on success, NULL on failure. + */ +void *dmam_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp) +{ + struct dma_devres *dr; + void *vaddr; + + dr = devres_alloc(dmam_release, sizeof(*dr), gfp); + if (!dr) + return NULL; + + vaddr = dma_alloc_coherent(dev, size, dma_handle, gfp); + if (!vaddr) { + devres_free(dr); + return NULL; + } + + dr->vaddr = vaddr; + dr->dma_handle = *dma_handle; + dr->size = size; + + devres_add(dev, dr); + + return vaddr; +} +EXPORT_SYMBOL(dmam_alloc_coherent); + +/** + * dmam_free_coherent - Managed dma_free_coherent() + * @dev: Device to free coherent memory for + * @size: Size of allocation + * @vaddr: Virtual address of the memory to free + * @dma_handle: DMA handle of the memory to free + * + * Managed dma_free_coherent(). + */ +void dmam_free_coherent(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_handle) +{ + struct dma_devres match_data = { size, vaddr, dma_handle }; + + dma_free_coherent(dev, size, vaddr, dma_handle); + WARN_ON(devres_destroy(dev, dmam_release, dmam_match, &match_data)); +} +EXPORT_SYMBOL(dmam_free_coherent); + +/** + * dmam_alloc_attrs - Managed dma_alloc_attrs() + * @dev: Device to allocate non_coherent memory for + * @size: Size of allocation + * @dma_handle: Out argument for allocated DMA handle + * @gfp: Allocation flags + * @attrs: Flags in the DMA_ATTR_* namespace. + * + * Managed dma_alloc_attrs(). Memory allocated using this function will be + * automatically released on driver detach. + * + * RETURNS: + * Pointer to allocated memory on success, NULL on failure. + */ +void *dmam_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle, + gfp_t gfp, unsigned long attrs) +{ + struct dma_devres *dr; + void *vaddr; + + dr = devres_alloc(dmam_release, sizeof(*dr), gfp); + if (!dr) + return NULL; + + vaddr = dma_alloc_attrs(dev, size, dma_handle, gfp, attrs); + if (!vaddr) { + devres_free(dr); + return NULL; + } + + dr->vaddr = vaddr; + dr->dma_handle = *dma_handle; + dr->size = size; + dr->attrs = attrs; + + devres_add(dev, dr); + + return vaddr; +} +EXPORT_SYMBOL(dmam_alloc_attrs); + +#ifdef CONFIG_HAVE_GENERIC_DMA_COHERENT + +static void dmam_coherent_decl_release(struct device *dev, void *res) +{ + dma_release_declared_memory(dev); +} + +/** + * dmam_declare_coherent_memory - Managed dma_declare_coherent_memory() + * @dev: Device to declare coherent memory for + * @phys_addr: Physical address of coherent memory to be declared + * @device_addr: Device address of coherent memory to be declared + * @size: Size of coherent memory to be declared + * @flags: Flags + * + * Managed dma_declare_coherent_memory(). + * + * RETURNS: + * 0 on success, -errno on failure. + */ +int dmam_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, + dma_addr_t device_addr, size_t size, int flags) +{ + void *res; + int rc; + + res = devres_alloc(dmam_coherent_decl_release, 0, GFP_KERNEL); + if (!res) + return -ENOMEM; + + rc = dma_declare_coherent_memory(dev, phys_addr, device_addr, size, + flags); + if (!rc) + devres_add(dev, res); + else + devres_free(res); + + return rc; +} +EXPORT_SYMBOL(dmam_declare_coherent_memory); + +/** + * dmam_release_declared_memory - Managed dma_release_declared_memory(). + * @dev: Device to release declared coherent memory for + * + * Managed dmam_release_declared_memory(). + */ +void dmam_release_declared_memory(struct device *dev) +{ + WARN_ON(devres_destroy(dev, dmam_coherent_decl_release, NULL, NULL)); +} +EXPORT_SYMBOL(dmam_release_declared_memory); + +#endif + +/* + * Create scatter-list for the already allocated DMA buffer. + */ +int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t handle, size_t size) +{ + struct page *page = virt_to_page(cpu_addr); + int ret; + + ret = sg_alloc_table(sgt, 1, GFP_KERNEL); + if (unlikely(ret)) + return ret; + + sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0); + return 0; +} +EXPORT_SYMBOL(dma_common_get_sgtable); + +/* + * Create userspace mapping for the DMA-coherent memory. + */ +int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size) +{ + int ret = -ENXIO; +#ifndef CONFIG_ARCH_NO_COHERENT_DMA_MMAP + unsigned long user_count = vma_pages(vma); + unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT; + unsigned long off = vma->vm_pgoff; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + if (dma_mmap_from_dev_coherent(dev, vma, cpu_addr, size, &ret)) + return ret; + + if (off < count && user_count <= (count - off)) + ret = remap_pfn_range(vma, vma->vm_start, + page_to_pfn(virt_to_page(cpu_addr)) + off, + user_count << PAGE_SHIFT, + vma->vm_page_prot); +#endif /* !CONFIG_ARCH_NO_COHERENT_DMA_MMAP */ + + return ret; +} +EXPORT_SYMBOL(dma_common_mmap); + +#ifdef CONFIG_MMU +static struct vm_struct *__dma_common_pages_remap(struct page **pages, + size_t size, unsigned long vm_flags, pgprot_t prot, + const void *caller) +{ + struct vm_struct *area; + + area = get_vm_area_caller(size, vm_flags, caller); + if (!area) + return NULL; + + if (map_vm_area(area, prot, pages)) { + vunmap(area->addr); + return NULL; + } + + return area; +} + +/* + * remaps an array of PAGE_SIZE pages into another vm_area + * Cannot be used in non-sleeping contexts + */ +void *dma_common_pages_remap(struct page **pages, size_t size, + unsigned long vm_flags, pgprot_t prot, + const void *caller) +{ + struct vm_struct *area; + + area = __dma_common_pages_remap(pages, size, vm_flags, prot, caller); + if (!area) + return NULL; + + area->pages = pages; + + return area->addr; +} + +/* + * remaps an allocated contiguous region into another vm_area. + * Cannot be used in non-sleeping contexts + */ + +void *dma_common_contiguous_remap(struct page *page, size_t size, + unsigned long vm_flags, + pgprot_t prot, const void *caller) +{ + int i; + struct page **pages; + struct vm_struct *area; + + pages = kmalloc(sizeof(struct page *) << get_order(size), GFP_KERNEL); + if (!pages) + return NULL; + + for (i = 0; i < (size >> PAGE_SHIFT); i++) + pages[i] = nth_page(page, i); + + area = __dma_common_pages_remap(pages, size, vm_flags, prot, caller); + + kfree(pages); + + if (!area) + return NULL; + return area->addr; +} + +/* + * unmaps a range previously mapped by dma_common_*_remap + */ +void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags) +{ + struct vm_struct *area = find_vm_area(cpu_addr); + + if (!area || (area->flags & vm_flags) != vm_flags) { + WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr); + return; + } + + unmap_kernel_range((unsigned long)cpu_addr, PAGE_ALIGN(size)); + vunmap(cpu_addr); +} +#endif + +/* + * enables DMA API use for a device + */ +int dma_configure(struct device *dev) +{ + if (dev->bus->dma_configure) + return dev->bus->dma_configure(dev); + return 0; +} + +void dma_deconfigure(struct device *dev) +{ + of_dma_deconfigure(dev); + acpi_dma_deconfigure(dev); +} diff --git a/kernel/dma/noncoherent.c b/kernel/dma/noncoherent.c new file mode 100644 index 000000000000..79e9a757387f --- /dev/null +++ b/kernel/dma/noncoherent.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Christoph Hellwig. + * + * DMA operations that map physical memory directly without providing cache + * coherence. + */ +#include +#include +#include +#include +#include + +static void dma_noncoherent_sync_single_for_device(struct device *dev, + dma_addr_t addr, size_t size, enum dma_data_direction dir) +{ + arch_sync_dma_for_device(dev, dma_to_phys(dev, addr), size, dir); +} + +static void dma_noncoherent_sync_sg_for_device(struct device *dev, + struct scatterlist *sgl, int nents, enum dma_data_direction dir) +{ + struct scatterlist *sg; + int i; + + for_each_sg(sgl, sg, nents, i) + arch_sync_dma_for_device(dev, sg_phys(sg), sg->length, dir); +} + +static dma_addr_t dma_noncoherent_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, enum dma_data_direction dir, + unsigned long attrs) +{ + dma_addr_t addr; + + addr = dma_direct_map_page(dev, page, offset, size, dir, attrs); + if (!dma_mapping_error(dev, addr) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) + arch_sync_dma_for_device(dev, page_to_phys(page) + offset, + size, dir); + return addr; +} + +static int dma_noncoherent_map_sg(struct device *dev, struct scatterlist *sgl, + int nents, enum dma_data_direction dir, unsigned long attrs) +{ + nents = dma_direct_map_sg(dev, sgl, nents, dir, attrs); + if (nents > 0 && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) + dma_noncoherent_sync_sg_for_device(dev, sgl, nents, dir); + return nents; +} + +#ifdef CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU +static void dma_noncoherent_sync_single_for_cpu(struct device *dev, + dma_addr_t addr, size_t size, enum dma_data_direction dir) +{ + arch_sync_dma_for_cpu(dev, dma_to_phys(dev, addr), size, dir); +} + +static void dma_noncoherent_sync_sg_for_cpu(struct device *dev, + struct scatterlist *sgl, int nents, enum dma_data_direction dir) +{ + struct scatterlist *sg; + int i; + + for_each_sg(sgl, sg, nents, i) + arch_sync_dma_for_cpu(dev, sg_phys(sg), sg->length, dir); +} + +static void dma_noncoherent_unmap_page(struct device *dev, dma_addr_t addr, + size_t size, enum dma_data_direction dir, unsigned long attrs) +{ + if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC)) + dma_noncoherent_sync_single_for_cpu(dev, addr, size, dir); +} + +static void dma_noncoherent_unmap_sg(struct device *dev, struct scatterlist *sgl, + int nents, enum dma_data_direction dir, unsigned long attrs) +{ + if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC)) + dma_noncoherent_sync_sg_for_cpu(dev, sgl, nents, dir); +} +#endif + +const struct dma_map_ops dma_noncoherent_ops = { + .alloc = arch_dma_alloc, + .free = arch_dma_free, + .mmap = arch_dma_mmap, + .sync_single_for_device = dma_noncoherent_sync_single_for_device, + .sync_sg_for_device = dma_noncoherent_sync_sg_for_device, + .map_page = dma_noncoherent_map_page, + .map_sg = dma_noncoherent_map_sg, +#ifdef CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU + .sync_single_for_cpu = dma_noncoherent_sync_single_for_cpu, + .sync_sg_for_cpu = dma_noncoherent_sync_sg_for_cpu, + .unmap_page = dma_noncoherent_unmap_page, + .unmap_sg = dma_noncoherent_unmap_sg, +#endif + .dma_supported = dma_direct_supported, + .mapping_error = dma_direct_mapping_error, + .cache_sync = arch_dma_cache_sync, +}; +EXPORT_SYMBOL(dma_noncoherent_ops); diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c new file mode 100644 index 000000000000..04b68d9dffac --- /dev/null +++ b/kernel/dma/swiotlb.c @@ -0,0 +1,1087 @@ +/* + * Dynamic DMA mapping support. + * + * This implementation is a fallback for platforms that do not support + * I/O TLBs (aka DMA address translation hardware). + * Copyright (C) 2000 Asit Mallick + * Copyright (C) 2000 Goutham Rao + * Copyright (C) 2000, 2003 Hewlett-Packard Co + * David Mosberger-Tang + * + * 03/05/07 davidm Switch from PCI-DMA to generic device DMA API. + * 00/12/13 davidm Rename to swiotlb.c and add mark_clean() to avoid + * unnecessary i-cache flushing. + * 04/07/.. ak Better overflow handling. Assorted fixes. + * 05/09/10 linville Add support for syncing ranges, support syncing for + * DMA_BIDIRECTIONAL mappings, miscellaneous cleanup. + * 08/12/11 beckyb Add highmem support + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include + +#define OFFSET(val,align) ((unsigned long) \ + ( (val) & ( (align) - 1))) + +#define SLABS_PER_PAGE (1 << (PAGE_SHIFT - IO_TLB_SHIFT)) + +/* + * Minimum IO TLB size to bother booting with. Systems with mainly + * 64bit capable cards will only lightly use the swiotlb. If we can't + * allocate a contiguous 1MB, we're probably in trouble anyway. + */ +#define IO_TLB_MIN_SLABS ((1<<20) >> IO_TLB_SHIFT) + +enum swiotlb_force swiotlb_force; + +/* + * Used to do a quick range check in swiotlb_tbl_unmap_single and + * swiotlb_tbl_sync_single_*, to see if the memory was in fact allocated by this + * API. + */ +static phys_addr_t io_tlb_start, io_tlb_end; + +/* + * The number of IO TLB blocks (in groups of 64) between io_tlb_start and + * io_tlb_end. This is command line adjustable via setup_io_tlb_npages. + */ +static unsigned long io_tlb_nslabs; + +/* + * When the IOMMU overflows we return a fallback buffer. This sets the size. + */ +static unsigned long io_tlb_overflow = 32*1024; + +static phys_addr_t io_tlb_overflow_buffer; + +/* + * This is a free list describing the number of free entries available from + * each index + */ +static unsigned int *io_tlb_list; +static unsigned int io_tlb_index; + +/* + * Max segment that we can provide which (if pages are contingous) will + * not be bounced (unless SWIOTLB_FORCE is set). + */ +unsigned int max_segment; + +/* + * We need to save away the original address corresponding to a mapped entry + * for the sync operations. + */ +#define INVALID_PHYS_ADDR (~(phys_addr_t)0) +static phys_addr_t *io_tlb_orig_addr; + +/* + * Protect the above data structures in the map and unmap calls + */ +static DEFINE_SPINLOCK(io_tlb_lock); + +static int late_alloc; + +static int __init +setup_io_tlb_npages(char *str) +{ + if (isdigit(*str)) { + io_tlb_nslabs = simple_strtoul(str, &str, 0); + /* avoid tail segment of size < IO_TLB_SEGSIZE */ + io_tlb_nslabs = ALIGN(io_tlb_nslabs, IO_TLB_SEGSIZE); + } + if (*str == ',') + ++str; + if (!strcmp(str, "force")) { + swiotlb_force = SWIOTLB_FORCE; + } else if (!strcmp(str, "noforce")) { + swiotlb_force = SWIOTLB_NO_FORCE; + io_tlb_nslabs = 1; + } + + return 0; +} +early_param("swiotlb", setup_io_tlb_npages); +/* make io_tlb_overflow tunable too? */ + +unsigned long swiotlb_nr_tbl(void) +{ + return io_tlb_nslabs; +} +EXPORT_SYMBOL_GPL(swiotlb_nr_tbl); + +unsigned int swiotlb_max_segment(void) +{ + return max_segment; +} +EXPORT_SYMBOL_GPL(swiotlb_max_segment); + +void swiotlb_set_max_segment(unsigned int val) +{ + if (swiotlb_force == SWIOTLB_FORCE) + max_segment = 1; + else + max_segment = rounddown(val, PAGE_SIZE); +} + +/* default to 64MB */ +#define IO_TLB_DEFAULT_SIZE (64UL<<20) +unsigned long swiotlb_size_or_default(void) +{ + unsigned long size; + + size = io_tlb_nslabs << IO_TLB_SHIFT; + + return size ? size : (IO_TLB_DEFAULT_SIZE); +} + +static bool no_iotlb_memory; + +void swiotlb_print_info(void) +{ + unsigned long bytes = io_tlb_nslabs << IO_TLB_SHIFT; + unsigned char *vstart, *vend; + + if (no_iotlb_memory) { + pr_warn("software IO TLB: No low mem\n"); + return; + } + + vstart = phys_to_virt(io_tlb_start); + vend = phys_to_virt(io_tlb_end); + + printk(KERN_INFO "software IO TLB [mem %#010llx-%#010llx] (%luMB) mapped at [%p-%p]\n", + (unsigned long long)io_tlb_start, + (unsigned long long)io_tlb_end, + bytes >> 20, vstart, vend - 1); +} + +/* + * Early SWIOTLB allocation may be too early to allow an architecture to + * perform the desired operations. This function allows the architecture to + * call SWIOTLB when the operations are possible. It needs to be called + * before the SWIOTLB memory is used. + */ +void __init swiotlb_update_mem_attributes(void) +{ + void *vaddr; + unsigned long bytes; + + if (no_iotlb_memory || late_alloc) + return; + + vaddr = phys_to_virt(io_tlb_start); + bytes = PAGE_ALIGN(io_tlb_nslabs << IO_TLB_SHIFT); + set_memory_decrypted((unsigned long)vaddr, bytes >> PAGE_SHIFT); + memset(vaddr, 0, bytes); + + vaddr = phys_to_virt(io_tlb_overflow_buffer); + bytes = PAGE_ALIGN(io_tlb_overflow); + set_memory_decrypted((unsigned long)vaddr, bytes >> PAGE_SHIFT); + memset(vaddr, 0, bytes); +} + +int __init swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int verbose) +{ + void *v_overflow_buffer; + unsigned long i, bytes; + + bytes = nslabs << IO_TLB_SHIFT; + + io_tlb_nslabs = nslabs; + io_tlb_start = __pa(tlb); + io_tlb_end = io_tlb_start + bytes; + + /* + * Get the overflow emergency buffer + */ + v_overflow_buffer = memblock_virt_alloc_low_nopanic( + PAGE_ALIGN(io_tlb_overflow), + PAGE_SIZE); + if (!v_overflow_buffer) + return -ENOMEM; + + io_tlb_overflow_buffer = __pa(v_overflow_buffer); + + /* + * Allocate and initialize the free list array. This array is used + * to find contiguous free memory regions of size up to IO_TLB_SEGSIZE + * between io_tlb_start and io_tlb_end. + */ + io_tlb_list = memblock_virt_alloc( + PAGE_ALIGN(io_tlb_nslabs * sizeof(int)), + PAGE_SIZE); + io_tlb_orig_addr = memblock_virt_alloc( + PAGE_ALIGN(io_tlb_nslabs * sizeof(phys_addr_t)), + PAGE_SIZE); + for (i = 0; i < io_tlb_nslabs; i++) { + io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE); + io_tlb_orig_addr[i] = INVALID_PHYS_ADDR; + } + io_tlb_index = 0; + + if (verbose) + swiotlb_print_info(); + + swiotlb_set_max_segment(io_tlb_nslabs << IO_TLB_SHIFT); + return 0; +} + +/* + * Statically reserve bounce buffer space and initialize bounce buffer data + * structures for the software IO TLB used to implement the DMA API. + */ +void __init +swiotlb_init(int verbose) +{ + size_t default_size = IO_TLB_DEFAULT_SIZE; + unsigned char *vstart; + unsigned long bytes; + + if (!io_tlb_nslabs) { + io_tlb_nslabs = (default_size >> IO_TLB_SHIFT); + io_tlb_nslabs = ALIGN(io_tlb_nslabs, IO_TLB_SEGSIZE); + } + + bytes = io_tlb_nslabs << IO_TLB_SHIFT; + + /* Get IO TLB memory from the low pages */ + vstart = memblock_virt_alloc_low_nopanic(PAGE_ALIGN(bytes), PAGE_SIZE); + if (vstart && !swiotlb_init_with_tbl(vstart, io_tlb_nslabs, verbose)) + return; + + if (io_tlb_start) + memblock_free_early(io_tlb_start, + PAGE_ALIGN(io_tlb_nslabs << IO_TLB_SHIFT)); + pr_warn("Cannot allocate SWIOTLB buffer"); + no_iotlb_memory = true; +} + +/* + * Systems with larger DMA zones (those that don't support ISA) can + * initialize the swiotlb later using the slab allocator if needed. + * This should be just like above, but with some error catching. + */ +int +swiotlb_late_init_with_default_size(size_t default_size) +{ + unsigned long bytes, req_nslabs = io_tlb_nslabs; + unsigned char *vstart = NULL; + unsigned int order; + int rc = 0; + + if (!io_tlb_nslabs) { + io_tlb_nslabs = (default_size >> IO_TLB_SHIFT); + io_tlb_nslabs = ALIGN(io_tlb_nslabs, IO_TLB_SEGSIZE); + } + + /* + * Get IO TLB memory from the low pages + */ + order = get_order(io_tlb_nslabs << IO_TLB_SHIFT); + io_tlb_nslabs = SLABS_PER_PAGE << order; + bytes = io_tlb_nslabs << IO_TLB_SHIFT; + + while ((SLABS_PER_PAGE << order) > IO_TLB_MIN_SLABS) { + vstart = (void *)__get_free_pages(GFP_DMA | __GFP_NOWARN, + order); + if (vstart) + break; + order--; + } + + if (!vstart) { + io_tlb_nslabs = req_nslabs; + return -ENOMEM; + } + if (order != get_order(bytes)) { + printk(KERN_WARNING "Warning: only able to allocate %ld MB " + "for software IO TLB\n", (PAGE_SIZE << order) >> 20); + io_tlb_nslabs = SLABS_PER_PAGE << order; + } + rc = swiotlb_late_init_with_tbl(vstart, io_tlb_nslabs); + if (rc) + free_pages((unsigned long)vstart, order); + + return rc; +} + +int +swiotlb_late_init_with_tbl(char *tlb, unsigned long nslabs) +{ + unsigned long i, bytes; + unsigned char *v_overflow_buffer; + + bytes = nslabs << IO_TLB_SHIFT; + + io_tlb_nslabs = nslabs; + io_tlb_start = virt_to_phys(tlb); + io_tlb_end = io_tlb_start + bytes; + + set_memory_decrypted((unsigned long)tlb, bytes >> PAGE_SHIFT); + memset(tlb, 0, bytes); + + /* + * Get the overflow emergency buffer + */ + v_overflow_buffer = (void *)__get_free_pages(GFP_DMA, + get_order(io_tlb_overflow)); + if (!v_overflow_buffer) + goto cleanup2; + + set_memory_decrypted((unsigned long)v_overflow_buffer, + io_tlb_overflow >> PAGE_SHIFT); + memset(v_overflow_buffer, 0, io_tlb_overflow); + io_tlb_overflow_buffer = virt_to_phys(v_overflow_buffer); + + /* + * Allocate and initialize the free list array. This array is used + * to find contiguous free memory regions of size up to IO_TLB_SEGSIZE + * between io_tlb_start and io_tlb_end. + */ + io_tlb_list = (unsigned int *)__get_free_pages(GFP_KERNEL, + get_order(io_tlb_nslabs * sizeof(int))); + if (!io_tlb_list) + goto cleanup3; + + io_tlb_orig_addr = (phys_addr_t *) + __get_free_pages(GFP_KERNEL, + get_order(io_tlb_nslabs * + sizeof(phys_addr_t))); + if (!io_tlb_orig_addr) + goto cleanup4; + + for (i = 0; i < io_tlb_nslabs; i++) { + io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE); + io_tlb_orig_addr[i] = INVALID_PHYS_ADDR; + } + io_tlb_index = 0; + + swiotlb_print_info(); + + late_alloc = 1; + + swiotlb_set_max_segment(io_tlb_nslabs << IO_TLB_SHIFT); + + return 0; + +cleanup4: + free_pages((unsigned long)io_tlb_list, get_order(io_tlb_nslabs * + sizeof(int))); + io_tlb_list = NULL; +cleanup3: + free_pages((unsigned long)v_overflow_buffer, + get_order(io_tlb_overflow)); + io_tlb_overflow_buffer = 0; +cleanup2: + io_tlb_end = 0; + io_tlb_start = 0; + io_tlb_nslabs = 0; + max_segment = 0; + return -ENOMEM; +} + +void __init swiotlb_exit(void) +{ + if (!io_tlb_orig_addr) + return; + + if (late_alloc) { + free_pages((unsigned long)phys_to_virt(io_tlb_overflow_buffer), + get_order(io_tlb_overflow)); + free_pages((unsigned long)io_tlb_orig_addr, + get_order(io_tlb_nslabs * sizeof(phys_addr_t))); + free_pages((unsigned long)io_tlb_list, get_order(io_tlb_nslabs * + sizeof(int))); + free_pages((unsigned long)phys_to_virt(io_tlb_start), + get_order(io_tlb_nslabs << IO_TLB_SHIFT)); + } else { + memblock_free_late(io_tlb_overflow_buffer, + PAGE_ALIGN(io_tlb_overflow)); + memblock_free_late(__pa(io_tlb_orig_addr), + PAGE_ALIGN(io_tlb_nslabs * sizeof(phys_addr_t))); + memblock_free_late(__pa(io_tlb_list), + PAGE_ALIGN(io_tlb_nslabs * sizeof(int))); + memblock_free_late(io_tlb_start, + PAGE_ALIGN(io_tlb_nslabs << IO_TLB_SHIFT)); + } + io_tlb_nslabs = 0; + max_segment = 0; +} + +int is_swiotlb_buffer(phys_addr_t paddr) +{ + return paddr >= io_tlb_start && paddr < io_tlb_end; +} + +/* + * Bounce: copy the swiotlb buffer back to the original dma location + */ +static void swiotlb_bounce(phys_addr_t orig_addr, phys_addr_t tlb_addr, + size_t size, enum dma_data_direction dir) +{ + unsigned long pfn = PFN_DOWN(orig_addr); + unsigned char *vaddr = phys_to_virt(tlb_addr); + + if (PageHighMem(pfn_to_page(pfn))) { + /* The buffer does not have a mapping. Map it in and copy */ + unsigned int offset = orig_addr & ~PAGE_MASK; + char *buffer; + unsigned int sz = 0; + unsigned long flags; + + while (size) { + sz = min_t(size_t, PAGE_SIZE - offset, size); + + local_irq_save(flags); + buffer = kmap_atomic(pfn_to_page(pfn)); + if (dir == DMA_TO_DEVICE) + memcpy(vaddr, buffer + offset, sz); + else + memcpy(buffer + offset, vaddr, sz); + kunmap_atomic(buffer); + local_irq_restore(flags); + + size -= sz; + pfn++; + vaddr += sz; + offset = 0; + } + } else if (dir == DMA_TO_DEVICE) { + memcpy(vaddr, phys_to_virt(orig_addr), size); + } else { + memcpy(phys_to_virt(orig_addr), vaddr, size); + } +} + +phys_addr_t swiotlb_tbl_map_single(struct device *hwdev, + dma_addr_t tbl_dma_addr, + phys_addr_t orig_addr, size_t size, + enum dma_data_direction dir, + unsigned long attrs) +{ + unsigned long flags; + phys_addr_t tlb_addr; + unsigned int nslots, stride, index, wrap; + int i; + unsigned long mask; + unsigned long offset_slots; + unsigned long max_slots; + + if (no_iotlb_memory) + panic("Can not allocate SWIOTLB buffer earlier and can't now provide you with the DMA bounce buffer"); + + if (mem_encrypt_active()) + pr_warn_once("%s is active and system is using DMA bounce buffers\n", + sme_active() ? "SME" : "SEV"); + + mask = dma_get_seg_boundary(hwdev); + + tbl_dma_addr &= mask; + + offset_slots = ALIGN(tbl_dma_addr, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT; + + /* + * Carefully handle integer overflow which can occur when mask == ~0UL. + */ + max_slots = mask + 1 + ? ALIGN(mask + 1, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT + : 1UL << (BITS_PER_LONG - IO_TLB_SHIFT); + + /* + * For mappings greater than or equal to a page, we limit the stride + * (and hence alignment) to a page size. + */ + nslots = ALIGN(size, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT; + if (size >= PAGE_SIZE) + stride = (1 << (PAGE_SHIFT - IO_TLB_SHIFT)); + else + stride = 1; + + BUG_ON(!nslots); + + /* + * Find suitable number of IO TLB entries size that will fit this + * request and allocate a buffer from that IO TLB pool. + */ + spin_lock_irqsave(&io_tlb_lock, flags); + index = ALIGN(io_tlb_index, stride); + if (index >= io_tlb_nslabs) + index = 0; + wrap = index; + + do { + while (iommu_is_span_boundary(index, nslots, offset_slots, + max_slots)) { + index += stride; + if (index >= io_tlb_nslabs) + index = 0; + if (index == wrap) + goto not_found; + } + + /* + * If we find a slot that indicates we have 'nslots' number of + * contiguous buffers, we allocate the buffers from that slot + * and mark the entries as '0' indicating unavailable. + */ + if (io_tlb_list[index] >= nslots) { + int count = 0; + + for (i = index; i < (int) (index + nslots); i++) + io_tlb_list[i] = 0; + for (i = index - 1; (OFFSET(i, IO_TLB_SEGSIZE) != IO_TLB_SEGSIZE - 1) && io_tlb_list[i]; i--) + io_tlb_list[i] = ++count; + tlb_addr = io_tlb_start + (index << IO_TLB_SHIFT); + + /* + * Update the indices to avoid searching in the next + * round. + */ + io_tlb_index = ((index + nslots) < io_tlb_nslabs + ? (index + nslots) : 0); + + goto found; + } + index += stride; + if (index >= io_tlb_nslabs) + index = 0; + } while (index != wrap); + +not_found: + spin_unlock_irqrestore(&io_tlb_lock, flags); + if (!(attrs & DMA_ATTR_NO_WARN) && printk_ratelimit()) + dev_warn(hwdev, "swiotlb buffer is full (sz: %zd bytes)\n", size); + return SWIOTLB_MAP_ERROR; +found: + spin_unlock_irqrestore(&io_tlb_lock, flags); + + /* + * Save away the mapping from the original address to the DMA address. + * This is needed when we sync the memory. Then we sync the buffer if + * needed. + */ + for (i = 0; i < nslots; i++) + io_tlb_orig_addr[index+i] = orig_addr + (i << IO_TLB_SHIFT); + if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) && + (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)) + swiotlb_bounce(orig_addr, tlb_addr, size, DMA_TO_DEVICE); + + return tlb_addr; +} + +/* + * Allocates bounce buffer and returns its physical address. + */ +static phys_addr_t +map_single(struct device *hwdev, phys_addr_t phys, size_t size, + enum dma_data_direction dir, unsigned long attrs) +{ + dma_addr_t start_dma_addr; + + if (swiotlb_force == SWIOTLB_NO_FORCE) { + dev_warn_ratelimited(hwdev, "Cannot do DMA to address %pa\n", + &phys); + return SWIOTLB_MAP_ERROR; + } + + start_dma_addr = __phys_to_dma(hwdev, io_tlb_start); + return swiotlb_tbl_map_single(hwdev, start_dma_addr, phys, size, + dir, attrs); +} + +/* + * tlb_addr is the physical address of the bounce buffer to unmap. + */ +void swiotlb_tbl_unmap_single(struct device *hwdev, phys_addr_t tlb_addr, + size_t size, enum dma_data_direction dir, + unsigned long attrs) +{ + unsigned long flags; + int i, count, nslots = ALIGN(size, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT; + int index = (tlb_addr - io_tlb_start) >> IO_TLB_SHIFT; + phys_addr_t orig_addr = io_tlb_orig_addr[index]; + + /* + * First, sync the memory before unmapping the entry + */ + if (orig_addr != INVALID_PHYS_ADDR && + !(attrs & DMA_ATTR_SKIP_CPU_SYNC) && + ((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL))) + swiotlb_bounce(orig_addr, tlb_addr, size, DMA_FROM_DEVICE); + + /* + * Return the buffer to the free list by setting the corresponding + * entries to indicate the number of contiguous entries available. + * While returning the entries to the free list, we merge the entries + * with slots below and above the pool being returned. + */ + spin_lock_irqsave(&io_tlb_lock, flags); + { + count = ((index + nslots) < ALIGN(index + 1, IO_TLB_SEGSIZE) ? + io_tlb_list[index + nslots] : 0); + /* + * Step 1: return the slots to the free list, merging the + * slots with superceeding slots + */ + for (i = index + nslots - 1; i >= index; i--) { + io_tlb_list[i] = ++count; + io_tlb_orig_addr[i] = INVALID_PHYS_ADDR; + } + /* + * Step 2: merge the returned slots with the preceding slots, + * if available (non zero) + */ + for (i = index - 1; (OFFSET(i, IO_TLB_SEGSIZE) != IO_TLB_SEGSIZE -1) && io_tlb_list[i]; i--) + io_tlb_list[i] = ++count; + } + spin_unlock_irqrestore(&io_tlb_lock, flags); +} + +void swiotlb_tbl_sync_single(struct device *hwdev, phys_addr_t tlb_addr, + size_t size, enum dma_data_direction dir, + enum dma_sync_target target) +{ + int index = (tlb_addr - io_tlb_start) >> IO_TLB_SHIFT; + phys_addr_t orig_addr = io_tlb_orig_addr[index]; + + if (orig_addr == INVALID_PHYS_ADDR) + return; + orig_addr += (unsigned long)tlb_addr & ((1 << IO_TLB_SHIFT) - 1); + + switch (target) { + case SYNC_FOR_CPU: + if (likely(dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL)) + swiotlb_bounce(orig_addr, tlb_addr, + size, DMA_FROM_DEVICE); + else + BUG_ON(dir != DMA_TO_DEVICE); + break; + case SYNC_FOR_DEVICE: + if (likely(dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)) + swiotlb_bounce(orig_addr, tlb_addr, + size, DMA_TO_DEVICE); + else + BUG_ON(dir != DMA_FROM_DEVICE); + break; + default: + BUG(); + } +} + +static inline bool dma_coherent_ok(struct device *dev, dma_addr_t addr, + size_t size) +{ + u64 mask = DMA_BIT_MASK(32); + + if (dev && dev->coherent_dma_mask) + mask = dev->coherent_dma_mask; + return addr + size - 1 <= mask; +} + +static void * +swiotlb_alloc_buffer(struct device *dev, size_t size, dma_addr_t *dma_handle, + unsigned long attrs) +{ + phys_addr_t phys_addr; + + if (swiotlb_force == SWIOTLB_NO_FORCE) + goto out_warn; + + phys_addr = swiotlb_tbl_map_single(dev, + __phys_to_dma(dev, io_tlb_start), + 0, size, DMA_FROM_DEVICE, attrs); + if (phys_addr == SWIOTLB_MAP_ERROR) + goto out_warn; + + *dma_handle = __phys_to_dma(dev, phys_addr); + if (!dma_coherent_ok(dev, *dma_handle, size)) + goto out_unmap; + + memset(phys_to_virt(phys_addr), 0, size); + return phys_to_virt(phys_addr); + +out_unmap: + dev_warn(dev, "hwdev DMA mask = 0x%016Lx, dev_addr = 0x%016Lx\n", + (unsigned long long)dev->coherent_dma_mask, + (unsigned long long)*dma_handle); + + /* + * DMA_TO_DEVICE to avoid memcpy in unmap_single. + * DMA_ATTR_SKIP_CPU_SYNC is optional. + */ + swiotlb_tbl_unmap_single(dev, phys_addr, size, DMA_TO_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); +out_warn: + if (!(attrs & DMA_ATTR_NO_WARN) && printk_ratelimit()) { + dev_warn(dev, + "swiotlb: coherent allocation failed, size=%zu\n", + size); + dump_stack(); + } + return NULL; +} + +static bool swiotlb_free_buffer(struct device *dev, size_t size, + dma_addr_t dma_addr) +{ + phys_addr_t phys_addr = dma_to_phys(dev, dma_addr); + + WARN_ON_ONCE(irqs_disabled()); + + if (!is_swiotlb_buffer(phys_addr)) + return false; + + /* + * DMA_TO_DEVICE to avoid memcpy in swiotlb_tbl_unmap_single. + * DMA_ATTR_SKIP_CPU_SYNC is optional. + */ + swiotlb_tbl_unmap_single(dev, phys_addr, size, DMA_TO_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + return true; +} + +static void +swiotlb_full(struct device *dev, size_t size, enum dma_data_direction dir, + int do_panic) +{ + if (swiotlb_force == SWIOTLB_NO_FORCE) + return; + + /* + * Ran out of IOMMU space for this operation. This is very bad. + * Unfortunately the drivers cannot handle this operation properly. + * unless they check for dma_mapping_error (most don't) + * When the mapping is small enough return a static buffer to limit + * the damage, or panic when the transfer is too big. + */ + dev_err_ratelimited(dev, "DMA: Out of SW-IOMMU space for %zu bytes\n", + size); + + if (size <= io_tlb_overflow || !do_panic) + return; + + if (dir == DMA_BIDIRECTIONAL) + panic("DMA: Random memory could be DMA accessed\n"); + if (dir == DMA_FROM_DEVICE) + panic("DMA: Random memory could be DMA written\n"); + if (dir == DMA_TO_DEVICE) + panic("DMA: Random memory could be DMA read\n"); +} + +/* + * Map a single buffer of the indicated size for DMA in streaming mode. The + * physical address to use is returned. + * + * Once the device is given the dma address, the device owns this memory until + * either swiotlb_unmap_page or swiotlb_dma_sync_single is performed. + */ +dma_addr_t swiotlb_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir, + unsigned long attrs) +{ + phys_addr_t map, phys = page_to_phys(page) + offset; + dma_addr_t dev_addr = phys_to_dma(dev, phys); + + BUG_ON(dir == DMA_NONE); + /* + * If the address happens to be in the device's DMA window, + * we can safely return the device addr and not worry about bounce + * buffering it. + */ + if (dma_capable(dev, dev_addr, size) && swiotlb_force != SWIOTLB_FORCE) + return dev_addr; + + trace_swiotlb_bounced(dev, dev_addr, size, swiotlb_force); + + /* Oh well, have to allocate and map a bounce buffer. */ + map = map_single(dev, phys, size, dir, attrs); + if (map == SWIOTLB_MAP_ERROR) { + swiotlb_full(dev, size, dir, 1); + return __phys_to_dma(dev, io_tlb_overflow_buffer); + } + + dev_addr = __phys_to_dma(dev, map); + + /* Ensure that the address returned is DMA'ble */ + if (dma_capable(dev, dev_addr, size)) + return dev_addr; + + attrs |= DMA_ATTR_SKIP_CPU_SYNC; + swiotlb_tbl_unmap_single(dev, map, size, dir, attrs); + + return __phys_to_dma(dev, io_tlb_overflow_buffer); +} + +/* + * Unmap a single streaming mode DMA translation. The dma_addr and size must + * match what was provided for in a previous swiotlb_map_page call. All + * other usages are undefined. + * + * After this call, reads by the cpu to the buffer are guaranteed to see + * whatever the device wrote there. + */ +static void unmap_single(struct device *hwdev, dma_addr_t dev_addr, + size_t size, enum dma_data_direction dir, + unsigned long attrs) +{ + phys_addr_t paddr = dma_to_phys(hwdev, dev_addr); + + BUG_ON(dir == DMA_NONE); + + if (is_swiotlb_buffer(paddr)) { + swiotlb_tbl_unmap_single(hwdev, paddr, size, dir, attrs); + return; + } + + if (dir != DMA_FROM_DEVICE) + return; + + /* + * phys_to_virt doesn't work with hihgmem page but we could + * call dma_mark_clean() with hihgmem page here. However, we + * are fine since dma_mark_clean() is null on POWERPC. We can + * make dma_mark_clean() take a physical address if necessary. + */ + dma_mark_clean(phys_to_virt(paddr), size); +} + +void swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr, + size_t size, enum dma_data_direction dir, + unsigned long attrs) +{ + unmap_single(hwdev, dev_addr, size, dir, attrs); +} + +/* + * Make physical memory consistent for a single streaming mode DMA translation + * after a transfer. + * + * If you perform a swiotlb_map_page() but wish to interrogate the buffer + * using the cpu, yet do not wish to teardown the dma mapping, you must + * call this function before doing so. At the next point you give the dma + * address back to the card, you must first perform a + * swiotlb_dma_sync_for_device, and then the device again owns the buffer + */ +static void +swiotlb_sync_single(struct device *hwdev, dma_addr_t dev_addr, + size_t size, enum dma_data_direction dir, + enum dma_sync_target target) +{ + phys_addr_t paddr = dma_to_phys(hwdev, dev_addr); + + BUG_ON(dir == DMA_NONE); + + if (is_swiotlb_buffer(paddr)) { + swiotlb_tbl_sync_single(hwdev, paddr, size, dir, target); + return; + } + + if (dir != DMA_FROM_DEVICE) + return; + + dma_mark_clean(phys_to_virt(paddr), size); +} + +void +swiotlb_sync_single_for_cpu(struct device *hwdev, dma_addr_t dev_addr, + size_t size, enum dma_data_direction dir) +{ + swiotlb_sync_single(hwdev, dev_addr, size, dir, SYNC_FOR_CPU); +} + +void +swiotlb_sync_single_for_device(struct device *hwdev, dma_addr_t dev_addr, + size_t size, enum dma_data_direction dir) +{ + swiotlb_sync_single(hwdev, dev_addr, size, dir, SYNC_FOR_DEVICE); +} + +/* + * Map a set of buffers described by scatterlist in streaming mode for DMA. + * This is the scatter-gather version of the above swiotlb_map_page + * interface. Here the scatter gather list elements are each tagged with the + * appropriate dma address and length. They are obtained via + * sg_dma_{address,length}(SG). + * + * NOTE: An implementation may be able to use a smaller number of + * DMA address/length pairs than there are SG table elements. + * (for example via virtual mapping capabilities) + * The routine returns the number of addr/length pairs actually + * used, at most nents. + * + * Device ownership issues as mentioned above for swiotlb_map_page are the + * same here. + */ +int +swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, int nelems, + enum dma_data_direction dir, unsigned long attrs) +{ + struct scatterlist *sg; + int i; + + BUG_ON(dir == DMA_NONE); + + for_each_sg(sgl, sg, nelems, i) { + phys_addr_t paddr = sg_phys(sg); + dma_addr_t dev_addr = phys_to_dma(hwdev, paddr); + + if (swiotlb_force == SWIOTLB_FORCE || + !dma_capable(hwdev, dev_addr, sg->length)) { + phys_addr_t map = map_single(hwdev, sg_phys(sg), + sg->length, dir, attrs); + if (map == SWIOTLB_MAP_ERROR) { + /* Don't panic here, we expect map_sg users + to do proper error handling. */ + swiotlb_full(hwdev, sg->length, dir, 0); + attrs |= DMA_ATTR_SKIP_CPU_SYNC; + swiotlb_unmap_sg_attrs(hwdev, sgl, i, dir, + attrs); + sg_dma_len(sgl) = 0; + return 0; + } + sg->dma_address = __phys_to_dma(hwdev, map); + } else + sg->dma_address = dev_addr; + sg_dma_len(sg) = sg->length; + } + return nelems; +} + +/* + * Unmap a set of streaming mode DMA translations. Again, cpu read rules + * concerning calls here are the same as for swiotlb_unmap_page() above. + */ +void +swiotlb_unmap_sg_attrs(struct device *hwdev, struct scatterlist *sgl, + int nelems, enum dma_data_direction dir, + unsigned long attrs) +{ + struct scatterlist *sg; + int i; + + BUG_ON(dir == DMA_NONE); + + for_each_sg(sgl, sg, nelems, i) + unmap_single(hwdev, sg->dma_address, sg_dma_len(sg), dir, + attrs); +} + +/* + * Make physical memory consistent for a set of streaming mode DMA translations + * after a transfer. + * + * The same as swiotlb_sync_single_* but for a scatter-gather list, same rules + * and usage. + */ +static void +swiotlb_sync_sg(struct device *hwdev, struct scatterlist *sgl, + int nelems, enum dma_data_direction dir, + enum dma_sync_target target) +{ + struct scatterlist *sg; + int i; + + for_each_sg(sgl, sg, nelems, i) + swiotlb_sync_single(hwdev, sg->dma_address, + sg_dma_len(sg), dir, target); +} + +void +swiotlb_sync_sg_for_cpu(struct device *hwdev, struct scatterlist *sg, + int nelems, enum dma_data_direction dir) +{ + swiotlb_sync_sg(hwdev, sg, nelems, dir, SYNC_FOR_CPU); +} + +void +swiotlb_sync_sg_for_device(struct device *hwdev, struct scatterlist *sg, + int nelems, enum dma_data_direction dir) +{ + swiotlb_sync_sg(hwdev, sg, nelems, dir, SYNC_FOR_DEVICE); +} + +int +swiotlb_dma_mapping_error(struct device *hwdev, dma_addr_t dma_addr) +{ + return (dma_addr == __phys_to_dma(hwdev, io_tlb_overflow_buffer)); +} + +/* + * Return whether the given device DMA address mask can be supported + * properly. For example, if your device can only drive the low 24-bits + * during bus mastering, then you would pass 0x00ffffff as the mask to + * this function. + */ +int +swiotlb_dma_supported(struct device *hwdev, u64 mask) +{ + return __phys_to_dma(hwdev, io_tlb_end - 1) <= mask; +} + +void *swiotlb_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, + gfp_t gfp, unsigned long attrs) +{ + void *vaddr; + + /* temporary workaround: */ + if (gfp & __GFP_NOWARN) + attrs |= DMA_ATTR_NO_WARN; + + /* + * Don't print a warning when the first allocation attempt fails. + * swiotlb_alloc_coherent() will print a warning when the DMA memory + * allocation ultimately failed. + */ + gfp |= __GFP_NOWARN; + + vaddr = dma_direct_alloc(dev, size, dma_handle, gfp, attrs); + if (!vaddr) + vaddr = swiotlb_alloc_buffer(dev, size, dma_handle, attrs); + return vaddr; +} + +void swiotlb_free(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_addr, unsigned long attrs) +{ + if (!swiotlb_free_buffer(dev, size, dma_addr)) + dma_direct_free(dev, size, vaddr, dma_addr, attrs); +} + +const struct dma_map_ops swiotlb_dma_ops = { + .mapping_error = swiotlb_dma_mapping_error, + .alloc = swiotlb_alloc, + .free = swiotlb_free, + .sync_single_for_cpu = swiotlb_sync_single_for_cpu, + .sync_single_for_device = swiotlb_sync_single_for_device, + .sync_sg_for_cpu = swiotlb_sync_sg_for_cpu, + .sync_sg_for_device = swiotlb_sync_sg_for_device, + .map_sg = swiotlb_map_sg_attrs, + .unmap_sg = swiotlb_unmap_sg_attrs, + .map_page = swiotlb_map_page, + .unmap_page = swiotlb_unmap_page, + .dma_supported = dma_direct_supported, +}; diff --git a/kernel/dma/virt.c b/kernel/dma/virt.c new file mode 100644 index 000000000000..631ddec4b60a --- /dev/null +++ b/kernel/dma/virt.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DMA operations that map to virtual addresses without flushing memory. + */ +#include +#include +#include +#include + +static void *dma_virt_alloc(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp, + unsigned long attrs) +{ + void *ret; + + ret = (void *)__get_free_pages(gfp, get_order(size)); + if (ret) + *dma_handle = (uintptr_t)ret; + return ret; +} + +static void dma_virt_free(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t dma_addr, + unsigned long attrs) +{ + free_pages((unsigned long)cpu_addr, get_order(size)); +} + +static dma_addr_t dma_virt_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir, + unsigned long attrs) +{ + return (uintptr_t)(page_address(page) + offset); +} + +static int dma_virt_map_sg(struct device *dev, struct scatterlist *sgl, + int nents, enum dma_data_direction dir, + unsigned long attrs) +{ + int i; + struct scatterlist *sg; + + for_each_sg(sgl, sg, nents, i) { + BUG_ON(!sg_page(sg)); + sg_dma_address(sg) = (uintptr_t)sg_virt(sg); + sg_dma_len(sg) = sg->length; + } + + return nents; +} + +const struct dma_map_ops dma_virt_ops = { + .alloc = dma_virt_alloc, + .free = dma_virt_free, + .map_page = dma_virt_map_page, + .map_sg = dma_virt_map_sg, +}; +EXPORT_SYMBOL(dma_virt_ops); diff --git a/lib/Kconfig b/lib/Kconfig index 809fdd155739..803fcbced729 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -420,60 +420,15 @@ config HAS_IOPORT_MAP depends on HAS_IOMEM && !NO_IOPORT_MAP default y -config HAS_DMA - bool - depends on !NO_DMA - default y +source "kernel/dma/Kconfig" config SGL_ALLOC bool default n -config NEED_SG_DMA_LENGTH - bool - -config NEED_DMA_MAP_STATE - bool - -config ARCH_DMA_ADDR_T_64BIT - def_bool 64BIT || PHYS_ADDR_T_64BIT - config IOMMU_HELPER bool -config ARCH_HAS_SYNC_DMA_FOR_DEVICE - bool - -config ARCH_HAS_SYNC_DMA_FOR_CPU - bool - select NEED_DMA_MAP_STATE - -config DMA_DIRECT_OPS - bool - depends on HAS_DMA - -config DMA_NONCOHERENT_OPS - bool - depends on HAS_DMA - select DMA_DIRECT_OPS - -config DMA_NONCOHERENT_MMAP - bool - depends on DMA_NONCOHERENT_OPS - -config DMA_NONCOHERENT_CACHE_SYNC - bool - depends on DMA_NONCOHERENT_OPS - -config DMA_VIRT_OPS - bool - depends on HAS_DMA - -config SWIOTLB - bool - select DMA_DIRECT_OPS - select NEED_DMA_MAP_STATE - config CHECK_SIGNATURE bool diff --git a/lib/Makefile b/lib/Makefile index 5e0e160c9242..8153fdab287f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -29,9 +29,6 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \ lib-$(CONFIG_PRINTK) += dump_stack.o lib-$(CONFIG_MMU) += ioremap.o lib-$(CONFIG_SMP) += cpumask.o -obj-$(CONFIG_DMA_DIRECT_OPS) += dma-direct.o -obj-$(CONFIG_DMA_NONCOHERENT_OPS) += dma-noncoherent.o -obj-$(CONFIG_DMA_VIRT_OPS) += dma-virt.o lib-y += kobject.o klist.o obj-y += lockref.o @@ -148,7 +145,6 @@ obj-$(CONFIG_SMP) += percpu_counter.o obj-$(CONFIG_AUDIT_GENERIC) += audit.o obj-$(CONFIG_AUDIT_COMPAT_GENERIC) += compat_audit.o -obj-$(CONFIG_SWIOTLB) += swiotlb.o obj-$(CONFIG_IOMMU_HELPER) += iommu-helper.o obj-$(CONFIG_FAULT_INJECTION) += fault-inject.o obj-$(CONFIG_NOTIFIER_ERROR_INJECTION) += notifier-error-inject.o @@ -169,8 +165,6 @@ obj-$(CONFIG_NLATTR) += nlattr.o obj-$(CONFIG_LRU_CACHE) += lru_cache.o -obj-$(CONFIG_DMA_API_DEBUG) += dma-debug.o - obj-$(CONFIG_GENERIC_CSUM) += checksum.o obj-$(CONFIG_GENERIC_ATOMIC64) += atomic64.o diff --git a/lib/dma-debug.c b/lib/dma-debug.c deleted file mode 100644 index c007d25bee09..000000000000 --- a/lib/dma-debug.c +++ /dev/null @@ -1,1773 +0,0 @@ -/* - * Copyright (C) 2008 Advanced Micro Devices, Inc. - * - * Author: Joerg Roedel - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define HASH_SIZE 1024ULL -#define HASH_FN_SHIFT 13 -#define HASH_FN_MASK (HASH_SIZE - 1) - -/* allow architectures to override this if absolutely required */ -#ifndef PREALLOC_DMA_DEBUG_ENTRIES -#define PREALLOC_DMA_DEBUG_ENTRIES (1 << 16) -#endif - -enum { - dma_debug_single, - dma_debug_page, - dma_debug_sg, - dma_debug_coherent, - dma_debug_resource, -}; - -enum map_err_types { - MAP_ERR_CHECK_NOT_APPLICABLE, - MAP_ERR_NOT_CHECKED, - MAP_ERR_CHECKED, -}; - -#define DMA_DEBUG_STACKTRACE_ENTRIES 5 - -/** - * struct dma_debug_entry - track a dma_map* or dma_alloc_coherent mapping - * @list: node on pre-allocated free_entries list - * @dev: 'dev' argument to dma_map_{page|single|sg} or dma_alloc_coherent - * @type: single, page, sg, coherent - * @pfn: page frame of the start address - * @offset: offset of mapping relative to pfn - * @size: length of the mapping - * @direction: enum dma_data_direction - * @sg_call_ents: 'nents' from dma_map_sg - * @sg_mapped_ents: 'mapped_ents' from dma_map_sg - * @map_err_type: track whether dma_mapping_error() was checked - * @stacktrace: support backtraces when a violation is detected - */ -struct dma_debug_entry { - struct list_head list; - struct device *dev; - int type; - unsigned long pfn; - size_t offset; - u64 dev_addr; - u64 size; - int direction; - int sg_call_ents; - int sg_mapped_ents; - enum map_err_types map_err_type; -#ifdef CONFIG_STACKTRACE - struct stack_trace stacktrace; - unsigned long st_entries[DMA_DEBUG_STACKTRACE_ENTRIES]; -#endif -}; - -typedef bool (*match_fn)(struct dma_debug_entry *, struct dma_debug_entry *); - -struct hash_bucket { - struct list_head list; - spinlock_t lock; -} ____cacheline_aligned_in_smp; - -/* Hash list to save the allocated dma addresses */ -static struct hash_bucket dma_entry_hash[HASH_SIZE]; -/* List of pre-allocated dma_debug_entry's */ -static LIST_HEAD(free_entries); -/* Lock for the list above */ -static DEFINE_SPINLOCK(free_entries_lock); - -/* Global disable flag - will be set in case of an error */ -static bool global_disable __read_mostly; - -/* Early initialization disable flag, set at the end of dma_debug_init */ -static bool dma_debug_initialized __read_mostly; - -static inline bool dma_debug_disabled(void) -{ - return global_disable || !dma_debug_initialized; -} - -/* Global error count */ -static u32 error_count; - -/* Global error show enable*/ -static u32 show_all_errors __read_mostly; -/* Number of errors to show */ -static u32 show_num_errors = 1; - -static u32 num_free_entries; -static u32 min_free_entries; -static u32 nr_total_entries; - -/* number of preallocated entries requested by kernel cmdline */ -static u32 nr_prealloc_entries = PREALLOC_DMA_DEBUG_ENTRIES; - -/* debugfs dentry's for the stuff above */ -static struct dentry *dma_debug_dent __read_mostly; -static struct dentry *global_disable_dent __read_mostly; -static struct dentry *error_count_dent __read_mostly; -static struct dentry *show_all_errors_dent __read_mostly; -static struct dentry *show_num_errors_dent __read_mostly; -static struct dentry *num_free_entries_dent __read_mostly; -static struct dentry *min_free_entries_dent __read_mostly; -static struct dentry *filter_dent __read_mostly; - -/* per-driver filter related state */ - -#define NAME_MAX_LEN 64 - -static char current_driver_name[NAME_MAX_LEN] __read_mostly; -static struct device_driver *current_driver __read_mostly; - -static DEFINE_RWLOCK(driver_name_lock); - -static const char *const maperr2str[] = { - [MAP_ERR_CHECK_NOT_APPLICABLE] = "dma map error check not applicable", - [MAP_ERR_NOT_CHECKED] = "dma map error not checked", - [MAP_ERR_CHECKED] = "dma map error checked", -}; - -static const char *type2name[5] = { "single", "page", - "scather-gather", "coherent", - "resource" }; - -static const char *dir2name[4] = { "DMA_BIDIRECTIONAL", "DMA_TO_DEVICE", - "DMA_FROM_DEVICE", "DMA_NONE" }; - -/* - * The access to some variables in this macro is racy. We can't use atomic_t - * here because all these variables are exported to debugfs. Some of them even - * writeable. This is also the reason why a lock won't help much. But anyway, - * the races are no big deal. Here is why: - * - * error_count: the addition is racy, but the worst thing that can happen is - * that we don't count some errors - * show_num_errors: the subtraction is racy. Also no big deal because in - * worst case this will result in one warning more in the - * system log than the user configured. This variable is - * writeable via debugfs. - */ -static inline void dump_entry_trace(struct dma_debug_entry *entry) -{ -#ifdef CONFIG_STACKTRACE - if (entry) { - pr_warning("Mapped at:\n"); - print_stack_trace(&entry->stacktrace, 0); - } -#endif -} - -static bool driver_filter(struct device *dev) -{ - struct device_driver *drv; - unsigned long flags; - bool ret; - - /* driver filter off */ - if (likely(!current_driver_name[0])) - return true; - - /* driver filter on and initialized */ - if (current_driver && dev && dev->driver == current_driver) - return true; - - /* driver filter on, but we can't filter on a NULL device... */ - if (!dev) - return false; - - if (current_driver || !current_driver_name[0]) - return false; - - /* driver filter on but not yet initialized */ - drv = dev->driver; - if (!drv) - return false; - - /* lock to protect against change of current_driver_name */ - read_lock_irqsave(&driver_name_lock, flags); - - ret = false; - if (drv->name && - strncmp(current_driver_name, drv->name, NAME_MAX_LEN - 1) == 0) { - current_driver = drv; - ret = true; - } - - read_unlock_irqrestore(&driver_name_lock, flags); - - return ret; -} - -#define err_printk(dev, entry, format, arg...) do { \ - error_count += 1; \ - if (driver_filter(dev) && \ - (show_all_errors || show_num_errors > 0)) { \ - WARN(1, "%s %s: " format, \ - dev ? dev_driver_string(dev) : "NULL", \ - dev ? dev_name(dev) : "NULL", ## arg); \ - dump_entry_trace(entry); \ - } \ - if (!show_all_errors && show_num_errors > 0) \ - show_num_errors -= 1; \ - } while (0); - -/* - * Hash related functions - * - * Every DMA-API request is saved into a struct dma_debug_entry. To - * have quick access to these structs they are stored into a hash. - */ -static int hash_fn(struct dma_debug_entry *entry) -{ - /* - * Hash function is based on the dma address. - * We use bits 20-27 here as the index into the hash - */ - return (entry->dev_addr >> HASH_FN_SHIFT) & HASH_FN_MASK; -} - -/* - * Request exclusive access to a hash bucket for a given dma_debug_entry. - */ -static struct hash_bucket *get_hash_bucket(struct dma_debug_entry *entry, - unsigned long *flags) - __acquires(&dma_entry_hash[idx].lock) -{ - int idx = hash_fn(entry); - unsigned long __flags; - - spin_lock_irqsave(&dma_entry_hash[idx].lock, __flags); - *flags = __flags; - return &dma_entry_hash[idx]; -} - -/* - * Give up exclusive access to the hash bucket - */ -static void put_hash_bucket(struct hash_bucket *bucket, - unsigned long *flags) - __releases(&bucket->lock) -{ - unsigned long __flags = *flags; - - spin_unlock_irqrestore(&bucket->lock, __flags); -} - -static bool exact_match(struct dma_debug_entry *a, struct dma_debug_entry *b) -{ - return ((a->dev_addr == b->dev_addr) && - (a->dev == b->dev)) ? true : false; -} - -static bool containing_match(struct dma_debug_entry *a, - struct dma_debug_entry *b) -{ - if (a->dev != b->dev) - return false; - - if ((b->dev_addr <= a->dev_addr) && - ((b->dev_addr + b->size) >= (a->dev_addr + a->size))) - return true; - - return false; -} - -/* - * Search a given entry in the hash bucket list - */ -static struct dma_debug_entry *__hash_bucket_find(struct hash_bucket *bucket, - struct dma_debug_entry *ref, - match_fn match) -{ - struct dma_debug_entry *entry, *ret = NULL; - int matches = 0, match_lvl, last_lvl = -1; - - list_for_each_entry(entry, &bucket->list, list) { - if (!match(ref, entry)) - continue; - - /* - * Some drivers map the same physical address multiple - * times. Without a hardware IOMMU this results in the - * same device addresses being put into the dma-debug - * hash multiple times too. This can result in false - * positives being reported. Therefore we implement a - * best-fit algorithm here which returns the entry from - * the hash which fits best to the reference value - * instead of the first-fit. - */ - matches += 1; - match_lvl = 0; - entry->size == ref->size ? ++match_lvl : 0; - entry->type == ref->type ? ++match_lvl : 0; - entry->direction == ref->direction ? ++match_lvl : 0; - entry->sg_call_ents == ref->sg_call_ents ? ++match_lvl : 0; - - if (match_lvl == 4) { - /* perfect-fit - return the result */ - return entry; - } else if (match_lvl > last_lvl) { - /* - * We found an entry that fits better then the - * previous one or it is the 1st match. - */ - last_lvl = match_lvl; - ret = entry; - } - } - - /* - * If we have multiple matches but no perfect-fit, just return - * NULL. - */ - ret = (matches == 1) ? ret : NULL; - - return ret; -} - -static struct dma_debug_entry *bucket_find_exact(struct hash_bucket *bucket, - struct dma_debug_entry *ref) -{ - return __hash_bucket_find(bucket, ref, exact_match); -} - -static struct dma_debug_entry *bucket_find_contain(struct hash_bucket **bucket, - struct dma_debug_entry *ref, - unsigned long *flags) -{ - - unsigned int max_range = dma_get_max_seg_size(ref->dev); - struct dma_debug_entry *entry, index = *ref; - unsigned int range = 0; - - while (range <= max_range) { - entry = __hash_bucket_find(*bucket, ref, containing_match); - - if (entry) - return entry; - - /* - * Nothing found, go back a hash bucket - */ - put_hash_bucket(*bucket, flags); - range += (1 << HASH_FN_SHIFT); - index.dev_addr -= (1 << HASH_FN_SHIFT); - *bucket = get_hash_bucket(&index, flags); - } - - return NULL; -} - -/* - * Add an entry to a hash bucket - */ -static void hash_bucket_add(struct hash_bucket *bucket, - struct dma_debug_entry *entry) -{ - list_add_tail(&entry->list, &bucket->list); -} - -/* - * Remove entry from a hash bucket list - */ -static void hash_bucket_del(struct dma_debug_entry *entry) -{ - list_del(&entry->list); -} - -static unsigned long long phys_addr(struct dma_debug_entry *entry) -{ - if (entry->type == dma_debug_resource) - return __pfn_to_phys(entry->pfn) + entry->offset; - - return page_to_phys(pfn_to_page(entry->pfn)) + entry->offset; -} - -/* - * Dump mapping entries for debugging purposes - */ -void debug_dma_dump_mappings(struct device *dev) -{ - int idx; - - for (idx = 0; idx < HASH_SIZE; idx++) { - struct hash_bucket *bucket = &dma_entry_hash[idx]; - struct dma_debug_entry *entry; - unsigned long flags; - - spin_lock_irqsave(&bucket->lock, flags); - - list_for_each_entry(entry, &bucket->list, list) { - if (!dev || dev == entry->dev) { - dev_info(entry->dev, - "%s idx %d P=%Lx N=%lx D=%Lx L=%Lx %s %s\n", - type2name[entry->type], idx, - phys_addr(entry), entry->pfn, - entry->dev_addr, entry->size, - dir2name[entry->direction], - maperr2str[entry->map_err_type]); - } - } - - spin_unlock_irqrestore(&bucket->lock, flags); - } -} - -/* - * For each mapping (initial cacheline in the case of - * dma_alloc_coherent/dma_map_page, initial cacheline in each page of a - * scatterlist, or the cacheline specified in dma_map_single) insert - * into this tree using the cacheline as the key. At - * dma_unmap_{single|sg|page} or dma_free_coherent delete the entry. If - * the entry already exists at insertion time add a tag as a reference - * count for the overlapping mappings. For now, the overlap tracking - * just ensures that 'unmaps' balance 'maps' before marking the - * cacheline idle, but we should also be flagging overlaps as an API - * violation. - * - * Memory usage is mostly constrained by the maximum number of available - * dma-debug entries in that we need a free dma_debug_entry before - * inserting into the tree. In the case of dma_map_page and - * dma_alloc_coherent there is only one dma_debug_entry and one - * dma_active_cacheline entry to track per event. dma_map_sg(), on the - * other hand, consumes a single dma_debug_entry, but inserts 'nents' - * entries into the tree. - * - * At any time debug_dma_assert_idle() can be called to trigger a - * warning if any cachelines in the given page are in the active set. - */ -static RADIX_TREE(dma_active_cacheline, GFP_NOWAIT); -static DEFINE_SPINLOCK(radix_lock); -#define ACTIVE_CACHELINE_MAX_OVERLAP ((1 << RADIX_TREE_MAX_TAGS) - 1) -#define CACHELINE_PER_PAGE_SHIFT (PAGE_SHIFT - L1_CACHE_SHIFT) -#define CACHELINES_PER_PAGE (1 << CACHELINE_PER_PAGE_SHIFT) - -static phys_addr_t to_cacheline_number(struct dma_debug_entry *entry) -{ - return (entry->pfn << CACHELINE_PER_PAGE_SHIFT) + - (entry->offset >> L1_CACHE_SHIFT); -} - -static int active_cacheline_read_overlap(phys_addr_t cln) -{ - int overlap = 0, i; - - for (i = RADIX_TREE_MAX_TAGS - 1; i >= 0; i--) - if (radix_tree_tag_get(&dma_active_cacheline, cln, i)) - overlap |= 1 << i; - return overlap; -} - -static int active_cacheline_set_overlap(phys_addr_t cln, int overlap) -{ - int i; - - if (overlap > ACTIVE_CACHELINE_MAX_OVERLAP || overlap < 0) - return overlap; - - for (i = RADIX_TREE_MAX_TAGS - 1; i >= 0; i--) - if (overlap & 1 << i) - radix_tree_tag_set(&dma_active_cacheline, cln, i); - else - radix_tree_tag_clear(&dma_active_cacheline, cln, i); - - return overlap; -} - -static void active_cacheline_inc_overlap(phys_addr_t cln) -{ - int overlap = active_cacheline_read_overlap(cln); - - overlap = active_cacheline_set_overlap(cln, ++overlap); - - /* If we overflowed the overlap counter then we're potentially - * leaking dma-mappings. Otherwise, if maps and unmaps are - * balanced then this overflow may cause false negatives in - * debug_dma_assert_idle() as the cacheline may be marked idle - * prematurely. - */ - WARN_ONCE(overlap > ACTIVE_CACHELINE_MAX_OVERLAP, - "DMA-API: exceeded %d overlapping mappings of cacheline %pa\n", - ACTIVE_CACHELINE_MAX_OVERLAP, &cln); -} - -static int active_cacheline_dec_overlap(phys_addr_t cln) -{ - int overlap = active_cacheline_read_overlap(cln); - - return active_cacheline_set_overlap(cln, --overlap); -} - -static int active_cacheline_insert(struct dma_debug_entry *entry) -{ - phys_addr_t cln = to_cacheline_number(entry); - unsigned long flags; - int rc; - - /* If the device is not writing memory then we don't have any - * concerns about the cpu consuming stale data. This mitigates - * legitimate usages of overlapping mappings. - */ - if (entry->direction == DMA_TO_DEVICE) - return 0; - - spin_lock_irqsave(&radix_lock, flags); - rc = radix_tree_insert(&dma_active_cacheline, cln, entry); - if (rc == -EEXIST) - active_cacheline_inc_overlap(cln); - spin_unlock_irqrestore(&radix_lock, flags); - - return rc; -} - -static void active_cacheline_remove(struct dma_debug_entry *entry) -{ - phys_addr_t cln = to_cacheline_number(entry); - unsigned long flags; - - /* ...mirror the insert case */ - if (entry->direction == DMA_TO_DEVICE) - return; - - spin_lock_irqsave(&radix_lock, flags); - /* since we are counting overlaps the final put of the - * cacheline will occur when the overlap count is 0. - * active_cacheline_dec_overlap() returns -1 in that case - */ - if (active_cacheline_dec_overlap(cln) < 0) - radix_tree_delete(&dma_active_cacheline, cln); - spin_unlock_irqrestore(&radix_lock, flags); -} - -/** - * debug_dma_assert_idle() - assert that a page is not undergoing dma - * @page: page to lookup in the dma_active_cacheline tree - * - * Place a call to this routine in cases where the cpu touching the page - * before the dma completes (page is dma_unmapped) will lead to data - * corruption. - */ -void debug_dma_assert_idle(struct page *page) -{ - static struct dma_debug_entry *ents[CACHELINES_PER_PAGE]; - struct dma_debug_entry *entry = NULL; - void **results = (void **) &ents; - unsigned int nents, i; - unsigned long flags; - phys_addr_t cln; - - if (dma_debug_disabled()) - return; - - if (!page) - return; - - cln = (phys_addr_t) page_to_pfn(page) << CACHELINE_PER_PAGE_SHIFT; - spin_lock_irqsave(&radix_lock, flags); - nents = radix_tree_gang_lookup(&dma_active_cacheline, results, cln, - CACHELINES_PER_PAGE); - for (i = 0; i < nents; i++) { - phys_addr_t ent_cln = to_cacheline_number(ents[i]); - - if (ent_cln == cln) { - entry = ents[i]; - break; - } else if (ent_cln >= cln + CACHELINES_PER_PAGE) - break; - } - spin_unlock_irqrestore(&radix_lock, flags); - - if (!entry) - return; - - cln = to_cacheline_number(entry); - err_printk(entry->dev, entry, - "DMA-API: cpu touching an active dma mapped cacheline [cln=%pa]\n", - &cln); -} - -/* - * Wrapper function for adding an entry to the hash. - * This function takes care of locking itself. - */ -static void add_dma_entry(struct dma_debug_entry *entry) -{ - struct hash_bucket *bucket; - unsigned long flags; - int rc; - - bucket = get_hash_bucket(entry, &flags); - hash_bucket_add(bucket, entry); - put_hash_bucket(bucket, &flags); - - rc = active_cacheline_insert(entry); - if (rc == -ENOMEM) { - pr_err("DMA-API: cacheline tracking ENOMEM, dma-debug disabled\n"); - global_disable = true; - } - - /* TODO: report -EEXIST errors here as overlapping mappings are - * not supported by the DMA API - */ -} - -static struct dma_debug_entry *__dma_entry_alloc(void) -{ - struct dma_debug_entry *entry; - - entry = list_entry(free_entries.next, struct dma_debug_entry, list); - list_del(&entry->list); - memset(entry, 0, sizeof(*entry)); - - num_free_entries -= 1; - if (num_free_entries < min_free_entries) - min_free_entries = num_free_entries; - - return entry; -} - -/* struct dma_entry allocator - * - * The next two functions implement the allocator for - * struct dma_debug_entries. - */ -static struct dma_debug_entry *dma_entry_alloc(void) -{ - struct dma_debug_entry *entry; - unsigned long flags; - - spin_lock_irqsave(&free_entries_lock, flags); - - if (list_empty(&free_entries)) { - global_disable = true; - spin_unlock_irqrestore(&free_entries_lock, flags); - pr_err("DMA-API: debugging out of memory - disabling\n"); - return NULL; - } - - entry = __dma_entry_alloc(); - - spin_unlock_irqrestore(&free_entries_lock, flags); - -#ifdef CONFIG_STACKTRACE - entry->stacktrace.max_entries = DMA_DEBUG_STACKTRACE_ENTRIES; - entry->stacktrace.entries = entry->st_entries; - entry->stacktrace.skip = 2; - save_stack_trace(&entry->stacktrace); -#endif - - return entry; -} - -static void dma_entry_free(struct dma_debug_entry *entry) -{ - unsigned long flags; - - active_cacheline_remove(entry); - - /* - * add to beginning of the list - this way the entries are - * more likely cache hot when they are reallocated. - */ - spin_lock_irqsave(&free_entries_lock, flags); - list_add(&entry->list, &free_entries); - num_free_entries += 1; - spin_unlock_irqrestore(&free_entries_lock, flags); -} - -int dma_debug_resize_entries(u32 num_entries) -{ - int i, delta, ret = 0; - unsigned long flags; - struct dma_debug_entry *entry; - LIST_HEAD(tmp); - - spin_lock_irqsave(&free_entries_lock, flags); - - if (nr_total_entries < num_entries) { - delta = num_entries - nr_total_entries; - - spin_unlock_irqrestore(&free_entries_lock, flags); - - for (i = 0; i < delta; i++) { - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - break; - - list_add_tail(&entry->list, &tmp); - } - - spin_lock_irqsave(&free_entries_lock, flags); - - list_splice(&tmp, &free_entries); - nr_total_entries += i; - num_free_entries += i; - } else { - delta = nr_total_entries - num_entries; - - for (i = 0; i < delta && !list_empty(&free_entries); i++) { - entry = __dma_entry_alloc(); - kfree(entry); - } - - nr_total_entries -= i; - } - - if (nr_total_entries != num_entries) - ret = 1; - - spin_unlock_irqrestore(&free_entries_lock, flags); - - return ret; -} - -/* - * DMA-API debugging init code - * - * The init code does two things: - * 1. Initialize core data structures - * 2. Preallocate a given number of dma_debug_entry structs - */ - -static int prealloc_memory(u32 num_entries) -{ - struct dma_debug_entry *entry, *next_entry; - int i; - - for (i = 0; i < num_entries; ++i) { - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - goto out_err; - - list_add_tail(&entry->list, &free_entries); - } - - num_free_entries = num_entries; - min_free_entries = num_entries; - - pr_info("DMA-API: preallocated %d debug entries\n", num_entries); - - return 0; - -out_err: - - list_for_each_entry_safe(entry, next_entry, &free_entries, list) { - list_del(&entry->list); - kfree(entry); - } - - return -ENOMEM; -} - -static ssize_t filter_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - char buf[NAME_MAX_LEN + 1]; - unsigned long flags; - int len; - - if (!current_driver_name[0]) - return 0; - - /* - * We can't copy to userspace directly because current_driver_name can - * only be read under the driver_name_lock with irqs disabled. So - * create a temporary copy first. - */ - read_lock_irqsave(&driver_name_lock, flags); - len = scnprintf(buf, NAME_MAX_LEN + 1, "%s\n", current_driver_name); - read_unlock_irqrestore(&driver_name_lock, flags); - - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t filter_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *ppos) -{ - char buf[NAME_MAX_LEN]; - unsigned long flags; - size_t len; - int i; - - /* - * We can't copy from userspace directly. Access to - * current_driver_name is protected with a write_lock with irqs - * disabled. Since copy_from_user can fault and may sleep we - * need to copy to temporary buffer first - */ - len = min(count, (size_t)(NAME_MAX_LEN - 1)); - if (copy_from_user(buf, userbuf, len)) - return -EFAULT; - - buf[len] = 0; - - write_lock_irqsave(&driver_name_lock, flags); - - /* - * Now handle the string we got from userspace very carefully. - * The rules are: - * - only use the first token we got - * - token delimiter is everything looking like a space - * character (' ', '\n', '\t' ...) - * - */ - if (!isalnum(buf[0])) { - /* - * If the first character userspace gave us is not - * alphanumerical then assume the filter should be - * switched off. - */ - if (current_driver_name[0]) - pr_info("DMA-API: switching off dma-debug driver filter\n"); - current_driver_name[0] = 0; - current_driver = NULL; - goto out_unlock; - } - - /* - * Now parse out the first token and use it as the name for the - * driver to filter for. - */ - for (i = 0; i < NAME_MAX_LEN - 1; ++i) { - current_driver_name[i] = buf[i]; - if (isspace(buf[i]) || buf[i] == ' ' || buf[i] == 0) - break; - } - current_driver_name[i] = 0; - current_driver = NULL; - - pr_info("DMA-API: enable driver filter for driver [%s]\n", - current_driver_name); - -out_unlock: - write_unlock_irqrestore(&driver_name_lock, flags); - - return count; -} - -static const struct file_operations filter_fops = { - .read = filter_read, - .write = filter_write, - .llseek = default_llseek, -}; - -static int dma_debug_fs_init(void) -{ - dma_debug_dent = debugfs_create_dir("dma-api", NULL); - if (!dma_debug_dent) { - pr_err("DMA-API: can not create debugfs directory\n"); - return -ENOMEM; - } - - global_disable_dent = debugfs_create_bool("disabled", 0444, - dma_debug_dent, - &global_disable); - if (!global_disable_dent) - goto out_err; - - error_count_dent = debugfs_create_u32("error_count", 0444, - dma_debug_dent, &error_count); - if (!error_count_dent) - goto out_err; - - show_all_errors_dent = debugfs_create_u32("all_errors", 0644, - dma_debug_dent, - &show_all_errors); - if (!show_all_errors_dent) - goto out_err; - - show_num_errors_dent = debugfs_create_u32("num_errors", 0644, - dma_debug_dent, - &show_num_errors); - if (!show_num_errors_dent) - goto out_err; - - num_free_entries_dent = debugfs_create_u32("num_free_entries", 0444, - dma_debug_dent, - &num_free_entries); - if (!num_free_entries_dent) - goto out_err; - - min_free_entries_dent = debugfs_create_u32("min_free_entries", 0444, - dma_debug_dent, - &min_free_entries); - if (!min_free_entries_dent) - goto out_err; - - filter_dent = debugfs_create_file("driver_filter", 0644, - dma_debug_dent, NULL, &filter_fops); - if (!filter_dent) - goto out_err; - - return 0; - -out_err: - debugfs_remove_recursive(dma_debug_dent); - - return -ENOMEM; -} - -static int device_dma_allocations(struct device *dev, struct dma_debug_entry **out_entry) -{ - struct dma_debug_entry *entry; - unsigned long flags; - int count = 0, i; - - for (i = 0; i < HASH_SIZE; ++i) { - spin_lock_irqsave(&dma_entry_hash[i].lock, flags); - list_for_each_entry(entry, &dma_entry_hash[i].list, list) { - if (entry->dev == dev) { - count += 1; - *out_entry = entry; - } - } - spin_unlock_irqrestore(&dma_entry_hash[i].lock, flags); - } - - return count; -} - -static int dma_debug_device_change(struct notifier_block *nb, unsigned long action, void *data) -{ - struct device *dev = data; - struct dma_debug_entry *uninitialized_var(entry); - int count; - - if (dma_debug_disabled()) - return 0; - - switch (action) { - case BUS_NOTIFY_UNBOUND_DRIVER: - count = device_dma_allocations(dev, &entry); - if (count == 0) - break; - err_printk(dev, entry, "DMA-API: device driver has pending " - "DMA allocations while released from device " - "[count=%d]\n" - "One of leaked entries details: " - "[device address=0x%016llx] [size=%llu bytes] " - "[mapped with %s] [mapped as %s]\n", - count, entry->dev_addr, entry->size, - dir2name[entry->direction], type2name[entry->type]); - break; - default: - break; - } - - return 0; -} - -void dma_debug_add_bus(struct bus_type *bus) -{ - struct notifier_block *nb; - - if (dma_debug_disabled()) - return; - - nb = kzalloc(sizeof(struct notifier_block), GFP_KERNEL); - if (nb == NULL) { - pr_err("dma_debug_add_bus: out of memory\n"); - return; - } - - nb->notifier_call = dma_debug_device_change; - - bus_register_notifier(bus, nb); -} - -static int dma_debug_init(void) -{ - int i; - - /* Do not use dma_debug_initialized here, since we really want to be - * called to set dma_debug_initialized - */ - if (global_disable) - return 0; - - for (i = 0; i < HASH_SIZE; ++i) { - INIT_LIST_HEAD(&dma_entry_hash[i].list); - spin_lock_init(&dma_entry_hash[i].lock); - } - - if (dma_debug_fs_init() != 0) { - pr_err("DMA-API: error creating debugfs entries - disabling\n"); - global_disable = true; - - return 0; - } - - if (prealloc_memory(nr_prealloc_entries) != 0) { - pr_err("DMA-API: debugging out of memory error - disabled\n"); - global_disable = true; - - return 0; - } - - nr_total_entries = num_free_entries; - - dma_debug_initialized = true; - - pr_info("DMA-API: debugging enabled by kernel config\n"); - return 0; -} -core_initcall(dma_debug_init); - -static __init int dma_debug_cmdline(char *str) -{ - if (!str) - return -EINVAL; - - if (strncmp(str, "off", 3) == 0) { - pr_info("DMA-API: debugging disabled on kernel command line\n"); - global_disable = true; - } - - return 0; -} - -static __init int dma_debug_entries_cmdline(char *str) -{ - if (!str) - return -EINVAL; - if (!get_option(&str, &nr_prealloc_entries)) - nr_prealloc_entries = PREALLOC_DMA_DEBUG_ENTRIES; - return 0; -} - -__setup("dma_debug=", dma_debug_cmdline); -__setup("dma_debug_entries=", dma_debug_entries_cmdline); - -static void check_unmap(struct dma_debug_entry *ref) -{ - struct dma_debug_entry *entry; - struct hash_bucket *bucket; - unsigned long flags; - - bucket = get_hash_bucket(ref, &flags); - entry = bucket_find_exact(bucket, ref); - - if (!entry) { - /* must drop lock before calling dma_mapping_error */ - put_hash_bucket(bucket, &flags); - - if (dma_mapping_error(ref->dev, ref->dev_addr)) { - err_printk(ref->dev, NULL, - "DMA-API: device driver tries to free an " - "invalid DMA memory address\n"); - } else { - err_printk(ref->dev, NULL, - "DMA-API: device driver tries to free DMA " - "memory it has not allocated [device " - "address=0x%016llx] [size=%llu bytes]\n", - ref->dev_addr, ref->size); - } - return; - } - - if (ref->size != entry->size) { - err_printk(ref->dev, entry, "DMA-API: device driver frees " - "DMA memory with different size " - "[device address=0x%016llx] [map size=%llu bytes] " - "[unmap size=%llu bytes]\n", - ref->dev_addr, entry->size, ref->size); - } - - if (ref->type != entry->type) { - err_printk(ref->dev, entry, "DMA-API: device driver frees " - "DMA memory with wrong function " - "[device address=0x%016llx] [size=%llu bytes] " - "[mapped as %s] [unmapped as %s]\n", - ref->dev_addr, ref->size, - type2name[entry->type], type2name[ref->type]); - } else if ((entry->type == dma_debug_coherent) && - (phys_addr(ref) != phys_addr(entry))) { - err_printk(ref->dev, entry, "DMA-API: device driver frees " - "DMA memory with different CPU address " - "[device address=0x%016llx] [size=%llu bytes] " - "[cpu alloc address=0x%016llx] " - "[cpu free address=0x%016llx]", - ref->dev_addr, ref->size, - phys_addr(entry), - phys_addr(ref)); - } - - if (ref->sg_call_ents && ref->type == dma_debug_sg && - ref->sg_call_ents != entry->sg_call_ents) { - err_printk(ref->dev, entry, "DMA-API: device driver frees " - "DMA sg list with different entry count " - "[map count=%d] [unmap count=%d]\n", - entry->sg_call_ents, ref->sg_call_ents); - } - - /* - * This may be no bug in reality - but most implementations of the - * DMA API don't handle this properly, so check for it here - */ - if (ref->direction != entry->direction) { - err_printk(ref->dev, entry, "DMA-API: device driver frees " - "DMA memory with different direction " - "[device address=0x%016llx] [size=%llu bytes] " - "[mapped with %s] [unmapped with %s]\n", - ref->dev_addr, ref->size, - dir2name[entry->direction], - dir2name[ref->direction]); - } - - /* - * Drivers should use dma_mapping_error() to check the returned - * addresses of dma_map_single() and dma_map_page(). - * If not, print this warning message. See Documentation/DMA-API.txt. - */ - if (entry->map_err_type == MAP_ERR_NOT_CHECKED) { - err_printk(ref->dev, entry, - "DMA-API: device driver failed to check map error" - "[device address=0x%016llx] [size=%llu bytes] " - "[mapped as %s]", - ref->dev_addr, ref->size, - type2name[entry->type]); - } - - hash_bucket_del(entry); - dma_entry_free(entry); - - put_hash_bucket(bucket, &flags); -} - -static void check_for_stack(struct device *dev, - struct page *page, size_t offset) -{ - void *addr; - struct vm_struct *stack_vm_area = task_stack_vm_area(current); - - if (!stack_vm_area) { - /* Stack is direct-mapped. */ - if (PageHighMem(page)) - return; - addr = page_address(page) + offset; - if (object_is_on_stack(addr)) - err_printk(dev, NULL, "DMA-API: device driver maps memory from stack [addr=%p]\n", addr); - } else { - /* Stack is vmalloced. */ - int i; - - for (i = 0; i < stack_vm_area->nr_pages; i++) { - if (page != stack_vm_area->pages[i]) - continue; - - addr = (u8 *)current->stack + i * PAGE_SIZE + offset; - err_printk(dev, NULL, "DMA-API: device driver maps memory from stack [probable addr=%p]\n", addr); - break; - } - } -} - -static inline bool overlap(void *addr, unsigned long len, void *start, void *end) -{ - unsigned long a1 = (unsigned long)addr; - unsigned long b1 = a1 + len; - unsigned long a2 = (unsigned long)start; - unsigned long b2 = (unsigned long)end; - - return !(b1 <= a2 || a1 >= b2); -} - -static void check_for_illegal_area(struct device *dev, void *addr, unsigned long len) -{ - if (overlap(addr, len, _stext, _etext) || - overlap(addr, len, __start_rodata, __end_rodata)) - err_printk(dev, NULL, "DMA-API: device driver maps memory from kernel text or rodata [addr=%p] [len=%lu]\n", addr, len); -} - -static void check_sync(struct device *dev, - struct dma_debug_entry *ref, - bool to_cpu) -{ - struct dma_debug_entry *entry; - struct hash_bucket *bucket; - unsigned long flags; - - bucket = get_hash_bucket(ref, &flags); - - entry = bucket_find_contain(&bucket, ref, &flags); - - if (!entry) { - err_printk(dev, NULL, "DMA-API: device driver tries " - "to sync DMA memory it has not allocated " - "[device address=0x%016llx] [size=%llu bytes]\n", - (unsigned long long)ref->dev_addr, ref->size); - goto out; - } - - if (ref->size > entry->size) { - err_printk(dev, entry, "DMA-API: device driver syncs" - " DMA memory outside allocated range " - "[device address=0x%016llx] " - "[allocation size=%llu bytes] " - "[sync offset+size=%llu]\n", - entry->dev_addr, entry->size, - ref->size); - } - - if (entry->direction == DMA_BIDIRECTIONAL) - goto out; - - if (ref->direction != entry->direction) { - err_printk(dev, entry, "DMA-API: device driver syncs " - "DMA memory with different direction " - "[device address=0x%016llx] [size=%llu bytes] " - "[mapped with %s] [synced with %s]\n", - (unsigned long long)ref->dev_addr, entry->size, - dir2name[entry->direction], - dir2name[ref->direction]); - } - - if (to_cpu && !(entry->direction == DMA_FROM_DEVICE) && - !(ref->direction == DMA_TO_DEVICE)) - err_printk(dev, entry, "DMA-API: device driver syncs " - "device read-only DMA memory for cpu " - "[device address=0x%016llx] [size=%llu bytes] " - "[mapped with %s] [synced with %s]\n", - (unsigned long long)ref->dev_addr, entry->size, - dir2name[entry->direction], - dir2name[ref->direction]); - - if (!to_cpu && !(entry->direction == DMA_TO_DEVICE) && - !(ref->direction == DMA_FROM_DEVICE)) - err_printk(dev, entry, "DMA-API: device driver syncs " - "device write-only DMA memory to device " - "[device address=0x%016llx] [size=%llu bytes] " - "[mapped with %s] [synced with %s]\n", - (unsigned long long)ref->dev_addr, entry->size, - dir2name[entry->direction], - dir2name[ref->direction]); - - if (ref->sg_call_ents && ref->type == dma_debug_sg && - ref->sg_call_ents != entry->sg_call_ents) { - err_printk(ref->dev, entry, "DMA-API: device driver syncs " - "DMA sg list with different entry count " - "[map count=%d] [sync count=%d]\n", - entry->sg_call_ents, ref->sg_call_ents); - } - -out: - put_hash_bucket(bucket, &flags); -} - -static void check_sg_segment(struct device *dev, struct scatterlist *sg) -{ -#ifdef CONFIG_DMA_API_DEBUG_SG - unsigned int max_seg = dma_get_max_seg_size(dev); - u64 start, end, boundary = dma_get_seg_boundary(dev); - - /* - * Either the driver forgot to set dma_parms appropriately, or - * whoever generated the list forgot to check them. - */ - if (sg->length > max_seg) - err_printk(dev, NULL, "DMA-API: mapping sg segment longer than device claims to support [len=%u] [max=%u]\n", - sg->length, max_seg); - /* - * In some cases this could potentially be the DMA API - * implementation's fault, but it would usually imply that - * the scatterlist was built inappropriately to begin with. - */ - start = sg_dma_address(sg); - end = start + sg_dma_len(sg) - 1; - if ((start ^ end) & ~boundary) - err_printk(dev, NULL, "DMA-API: mapping sg segment across boundary [start=0x%016llx] [end=0x%016llx] [boundary=0x%016llx]\n", - start, end, boundary); -#endif -} - -void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, - size_t size, int direction, dma_addr_t dma_addr, - bool map_single) -{ - struct dma_debug_entry *entry; - - if (unlikely(dma_debug_disabled())) - return; - - if (dma_mapping_error(dev, dma_addr)) - return; - - entry = dma_entry_alloc(); - if (!entry) - return; - - entry->dev = dev; - entry->type = dma_debug_page; - entry->pfn = page_to_pfn(page); - entry->offset = offset, - entry->dev_addr = dma_addr; - entry->size = size; - entry->direction = direction; - entry->map_err_type = MAP_ERR_NOT_CHECKED; - - if (map_single) - entry->type = dma_debug_single; - - check_for_stack(dev, page, offset); - - if (!PageHighMem(page)) { - void *addr = page_address(page) + offset; - - check_for_illegal_area(dev, addr, size); - } - - add_dma_entry(entry); -} -EXPORT_SYMBOL(debug_dma_map_page); - -void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) -{ - struct dma_debug_entry ref; - struct dma_debug_entry *entry; - struct hash_bucket *bucket; - unsigned long flags; - - if (unlikely(dma_debug_disabled())) - return; - - ref.dev = dev; - ref.dev_addr = dma_addr; - bucket = get_hash_bucket(&ref, &flags); - - list_for_each_entry(entry, &bucket->list, list) { - if (!exact_match(&ref, entry)) - continue; - - /* - * The same physical address can be mapped multiple - * times. Without a hardware IOMMU this results in the - * same device addresses being put into the dma-debug - * hash multiple times too. This can result in false - * positives being reported. Therefore we implement a - * best-fit algorithm here which updates the first entry - * from the hash which fits the reference value and is - * not currently listed as being checked. - */ - if (entry->map_err_type == MAP_ERR_NOT_CHECKED) { - entry->map_err_type = MAP_ERR_CHECKED; - break; - } - } - - put_hash_bucket(bucket, &flags); -} -EXPORT_SYMBOL(debug_dma_mapping_error); - -void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, - size_t size, int direction, bool map_single) -{ - struct dma_debug_entry ref = { - .type = dma_debug_page, - .dev = dev, - .dev_addr = addr, - .size = size, - .direction = direction, - }; - - if (unlikely(dma_debug_disabled())) - return; - - if (map_single) - ref.type = dma_debug_single; - - check_unmap(&ref); -} -EXPORT_SYMBOL(debug_dma_unmap_page); - -void debug_dma_map_sg(struct device *dev, struct scatterlist *sg, - int nents, int mapped_ents, int direction) -{ - struct dma_debug_entry *entry; - struct scatterlist *s; - int i; - - if (unlikely(dma_debug_disabled())) - return; - - for_each_sg(sg, s, mapped_ents, i) { - entry = dma_entry_alloc(); - if (!entry) - return; - - entry->type = dma_debug_sg; - entry->dev = dev; - entry->pfn = page_to_pfn(sg_page(s)); - entry->offset = s->offset, - entry->size = sg_dma_len(s); - entry->dev_addr = sg_dma_address(s); - entry->direction = direction; - entry->sg_call_ents = nents; - entry->sg_mapped_ents = mapped_ents; - - check_for_stack(dev, sg_page(s), s->offset); - - if (!PageHighMem(sg_page(s))) { - check_for_illegal_area(dev, sg_virt(s), sg_dma_len(s)); - } - - check_sg_segment(dev, s); - - add_dma_entry(entry); - } -} -EXPORT_SYMBOL(debug_dma_map_sg); - -static int get_nr_mapped_entries(struct device *dev, - struct dma_debug_entry *ref) -{ - struct dma_debug_entry *entry; - struct hash_bucket *bucket; - unsigned long flags; - int mapped_ents; - - bucket = get_hash_bucket(ref, &flags); - entry = bucket_find_exact(bucket, ref); - mapped_ents = 0; - - if (entry) - mapped_ents = entry->sg_mapped_ents; - put_hash_bucket(bucket, &flags); - - return mapped_ents; -} - -void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, - int nelems, int dir) -{ - struct scatterlist *s; - int mapped_ents = 0, i; - - if (unlikely(dma_debug_disabled())) - return; - - for_each_sg(sglist, s, nelems, i) { - - struct dma_debug_entry ref = { - .type = dma_debug_sg, - .dev = dev, - .pfn = page_to_pfn(sg_page(s)), - .offset = s->offset, - .dev_addr = sg_dma_address(s), - .size = sg_dma_len(s), - .direction = dir, - .sg_call_ents = nelems, - }; - - if (mapped_ents && i >= mapped_ents) - break; - - if (!i) - mapped_ents = get_nr_mapped_entries(dev, &ref); - - check_unmap(&ref); - } -} -EXPORT_SYMBOL(debug_dma_unmap_sg); - -void debug_dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t dma_addr, void *virt) -{ - struct dma_debug_entry *entry; - - if (unlikely(dma_debug_disabled())) - return; - - if (unlikely(virt == NULL)) - return; - - /* handle vmalloc and linear addresses */ - if (!is_vmalloc_addr(virt) && !virt_addr_valid(virt)) - return; - - entry = dma_entry_alloc(); - if (!entry) - return; - - entry->type = dma_debug_coherent; - entry->dev = dev; - entry->offset = offset_in_page(virt); - entry->size = size; - entry->dev_addr = dma_addr; - entry->direction = DMA_BIDIRECTIONAL; - - if (is_vmalloc_addr(virt)) - entry->pfn = vmalloc_to_pfn(virt); - else - entry->pfn = page_to_pfn(virt_to_page(virt)); - - add_dma_entry(entry); -} -EXPORT_SYMBOL(debug_dma_alloc_coherent); - -void debug_dma_free_coherent(struct device *dev, size_t size, - void *virt, dma_addr_t addr) -{ - struct dma_debug_entry ref = { - .type = dma_debug_coherent, - .dev = dev, - .offset = offset_in_page(virt), - .dev_addr = addr, - .size = size, - .direction = DMA_BIDIRECTIONAL, - }; - - /* handle vmalloc and linear addresses */ - if (!is_vmalloc_addr(virt) && !virt_addr_valid(virt)) - return; - - if (is_vmalloc_addr(virt)) - ref.pfn = vmalloc_to_pfn(virt); - else - ref.pfn = page_to_pfn(virt_to_page(virt)); - - if (unlikely(dma_debug_disabled())) - return; - - check_unmap(&ref); -} -EXPORT_SYMBOL(debug_dma_free_coherent); - -void debug_dma_map_resource(struct device *dev, phys_addr_t addr, size_t size, - int direction, dma_addr_t dma_addr) -{ - struct dma_debug_entry *entry; - - if (unlikely(dma_debug_disabled())) - return; - - entry = dma_entry_alloc(); - if (!entry) - return; - - entry->type = dma_debug_resource; - entry->dev = dev; - entry->pfn = PHYS_PFN(addr); - entry->offset = offset_in_page(addr); - entry->size = size; - entry->dev_addr = dma_addr; - entry->direction = direction; - entry->map_err_type = MAP_ERR_NOT_CHECKED; - - add_dma_entry(entry); -} -EXPORT_SYMBOL(debug_dma_map_resource); - -void debug_dma_unmap_resource(struct device *dev, dma_addr_t dma_addr, - size_t size, int direction) -{ - struct dma_debug_entry ref = { - .type = dma_debug_resource, - .dev = dev, - .dev_addr = dma_addr, - .size = size, - .direction = direction, - }; - - if (unlikely(dma_debug_disabled())) - return; - - check_unmap(&ref); -} -EXPORT_SYMBOL(debug_dma_unmap_resource); - -void debug_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, - size_t size, int direction) -{ - struct dma_debug_entry ref; - - if (unlikely(dma_debug_disabled())) - return; - - ref.type = dma_debug_single; - ref.dev = dev; - ref.dev_addr = dma_handle; - ref.size = size; - ref.direction = direction; - ref.sg_call_ents = 0; - - check_sync(dev, &ref, true); -} -EXPORT_SYMBOL(debug_dma_sync_single_for_cpu); - -void debug_dma_sync_single_for_device(struct device *dev, - dma_addr_t dma_handle, size_t size, - int direction) -{ - struct dma_debug_entry ref; - - if (unlikely(dma_debug_disabled())) - return; - - ref.type = dma_debug_single; - ref.dev = dev; - ref.dev_addr = dma_handle; - ref.size = size; - ref.direction = direction; - ref.sg_call_ents = 0; - - check_sync(dev, &ref, false); -} -EXPORT_SYMBOL(debug_dma_sync_single_for_device); - -void debug_dma_sync_single_range_for_cpu(struct device *dev, - dma_addr_t dma_handle, - unsigned long offset, size_t size, - int direction) -{ - struct dma_debug_entry ref; - - if (unlikely(dma_debug_disabled())) - return; - - ref.type = dma_debug_single; - ref.dev = dev; - ref.dev_addr = dma_handle; - ref.size = offset + size; - ref.direction = direction; - ref.sg_call_ents = 0; - - check_sync(dev, &ref, true); -} -EXPORT_SYMBOL(debug_dma_sync_single_range_for_cpu); - -void debug_dma_sync_single_range_for_device(struct device *dev, - dma_addr_t dma_handle, - unsigned long offset, - size_t size, int direction) -{ - struct dma_debug_entry ref; - - if (unlikely(dma_debug_disabled())) - return; - - ref.type = dma_debug_single; - ref.dev = dev; - ref.dev_addr = dma_handle; - ref.size = offset + size; - ref.direction = direction; - ref.sg_call_ents = 0; - - check_sync(dev, &ref, false); -} -EXPORT_SYMBOL(debug_dma_sync_single_range_for_device); - -void debug_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, - int nelems, int direction) -{ - struct scatterlist *s; - int mapped_ents = 0, i; - - if (unlikely(dma_debug_disabled())) - return; - - for_each_sg(sg, s, nelems, i) { - - struct dma_debug_entry ref = { - .type = dma_debug_sg, - .dev = dev, - .pfn = page_to_pfn(sg_page(s)), - .offset = s->offset, - .dev_addr = sg_dma_address(s), - .size = sg_dma_len(s), - .direction = direction, - .sg_call_ents = nelems, - }; - - if (!i) - mapped_ents = get_nr_mapped_entries(dev, &ref); - - if (i >= mapped_ents) - break; - - check_sync(dev, &ref, true); - } -} -EXPORT_SYMBOL(debug_dma_sync_sg_for_cpu); - -void debug_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, - int nelems, int direction) -{ - struct scatterlist *s; - int mapped_ents = 0, i; - - if (unlikely(dma_debug_disabled())) - return; - - for_each_sg(sg, s, nelems, i) { - - struct dma_debug_entry ref = { - .type = dma_debug_sg, - .dev = dev, - .pfn = page_to_pfn(sg_page(s)), - .offset = s->offset, - .dev_addr = sg_dma_address(s), - .size = sg_dma_len(s), - .direction = direction, - .sg_call_ents = nelems, - }; - if (!i) - mapped_ents = get_nr_mapped_entries(dev, &ref); - - if (i >= mapped_ents) - break; - - check_sync(dev, &ref, false); - } -} -EXPORT_SYMBOL(debug_dma_sync_sg_for_device); - -static int __init dma_debug_driver_setup(char *str) -{ - int i; - - for (i = 0; i < NAME_MAX_LEN - 1; ++i, ++str) { - current_driver_name[i] = *str; - if (*str == 0) - break; - } - - if (current_driver_name[0]) - pr_info("DMA-API: enable driver filter for driver [%s]\n", - current_driver_name); - - - return 1; -} -__setup("dma_debug_driver=", dma_debug_driver_setup); diff --git a/lib/dma-direct.c b/lib/dma-direct.c deleted file mode 100644 index 8be8106270c2..000000000000 --- a/lib/dma-direct.c +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * DMA operations that map physical memory directly without using an IOMMU or - * flushing caches. - */ -#include -#include -#include -#include -#include -#include -#include - -#define DIRECT_MAPPING_ERROR 0 - -/* - * Most architectures use ZONE_DMA for the first 16 Megabytes, but - * some use it for entirely different regions: - */ -#ifndef ARCH_ZONE_DMA_BITS -#define ARCH_ZONE_DMA_BITS 24 -#endif - -/* - * For AMD SEV all DMA must be to unencrypted addresses. - */ -static inline bool force_dma_unencrypted(void) -{ - return sev_active(); -} - -static bool -check_addr(struct device *dev, dma_addr_t dma_addr, size_t size, - const char *caller) -{ - if (unlikely(dev && !dma_capable(dev, dma_addr, size))) { - if (!dev->dma_mask) { - dev_err(dev, - "%s: call on device without dma_mask\n", - caller); - return false; - } - - if (*dev->dma_mask >= DMA_BIT_MASK(32)) { - dev_err(dev, - "%s: overflow %pad+%zu of device mask %llx\n", - caller, &dma_addr, size, *dev->dma_mask); - } - return false; - } - return true; -} - -static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size) -{ - dma_addr_t addr = force_dma_unencrypted() ? - __phys_to_dma(dev, phys) : phys_to_dma(dev, phys); - return addr + size - 1 <= dev->coherent_dma_mask; -} - -void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, - gfp_t gfp, unsigned long attrs) -{ - unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT; - int page_order = get_order(size); - struct page *page = NULL; - void *ret; - - /* we always manually zero the memory once we are done: */ - gfp &= ~__GFP_ZERO; - - /* GFP_DMA32 and GFP_DMA are no ops without the corresponding zones: */ - if (dev->coherent_dma_mask <= DMA_BIT_MASK(ARCH_ZONE_DMA_BITS)) - gfp |= GFP_DMA; - if (dev->coherent_dma_mask <= DMA_BIT_MASK(32) && !(gfp & GFP_DMA)) - gfp |= GFP_DMA32; - -again: - /* CMA can be used only in the context which permits sleeping */ - if (gfpflags_allow_blocking(gfp)) { - page = dma_alloc_from_contiguous(dev, count, page_order, gfp); - if (page && !dma_coherent_ok(dev, page_to_phys(page), size)) { - dma_release_from_contiguous(dev, page, count); - page = NULL; - } - } - if (!page) - page = alloc_pages_node(dev_to_node(dev), gfp, page_order); - - if (page && !dma_coherent_ok(dev, page_to_phys(page), size)) { - __free_pages(page, page_order); - page = NULL; - - if (IS_ENABLED(CONFIG_ZONE_DMA32) && - dev->coherent_dma_mask < DMA_BIT_MASK(64) && - !(gfp & (GFP_DMA32 | GFP_DMA))) { - gfp |= GFP_DMA32; - goto again; - } - - if (IS_ENABLED(CONFIG_ZONE_DMA) && - dev->coherent_dma_mask < DMA_BIT_MASK(32) && - !(gfp & GFP_DMA)) { - gfp = (gfp & ~GFP_DMA32) | GFP_DMA; - goto again; - } - } - - if (!page) - return NULL; - ret = page_address(page); - if (force_dma_unencrypted()) { - set_memory_decrypted((unsigned long)ret, 1 << page_order); - *dma_handle = __phys_to_dma(dev, page_to_phys(page)); - } else { - *dma_handle = phys_to_dma(dev, page_to_phys(page)); - } - memset(ret, 0, size); - return ret; -} - -/* - * NOTE: this function must never look at the dma_addr argument, because we want - * to be able to use it as a helper for iommu implementations as well. - */ -void dma_direct_free(struct device *dev, size_t size, void *cpu_addr, - dma_addr_t dma_addr, unsigned long attrs) -{ - unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT; - unsigned int page_order = get_order(size); - - if (force_dma_unencrypted()) - set_memory_encrypted((unsigned long)cpu_addr, 1 << page_order); - if (!dma_release_from_contiguous(dev, virt_to_page(cpu_addr), count)) - free_pages((unsigned long)cpu_addr, page_order); -} - -dma_addr_t dma_direct_map_page(struct device *dev, struct page *page, - unsigned long offset, size_t size, enum dma_data_direction dir, - unsigned long attrs) -{ - dma_addr_t dma_addr = phys_to_dma(dev, page_to_phys(page)) + offset; - - if (!check_addr(dev, dma_addr, size, __func__)) - return DIRECT_MAPPING_ERROR; - return dma_addr; -} - -int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents, - enum dma_data_direction dir, unsigned long attrs) -{ - int i; - struct scatterlist *sg; - - for_each_sg(sgl, sg, nents, i) { - BUG_ON(!sg_page(sg)); - - sg_dma_address(sg) = phys_to_dma(dev, sg_phys(sg)); - if (!check_addr(dev, sg_dma_address(sg), sg->length, __func__)) - return 0; - sg_dma_len(sg) = sg->length; - } - - return nents; -} - -int dma_direct_supported(struct device *dev, u64 mask) -{ -#ifdef CONFIG_ZONE_DMA - if (mask < DMA_BIT_MASK(ARCH_ZONE_DMA_BITS)) - return 0; -#else - /* - * Because 32-bit DMA masks are so common we expect every architecture - * to be able to satisfy them - either by not supporting more physical - * memory, or by providing a ZONE_DMA32. If neither is the case, the - * architecture needs to use an IOMMU instead of the direct mapping. - */ - if (mask < DMA_BIT_MASK(32)) - return 0; -#endif - /* - * Various PCI/PCIe bridges have broken support for > 32bit DMA even - * if the device itself might support it. - */ - if (dev->dma_32bit_limit && mask > DMA_BIT_MASK(32)) - return 0; - return 1; -} - -int dma_direct_mapping_error(struct device *dev, dma_addr_t dma_addr) -{ - return dma_addr == DIRECT_MAPPING_ERROR; -} - -const struct dma_map_ops dma_direct_ops = { - .alloc = dma_direct_alloc, - .free = dma_direct_free, - .map_page = dma_direct_map_page, - .map_sg = dma_direct_map_sg, - .dma_supported = dma_direct_supported, - .mapping_error = dma_direct_mapping_error, -}; -EXPORT_SYMBOL(dma_direct_ops); diff --git a/lib/dma-noncoherent.c b/lib/dma-noncoherent.c deleted file mode 100644 index 79e9a757387f..000000000000 --- a/lib/dma-noncoherent.c +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2018 Christoph Hellwig. - * - * DMA operations that map physical memory directly without providing cache - * coherence. - */ -#include -#include -#include -#include -#include - -static void dma_noncoherent_sync_single_for_device(struct device *dev, - dma_addr_t addr, size_t size, enum dma_data_direction dir) -{ - arch_sync_dma_for_device(dev, dma_to_phys(dev, addr), size, dir); -} - -static void dma_noncoherent_sync_sg_for_device(struct device *dev, - struct scatterlist *sgl, int nents, enum dma_data_direction dir) -{ - struct scatterlist *sg; - int i; - - for_each_sg(sgl, sg, nents, i) - arch_sync_dma_for_device(dev, sg_phys(sg), sg->length, dir); -} - -static dma_addr_t dma_noncoherent_map_page(struct device *dev, struct page *page, - unsigned long offset, size_t size, enum dma_data_direction dir, - unsigned long attrs) -{ - dma_addr_t addr; - - addr = dma_direct_map_page(dev, page, offset, size, dir, attrs); - if (!dma_mapping_error(dev, addr) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) - arch_sync_dma_for_device(dev, page_to_phys(page) + offset, - size, dir); - return addr; -} - -static int dma_noncoherent_map_sg(struct device *dev, struct scatterlist *sgl, - int nents, enum dma_data_direction dir, unsigned long attrs) -{ - nents = dma_direct_map_sg(dev, sgl, nents, dir, attrs); - if (nents > 0 && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) - dma_noncoherent_sync_sg_for_device(dev, sgl, nents, dir); - return nents; -} - -#ifdef CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU -static void dma_noncoherent_sync_single_for_cpu(struct device *dev, - dma_addr_t addr, size_t size, enum dma_data_direction dir) -{ - arch_sync_dma_for_cpu(dev, dma_to_phys(dev, addr), size, dir); -} - -static void dma_noncoherent_sync_sg_for_cpu(struct device *dev, - struct scatterlist *sgl, int nents, enum dma_data_direction dir) -{ - struct scatterlist *sg; - int i; - - for_each_sg(sgl, sg, nents, i) - arch_sync_dma_for_cpu(dev, sg_phys(sg), sg->length, dir); -} - -static void dma_noncoherent_unmap_page(struct device *dev, dma_addr_t addr, - size_t size, enum dma_data_direction dir, unsigned long attrs) -{ - if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC)) - dma_noncoherent_sync_single_for_cpu(dev, addr, size, dir); -} - -static void dma_noncoherent_unmap_sg(struct device *dev, struct scatterlist *sgl, - int nents, enum dma_data_direction dir, unsigned long attrs) -{ - if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC)) - dma_noncoherent_sync_sg_for_cpu(dev, sgl, nents, dir); -} -#endif - -const struct dma_map_ops dma_noncoherent_ops = { - .alloc = arch_dma_alloc, - .free = arch_dma_free, - .mmap = arch_dma_mmap, - .sync_single_for_device = dma_noncoherent_sync_single_for_device, - .sync_sg_for_device = dma_noncoherent_sync_sg_for_device, - .map_page = dma_noncoherent_map_page, - .map_sg = dma_noncoherent_map_sg, -#ifdef CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU - .sync_single_for_cpu = dma_noncoherent_sync_single_for_cpu, - .sync_sg_for_cpu = dma_noncoherent_sync_sg_for_cpu, - .unmap_page = dma_noncoherent_unmap_page, - .unmap_sg = dma_noncoherent_unmap_sg, -#endif - .dma_supported = dma_direct_supported, - .mapping_error = dma_direct_mapping_error, - .cache_sync = arch_dma_cache_sync, -}; -EXPORT_SYMBOL(dma_noncoherent_ops); diff --git a/lib/dma-virt.c b/lib/dma-virt.c deleted file mode 100644 index 8e61a02ef9ca..000000000000 --- a/lib/dma-virt.c +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * lib/dma-virt.c - * - * DMA operations that map to virtual addresses without flushing memory. - */ -#include -#include -#include -#include - -static void *dma_virt_alloc(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp, - unsigned long attrs) -{ - void *ret; - - ret = (void *)__get_free_pages(gfp, get_order(size)); - if (ret) - *dma_handle = (uintptr_t)ret; - return ret; -} - -static void dma_virt_free(struct device *dev, size_t size, - void *cpu_addr, dma_addr_t dma_addr, - unsigned long attrs) -{ - free_pages((unsigned long)cpu_addr, get_order(size)); -} - -static dma_addr_t dma_virt_map_page(struct device *dev, struct page *page, - unsigned long offset, size_t size, - enum dma_data_direction dir, - unsigned long attrs) -{ - return (uintptr_t)(page_address(page) + offset); -} - -static int dma_virt_map_sg(struct device *dev, struct scatterlist *sgl, - int nents, enum dma_data_direction dir, - unsigned long attrs) -{ - int i; - struct scatterlist *sg; - - for_each_sg(sgl, sg, nents, i) { - BUG_ON(!sg_page(sg)); - sg_dma_address(sg) = (uintptr_t)sg_virt(sg); - sg_dma_len(sg) = sg->length; - } - - return nents; -} - -const struct dma_map_ops dma_virt_ops = { - .alloc = dma_virt_alloc, - .free = dma_virt_free, - .map_page = dma_virt_map_page, - .map_sg = dma_virt_map_sg, -}; -EXPORT_SYMBOL(dma_virt_ops); diff --git a/lib/swiotlb.c b/lib/swiotlb.c deleted file mode 100644 index 04b68d9dffac..000000000000 --- a/lib/swiotlb.c +++ /dev/null @@ -1,1087 +0,0 @@ -/* - * Dynamic DMA mapping support. - * - * This implementation is a fallback for platforms that do not support - * I/O TLBs (aka DMA address translation hardware). - * Copyright (C) 2000 Asit Mallick - * Copyright (C) 2000 Goutham Rao - * Copyright (C) 2000, 2003 Hewlett-Packard Co - * David Mosberger-Tang - * - * 03/05/07 davidm Switch from PCI-DMA to generic device DMA API. - * 00/12/13 davidm Rename to swiotlb.c and add mark_clean() to avoid - * unnecessary i-cache flushing. - * 04/07/.. ak Better overflow handling. Assorted fixes. - * 05/09/10 linville Add support for syncing ranges, support syncing for - * DMA_BIDIRECTIONAL mappings, miscellaneous cleanup. - * 08/12/11 beckyb Add highmem support - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#define CREATE_TRACE_POINTS -#include - -#define OFFSET(val,align) ((unsigned long) \ - ( (val) & ( (align) - 1))) - -#define SLABS_PER_PAGE (1 << (PAGE_SHIFT - IO_TLB_SHIFT)) - -/* - * Minimum IO TLB size to bother booting with. Systems with mainly - * 64bit capable cards will only lightly use the swiotlb. If we can't - * allocate a contiguous 1MB, we're probably in trouble anyway. - */ -#define IO_TLB_MIN_SLABS ((1<<20) >> IO_TLB_SHIFT) - -enum swiotlb_force swiotlb_force; - -/* - * Used to do a quick range check in swiotlb_tbl_unmap_single and - * swiotlb_tbl_sync_single_*, to see if the memory was in fact allocated by this - * API. - */ -static phys_addr_t io_tlb_start, io_tlb_end; - -/* - * The number of IO TLB blocks (in groups of 64) between io_tlb_start and - * io_tlb_end. This is command line adjustable via setup_io_tlb_npages. - */ -static unsigned long io_tlb_nslabs; - -/* - * When the IOMMU overflows we return a fallback buffer. This sets the size. - */ -static unsigned long io_tlb_overflow = 32*1024; - -static phys_addr_t io_tlb_overflow_buffer; - -/* - * This is a free list describing the number of free entries available from - * each index - */ -static unsigned int *io_tlb_list; -static unsigned int io_tlb_index; - -/* - * Max segment that we can provide which (if pages are contingous) will - * not be bounced (unless SWIOTLB_FORCE is set). - */ -unsigned int max_segment; - -/* - * We need to save away the original address corresponding to a mapped entry - * for the sync operations. - */ -#define INVALID_PHYS_ADDR (~(phys_addr_t)0) -static phys_addr_t *io_tlb_orig_addr; - -/* - * Protect the above data structures in the map and unmap calls - */ -static DEFINE_SPINLOCK(io_tlb_lock); - -static int late_alloc; - -static int __init -setup_io_tlb_npages(char *str) -{ - if (isdigit(*str)) { - io_tlb_nslabs = simple_strtoul(str, &str, 0); - /* avoid tail segment of size < IO_TLB_SEGSIZE */ - io_tlb_nslabs = ALIGN(io_tlb_nslabs, IO_TLB_SEGSIZE); - } - if (*str == ',') - ++str; - if (!strcmp(str, "force")) { - swiotlb_force = SWIOTLB_FORCE; - } else if (!strcmp(str, "noforce")) { - swiotlb_force = SWIOTLB_NO_FORCE; - io_tlb_nslabs = 1; - } - - return 0; -} -early_param("swiotlb", setup_io_tlb_npages); -/* make io_tlb_overflow tunable too? */ - -unsigned long swiotlb_nr_tbl(void) -{ - return io_tlb_nslabs; -} -EXPORT_SYMBOL_GPL(swiotlb_nr_tbl); - -unsigned int swiotlb_max_segment(void) -{ - return max_segment; -} -EXPORT_SYMBOL_GPL(swiotlb_max_segment); - -void swiotlb_set_max_segment(unsigned int val) -{ - if (swiotlb_force == SWIOTLB_FORCE) - max_segment = 1; - else - max_segment = rounddown(val, PAGE_SIZE); -} - -/* default to 64MB */ -#define IO_TLB_DEFAULT_SIZE (64UL<<20) -unsigned long swiotlb_size_or_default(void) -{ - unsigned long size; - - size = io_tlb_nslabs << IO_TLB_SHIFT; - - return size ? size : (IO_TLB_DEFAULT_SIZE); -} - -static bool no_iotlb_memory; - -void swiotlb_print_info(void) -{ - unsigned long bytes = io_tlb_nslabs << IO_TLB_SHIFT; - unsigned char *vstart, *vend; - - if (no_iotlb_memory) { - pr_warn("software IO TLB: No low mem\n"); - return; - } - - vstart = phys_to_virt(io_tlb_start); - vend = phys_to_virt(io_tlb_end); - - printk(KERN_INFO "software IO TLB [mem %#010llx-%#010llx] (%luMB) mapped at [%p-%p]\n", - (unsigned long long)io_tlb_start, - (unsigned long long)io_tlb_end, - bytes >> 20, vstart, vend - 1); -} - -/* - * Early SWIOTLB allocation may be too early to allow an architecture to - * perform the desired operations. This function allows the architecture to - * call SWIOTLB when the operations are possible. It needs to be called - * before the SWIOTLB memory is used. - */ -void __init swiotlb_update_mem_attributes(void) -{ - void *vaddr; - unsigned long bytes; - - if (no_iotlb_memory || late_alloc) - return; - - vaddr = phys_to_virt(io_tlb_start); - bytes = PAGE_ALIGN(io_tlb_nslabs << IO_TLB_SHIFT); - set_memory_decrypted((unsigned long)vaddr, bytes >> PAGE_SHIFT); - memset(vaddr, 0, bytes); - - vaddr = phys_to_virt(io_tlb_overflow_buffer); - bytes = PAGE_ALIGN(io_tlb_overflow); - set_memory_decrypted((unsigned long)vaddr, bytes >> PAGE_SHIFT); - memset(vaddr, 0, bytes); -} - -int __init swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int verbose) -{ - void *v_overflow_buffer; - unsigned long i, bytes; - - bytes = nslabs << IO_TLB_SHIFT; - - io_tlb_nslabs = nslabs; - io_tlb_start = __pa(tlb); - io_tlb_end = io_tlb_start + bytes; - - /* - * Get the overflow emergency buffer - */ - v_overflow_buffer = memblock_virt_alloc_low_nopanic( - PAGE_ALIGN(io_tlb_overflow), - PAGE_SIZE); - if (!v_overflow_buffer) - return -ENOMEM; - - io_tlb_overflow_buffer = __pa(v_overflow_buffer); - - /* - * Allocate and initialize the free list array. This array is used - * to find contiguous free memory regions of size up to IO_TLB_SEGSIZE - * between io_tlb_start and io_tlb_end. - */ - io_tlb_list = memblock_virt_alloc( - PAGE_ALIGN(io_tlb_nslabs * sizeof(int)), - PAGE_SIZE); - io_tlb_orig_addr = memblock_virt_alloc( - PAGE_ALIGN(io_tlb_nslabs * sizeof(phys_addr_t)), - PAGE_SIZE); - for (i = 0; i < io_tlb_nslabs; i++) { - io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE); - io_tlb_orig_addr[i] = INVALID_PHYS_ADDR; - } - io_tlb_index = 0; - - if (verbose) - swiotlb_print_info(); - - swiotlb_set_max_segment(io_tlb_nslabs << IO_TLB_SHIFT); - return 0; -} - -/* - * Statically reserve bounce buffer space and initialize bounce buffer data - * structures for the software IO TLB used to implement the DMA API. - */ -void __init -swiotlb_init(int verbose) -{ - size_t default_size = IO_TLB_DEFAULT_SIZE; - unsigned char *vstart; - unsigned long bytes; - - if (!io_tlb_nslabs) { - io_tlb_nslabs = (default_size >> IO_TLB_SHIFT); - io_tlb_nslabs = ALIGN(io_tlb_nslabs, IO_TLB_SEGSIZE); - } - - bytes = io_tlb_nslabs << IO_TLB_SHIFT; - - /* Get IO TLB memory from the low pages */ - vstart = memblock_virt_alloc_low_nopanic(PAGE_ALIGN(bytes), PAGE_SIZE); - if (vstart && !swiotlb_init_with_tbl(vstart, io_tlb_nslabs, verbose)) - return; - - if (io_tlb_start) - memblock_free_early(io_tlb_start, - PAGE_ALIGN(io_tlb_nslabs << IO_TLB_SHIFT)); - pr_warn("Cannot allocate SWIOTLB buffer"); - no_iotlb_memory = true; -} - -/* - * Systems with larger DMA zones (those that don't support ISA) can - * initialize the swiotlb later using the slab allocator if needed. - * This should be just like above, but with some error catching. - */ -int -swiotlb_late_init_with_default_size(size_t default_size) -{ - unsigned long bytes, req_nslabs = io_tlb_nslabs; - unsigned char *vstart = NULL; - unsigned int order; - int rc = 0; - - if (!io_tlb_nslabs) { - io_tlb_nslabs = (default_size >> IO_TLB_SHIFT); - io_tlb_nslabs = ALIGN(io_tlb_nslabs, IO_TLB_SEGSIZE); - } - - /* - * Get IO TLB memory from the low pages - */ - order = get_order(io_tlb_nslabs << IO_TLB_SHIFT); - io_tlb_nslabs = SLABS_PER_PAGE << order; - bytes = io_tlb_nslabs << IO_TLB_SHIFT; - - while ((SLABS_PER_PAGE << order) > IO_TLB_MIN_SLABS) { - vstart = (void *)__get_free_pages(GFP_DMA | __GFP_NOWARN, - order); - if (vstart) - break; - order--; - } - - if (!vstart) { - io_tlb_nslabs = req_nslabs; - return -ENOMEM; - } - if (order != get_order(bytes)) { - printk(KERN_WARNING "Warning: only able to allocate %ld MB " - "for software IO TLB\n", (PAGE_SIZE << order) >> 20); - io_tlb_nslabs = SLABS_PER_PAGE << order; - } - rc = swiotlb_late_init_with_tbl(vstart, io_tlb_nslabs); - if (rc) - free_pages((unsigned long)vstart, order); - - return rc; -} - -int -swiotlb_late_init_with_tbl(char *tlb, unsigned long nslabs) -{ - unsigned long i, bytes; - unsigned char *v_overflow_buffer; - - bytes = nslabs << IO_TLB_SHIFT; - - io_tlb_nslabs = nslabs; - io_tlb_start = virt_to_phys(tlb); - io_tlb_end = io_tlb_start + bytes; - - set_memory_decrypted((unsigned long)tlb, bytes >> PAGE_SHIFT); - memset(tlb, 0, bytes); - - /* - * Get the overflow emergency buffer - */ - v_overflow_buffer = (void *)__get_free_pages(GFP_DMA, - get_order(io_tlb_overflow)); - if (!v_overflow_buffer) - goto cleanup2; - - set_memory_decrypted((unsigned long)v_overflow_buffer, - io_tlb_overflow >> PAGE_SHIFT); - memset(v_overflow_buffer, 0, io_tlb_overflow); - io_tlb_overflow_buffer = virt_to_phys(v_overflow_buffer); - - /* - * Allocate and initialize the free list array. This array is used - * to find contiguous free memory regions of size up to IO_TLB_SEGSIZE - * between io_tlb_start and io_tlb_end. - */ - io_tlb_list = (unsigned int *)__get_free_pages(GFP_KERNEL, - get_order(io_tlb_nslabs * sizeof(int))); - if (!io_tlb_list) - goto cleanup3; - - io_tlb_orig_addr = (phys_addr_t *) - __get_free_pages(GFP_KERNEL, - get_order(io_tlb_nslabs * - sizeof(phys_addr_t))); - if (!io_tlb_orig_addr) - goto cleanup4; - - for (i = 0; i < io_tlb_nslabs; i++) { - io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE); - io_tlb_orig_addr[i] = INVALID_PHYS_ADDR; - } - io_tlb_index = 0; - - swiotlb_print_info(); - - late_alloc = 1; - - swiotlb_set_max_segment(io_tlb_nslabs << IO_TLB_SHIFT); - - return 0; - -cleanup4: - free_pages((unsigned long)io_tlb_list, get_order(io_tlb_nslabs * - sizeof(int))); - io_tlb_list = NULL; -cleanup3: - free_pages((unsigned long)v_overflow_buffer, - get_order(io_tlb_overflow)); - io_tlb_overflow_buffer = 0; -cleanup2: - io_tlb_end = 0; - io_tlb_start = 0; - io_tlb_nslabs = 0; - max_segment = 0; - return -ENOMEM; -} - -void __init swiotlb_exit(void) -{ - if (!io_tlb_orig_addr) - return; - - if (late_alloc) { - free_pages((unsigned long)phys_to_virt(io_tlb_overflow_buffer), - get_order(io_tlb_overflow)); - free_pages((unsigned long)io_tlb_orig_addr, - get_order(io_tlb_nslabs * sizeof(phys_addr_t))); - free_pages((unsigned long)io_tlb_list, get_order(io_tlb_nslabs * - sizeof(int))); - free_pages((unsigned long)phys_to_virt(io_tlb_start), - get_order(io_tlb_nslabs << IO_TLB_SHIFT)); - } else { - memblock_free_late(io_tlb_overflow_buffer, - PAGE_ALIGN(io_tlb_overflow)); - memblock_free_late(__pa(io_tlb_orig_addr), - PAGE_ALIGN(io_tlb_nslabs * sizeof(phys_addr_t))); - memblock_free_late(__pa(io_tlb_list), - PAGE_ALIGN(io_tlb_nslabs * sizeof(int))); - memblock_free_late(io_tlb_start, - PAGE_ALIGN(io_tlb_nslabs << IO_TLB_SHIFT)); - } - io_tlb_nslabs = 0; - max_segment = 0; -} - -int is_swiotlb_buffer(phys_addr_t paddr) -{ - return paddr >= io_tlb_start && paddr < io_tlb_end; -} - -/* - * Bounce: copy the swiotlb buffer back to the original dma location - */ -static void swiotlb_bounce(phys_addr_t orig_addr, phys_addr_t tlb_addr, - size_t size, enum dma_data_direction dir) -{ - unsigned long pfn = PFN_DOWN(orig_addr); - unsigned char *vaddr = phys_to_virt(tlb_addr); - - if (PageHighMem(pfn_to_page(pfn))) { - /* The buffer does not have a mapping. Map it in and copy */ - unsigned int offset = orig_addr & ~PAGE_MASK; - char *buffer; - unsigned int sz = 0; - unsigned long flags; - - while (size) { - sz = min_t(size_t, PAGE_SIZE - offset, size); - - local_irq_save(flags); - buffer = kmap_atomic(pfn_to_page(pfn)); - if (dir == DMA_TO_DEVICE) - memcpy(vaddr, buffer + offset, sz); - else - memcpy(buffer + offset, vaddr, sz); - kunmap_atomic(buffer); - local_irq_restore(flags); - - size -= sz; - pfn++; - vaddr += sz; - offset = 0; - } - } else if (dir == DMA_TO_DEVICE) { - memcpy(vaddr, phys_to_virt(orig_addr), size); - } else { - memcpy(phys_to_virt(orig_addr), vaddr, size); - } -} - -phys_addr_t swiotlb_tbl_map_single(struct device *hwdev, - dma_addr_t tbl_dma_addr, - phys_addr_t orig_addr, size_t size, - enum dma_data_direction dir, - unsigned long attrs) -{ - unsigned long flags; - phys_addr_t tlb_addr; - unsigned int nslots, stride, index, wrap; - int i; - unsigned long mask; - unsigned long offset_slots; - unsigned long max_slots; - - if (no_iotlb_memory) - panic("Can not allocate SWIOTLB buffer earlier and can't now provide you with the DMA bounce buffer"); - - if (mem_encrypt_active()) - pr_warn_once("%s is active and system is using DMA bounce buffers\n", - sme_active() ? "SME" : "SEV"); - - mask = dma_get_seg_boundary(hwdev); - - tbl_dma_addr &= mask; - - offset_slots = ALIGN(tbl_dma_addr, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT; - - /* - * Carefully handle integer overflow which can occur when mask == ~0UL. - */ - max_slots = mask + 1 - ? ALIGN(mask + 1, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT - : 1UL << (BITS_PER_LONG - IO_TLB_SHIFT); - - /* - * For mappings greater than or equal to a page, we limit the stride - * (and hence alignment) to a page size. - */ - nslots = ALIGN(size, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT; - if (size >= PAGE_SIZE) - stride = (1 << (PAGE_SHIFT - IO_TLB_SHIFT)); - else - stride = 1; - - BUG_ON(!nslots); - - /* - * Find suitable number of IO TLB entries size that will fit this - * request and allocate a buffer from that IO TLB pool. - */ - spin_lock_irqsave(&io_tlb_lock, flags); - index = ALIGN(io_tlb_index, stride); - if (index >= io_tlb_nslabs) - index = 0; - wrap = index; - - do { - while (iommu_is_span_boundary(index, nslots, offset_slots, - max_slots)) { - index += stride; - if (index >= io_tlb_nslabs) - index = 0; - if (index == wrap) - goto not_found; - } - - /* - * If we find a slot that indicates we have 'nslots' number of - * contiguous buffers, we allocate the buffers from that slot - * and mark the entries as '0' indicating unavailable. - */ - if (io_tlb_list[index] >= nslots) { - int count = 0; - - for (i = index; i < (int) (index + nslots); i++) - io_tlb_list[i] = 0; - for (i = index - 1; (OFFSET(i, IO_TLB_SEGSIZE) != IO_TLB_SEGSIZE - 1) && io_tlb_list[i]; i--) - io_tlb_list[i] = ++count; - tlb_addr = io_tlb_start + (index << IO_TLB_SHIFT); - - /* - * Update the indices to avoid searching in the next - * round. - */ - io_tlb_index = ((index + nslots) < io_tlb_nslabs - ? (index + nslots) : 0); - - goto found; - } - index += stride; - if (index >= io_tlb_nslabs) - index = 0; - } while (index != wrap); - -not_found: - spin_unlock_irqrestore(&io_tlb_lock, flags); - if (!(attrs & DMA_ATTR_NO_WARN) && printk_ratelimit()) - dev_warn(hwdev, "swiotlb buffer is full (sz: %zd bytes)\n", size); - return SWIOTLB_MAP_ERROR; -found: - spin_unlock_irqrestore(&io_tlb_lock, flags); - - /* - * Save away the mapping from the original address to the DMA address. - * This is needed when we sync the memory. Then we sync the buffer if - * needed. - */ - for (i = 0; i < nslots; i++) - io_tlb_orig_addr[index+i] = orig_addr + (i << IO_TLB_SHIFT); - if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) && - (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)) - swiotlb_bounce(orig_addr, tlb_addr, size, DMA_TO_DEVICE); - - return tlb_addr; -} - -/* - * Allocates bounce buffer and returns its physical address. - */ -static phys_addr_t -map_single(struct device *hwdev, phys_addr_t phys, size_t size, - enum dma_data_direction dir, unsigned long attrs) -{ - dma_addr_t start_dma_addr; - - if (swiotlb_force == SWIOTLB_NO_FORCE) { - dev_warn_ratelimited(hwdev, "Cannot do DMA to address %pa\n", - &phys); - return SWIOTLB_MAP_ERROR; - } - - start_dma_addr = __phys_to_dma(hwdev, io_tlb_start); - return swiotlb_tbl_map_single(hwdev, start_dma_addr, phys, size, - dir, attrs); -} - -/* - * tlb_addr is the physical address of the bounce buffer to unmap. - */ -void swiotlb_tbl_unmap_single(struct device *hwdev, phys_addr_t tlb_addr, - size_t size, enum dma_data_direction dir, - unsigned long attrs) -{ - unsigned long flags; - int i, count, nslots = ALIGN(size, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT; - int index = (tlb_addr - io_tlb_start) >> IO_TLB_SHIFT; - phys_addr_t orig_addr = io_tlb_orig_addr[index]; - - /* - * First, sync the memory before unmapping the entry - */ - if (orig_addr != INVALID_PHYS_ADDR && - !(attrs & DMA_ATTR_SKIP_CPU_SYNC) && - ((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL))) - swiotlb_bounce(orig_addr, tlb_addr, size, DMA_FROM_DEVICE); - - /* - * Return the buffer to the free list by setting the corresponding - * entries to indicate the number of contiguous entries available. - * While returning the entries to the free list, we merge the entries - * with slots below and above the pool being returned. - */ - spin_lock_irqsave(&io_tlb_lock, flags); - { - count = ((index + nslots) < ALIGN(index + 1, IO_TLB_SEGSIZE) ? - io_tlb_list[index + nslots] : 0); - /* - * Step 1: return the slots to the free list, merging the - * slots with superceeding slots - */ - for (i = index + nslots - 1; i >= index; i--) { - io_tlb_list[i] = ++count; - io_tlb_orig_addr[i] = INVALID_PHYS_ADDR; - } - /* - * Step 2: merge the returned slots with the preceding slots, - * if available (non zero) - */ - for (i = index - 1; (OFFSET(i, IO_TLB_SEGSIZE) != IO_TLB_SEGSIZE -1) && io_tlb_list[i]; i--) - io_tlb_list[i] = ++count; - } - spin_unlock_irqrestore(&io_tlb_lock, flags); -} - -void swiotlb_tbl_sync_single(struct device *hwdev, phys_addr_t tlb_addr, - size_t size, enum dma_data_direction dir, - enum dma_sync_target target) -{ - int index = (tlb_addr - io_tlb_start) >> IO_TLB_SHIFT; - phys_addr_t orig_addr = io_tlb_orig_addr[index]; - - if (orig_addr == INVALID_PHYS_ADDR) - return; - orig_addr += (unsigned long)tlb_addr & ((1 << IO_TLB_SHIFT) - 1); - - switch (target) { - case SYNC_FOR_CPU: - if (likely(dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL)) - swiotlb_bounce(orig_addr, tlb_addr, - size, DMA_FROM_DEVICE); - else - BUG_ON(dir != DMA_TO_DEVICE); - break; - case SYNC_FOR_DEVICE: - if (likely(dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)) - swiotlb_bounce(orig_addr, tlb_addr, - size, DMA_TO_DEVICE); - else - BUG_ON(dir != DMA_FROM_DEVICE); - break; - default: - BUG(); - } -} - -static inline bool dma_coherent_ok(struct device *dev, dma_addr_t addr, - size_t size) -{ - u64 mask = DMA_BIT_MASK(32); - - if (dev && dev->coherent_dma_mask) - mask = dev->coherent_dma_mask; - return addr + size - 1 <= mask; -} - -static void * -swiotlb_alloc_buffer(struct device *dev, size_t size, dma_addr_t *dma_handle, - unsigned long attrs) -{ - phys_addr_t phys_addr; - - if (swiotlb_force == SWIOTLB_NO_FORCE) - goto out_warn; - - phys_addr = swiotlb_tbl_map_single(dev, - __phys_to_dma(dev, io_tlb_start), - 0, size, DMA_FROM_DEVICE, attrs); - if (phys_addr == SWIOTLB_MAP_ERROR) - goto out_warn; - - *dma_handle = __phys_to_dma(dev, phys_addr); - if (!dma_coherent_ok(dev, *dma_handle, size)) - goto out_unmap; - - memset(phys_to_virt(phys_addr), 0, size); - return phys_to_virt(phys_addr); - -out_unmap: - dev_warn(dev, "hwdev DMA mask = 0x%016Lx, dev_addr = 0x%016Lx\n", - (unsigned long long)dev->coherent_dma_mask, - (unsigned long long)*dma_handle); - - /* - * DMA_TO_DEVICE to avoid memcpy in unmap_single. - * DMA_ATTR_SKIP_CPU_SYNC is optional. - */ - swiotlb_tbl_unmap_single(dev, phys_addr, size, DMA_TO_DEVICE, - DMA_ATTR_SKIP_CPU_SYNC); -out_warn: - if (!(attrs & DMA_ATTR_NO_WARN) && printk_ratelimit()) { - dev_warn(dev, - "swiotlb: coherent allocation failed, size=%zu\n", - size); - dump_stack(); - } - return NULL; -} - -static bool swiotlb_free_buffer(struct device *dev, size_t size, - dma_addr_t dma_addr) -{ - phys_addr_t phys_addr = dma_to_phys(dev, dma_addr); - - WARN_ON_ONCE(irqs_disabled()); - - if (!is_swiotlb_buffer(phys_addr)) - return false; - - /* - * DMA_TO_DEVICE to avoid memcpy in swiotlb_tbl_unmap_single. - * DMA_ATTR_SKIP_CPU_SYNC is optional. - */ - swiotlb_tbl_unmap_single(dev, phys_addr, size, DMA_TO_DEVICE, - DMA_ATTR_SKIP_CPU_SYNC); - return true; -} - -static void -swiotlb_full(struct device *dev, size_t size, enum dma_data_direction dir, - int do_panic) -{ - if (swiotlb_force == SWIOTLB_NO_FORCE) - return; - - /* - * Ran out of IOMMU space for this operation. This is very bad. - * Unfortunately the drivers cannot handle this operation properly. - * unless they check for dma_mapping_error (most don't) - * When the mapping is small enough return a static buffer to limit - * the damage, or panic when the transfer is too big. - */ - dev_err_ratelimited(dev, "DMA: Out of SW-IOMMU space for %zu bytes\n", - size); - - if (size <= io_tlb_overflow || !do_panic) - return; - - if (dir == DMA_BIDIRECTIONAL) - panic("DMA: Random memory could be DMA accessed\n"); - if (dir == DMA_FROM_DEVICE) - panic("DMA: Random memory could be DMA written\n"); - if (dir == DMA_TO_DEVICE) - panic("DMA: Random memory could be DMA read\n"); -} - -/* - * Map a single buffer of the indicated size for DMA in streaming mode. The - * physical address to use is returned. - * - * Once the device is given the dma address, the device owns this memory until - * either swiotlb_unmap_page or swiotlb_dma_sync_single is performed. - */ -dma_addr_t swiotlb_map_page(struct device *dev, struct page *page, - unsigned long offset, size_t size, - enum dma_data_direction dir, - unsigned long attrs) -{ - phys_addr_t map, phys = page_to_phys(page) + offset; - dma_addr_t dev_addr = phys_to_dma(dev, phys); - - BUG_ON(dir == DMA_NONE); - /* - * If the address happens to be in the device's DMA window, - * we can safely return the device addr and not worry about bounce - * buffering it. - */ - if (dma_capable(dev, dev_addr, size) && swiotlb_force != SWIOTLB_FORCE) - return dev_addr; - - trace_swiotlb_bounced(dev, dev_addr, size, swiotlb_force); - - /* Oh well, have to allocate and map a bounce buffer. */ - map = map_single(dev, phys, size, dir, attrs); - if (map == SWIOTLB_MAP_ERROR) { - swiotlb_full(dev, size, dir, 1); - return __phys_to_dma(dev, io_tlb_overflow_buffer); - } - - dev_addr = __phys_to_dma(dev, map); - - /* Ensure that the address returned is DMA'ble */ - if (dma_capable(dev, dev_addr, size)) - return dev_addr; - - attrs |= DMA_ATTR_SKIP_CPU_SYNC; - swiotlb_tbl_unmap_single(dev, map, size, dir, attrs); - - return __phys_to_dma(dev, io_tlb_overflow_buffer); -} - -/* - * Unmap a single streaming mode DMA translation. The dma_addr and size must - * match what was provided for in a previous swiotlb_map_page call. All - * other usages are undefined. - * - * After this call, reads by the cpu to the buffer are guaranteed to see - * whatever the device wrote there. - */ -static void unmap_single(struct device *hwdev, dma_addr_t dev_addr, - size_t size, enum dma_data_direction dir, - unsigned long attrs) -{ - phys_addr_t paddr = dma_to_phys(hwdev, dev_addr); - - BUG_ON(dir == DMA_NONE); - - if (is_swiotlb_buffer(paddr)) { - swiotlb_tbl_unmap_single(hwdev, paddr, size, dir, attrs); - return; - } - - if (dir != DMA_FROM_DEVICE) - return; - - /* - * phys_to_virt doesn't work with hihgmem page but we could - * call dma_mark_clean() with hihgmem page here. However, we - * are fine since dma_mark_clean() is null on POWERPC. We can - * make dma_mark_clean() take a physical address if necessary. - */ - dma_mark_clean(phys_to_virt(paddr), size); -} - -void swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr, - size_t size, enum dma_data_direction dir, - unsigned long attrs) -{ - unmap_single(hwdev, dev_addr, size, dir, attrs); -} - -/* - * Make physical memory consistent for a single streaming mode DMA translation - * after a transfer. - * - * If you perform a swiotlb_map_page() but wish to interrogate the buffer - * using the cpu, yet do not wish to teardown the dma mapping, you must - * call this function before doing so. At the next point you give the dma - * address back to the card, you must first perform a - * swiotlb_dma_sync_for_device, and then the device again owns the buffer - */ -static void -swiotlb_sync_single(struct device *hwdev, dma_addr_t dev_addr, - size_t size, enum dma_data_direction dir, - enum dma_sync_target target) -{ - phys_addr_t paddr = dma_to_phys(hwdev, dev_addr); - - BUG_ON(dir == DMA_NONE); - - if (is_swiotlb_buffer(paddr)) { - swiotlb_tbl_sync_single(hwdev, paddr, size, dir, target); - return; - } - - if (dir != DMA_FROM_DEVICE) - return; - - dma_mark_clean(phys_to_virt(paddr), size); -} - -void -swiotlb_sync_single_for_cpu(struct device *hwdev, dma_addr_t dev_addr, - size_t size, enum dma_data_direction dir) -{ - swiotlb_sync_single(hwdev, dev_addr, size, dir, SYNC_FOR_CPU); -} - -void -swiotlb_sync_single_for_device(struct device *hwdev, dma_addr_t dev_addr, - size_t size, enum dma_data_direction dir) -{ - swiotlb_sync_single(hwdev, dev_addr, size, dir, SYNC_FOR_DEVICE); -} - -/* - * Map a set of buffers described by scatterlist in streaming mode for DMA. - * This is the scatter-gather version of the above swiotlb_map_page - * interface. Here the scatter gather list elements are each tagged with the - * appropriate dma address and length. They are obtained via - * sg_dma_{address,length}(SG). - * - * NOTE: An implementation may be able to use a smaller number of - * DMA address/length pairs than there are SG table elements. - * (for example via virtual mapping capabilities) - * The routine returns the number of addr/length pairs actually - * used, at most nents. - * - * Device ownership issues as mentioned above for swiotlb_map_page are the - * same here. - */ -int -swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, int nelems, - enum dma_data_direction dir, unsigned long attrs) -{ - struct scatterlist *sg; - int i; - - BUG_ON(dir == DMA_NONE); - - for_each_sg(sgl, sg, nelems, i) { - phys_addr_t paddr = sg_phys(sg); - dma_addr_t dev_addr = phys_to_dma(hwdev, paddr); - - if (swiotlb_force == SWIOTLB_FORCE || - !dma_capable(hwdev, dev_addr, sg->length)) { - phys_addr_t map = map_single(hwdev, sg_phys(sg), - sg->length, dir, attrs); - if (map == SWIOTLB_MAP_ERROR) { - /* Don't panic here, we expect map_sg users - to do proper error handling. */ - swiotlb_full(hwdev, sg->length, dir, 0); - attrs |= DMA_ATTR_SKIP_CPU_SYNC; - swiotlb_unmap_sg_attrs(hwdev, sgl, i, dir, - attrs); - sg_dma_len(sgl) = 0; - return 0; - } - sg->dma_address = __phys_to_dma(hwdev, map); - } else - sg->dma_address = dev_addr; - sg_dma_len(sg) = sg->length; - } - return nelems; -} - -/* - * Unmap a set of streaming mode DMA translations. Again, cpu read rules - * concerning calls here are the same as for swiotlb_unmap_page() above. - */ -void -swiotlb_unmap_sg_attrs(struct device *hwdev, struct scatterlist *sgl, - int nelems, enum dma_data_direction dir, - unsigned long attrs) -{ - struct scatterlist *sg; - int i; - - BUG_ON(dir == DMA_NONE); - - for_each_sg(sgl, sg, nelems, i) - unmap_single(hwdev, sg->dma_address, sg_dma_len(sg), dir, - attrs); -} - -/* - * Make physical memory consistent for a set of streaming mode DMA translations - * after a transfer. - * - * The same as swiotlb_sync_single_* but for a scatter-gather list, same rules - * and usage. - */ -static void -swiotlb_sync_sg(struct device *hwdev, struct scatterlist *sgl, - int nelems, enum dma_data_direction dir, - enum dma_sync_target target) -{ - struct scatterlist *sg; - int i; - - for_each_sg(sgl, sg, nelems, i) - swiotlb_sync_single(hwdev, sg->dma_address, - sg_dma_len(sg), dir, target); -} - -void -swiotlb_sync_sg_for_cpu(struct device *hwdev, struct scatterlist *sg, - int nelems, enum dma_data_direction dir) -{ - swiotlb_sync_sg(hwdev, sg, nelems, dir, SYNC_FOR_CPU); -} - -void -swiotlb_sync_sg_for_device(struct device *hwdev, struct scatterlist *sg, - int nelems, enum dma_data_direction dir) -{ - swiotlb_sync_sg(hwdev, sg, nelems, dir, SYNC_FOR_DEVICE); -} - -int -swiotlb_dma_mapping_error(struct device *hwdev, dma_addr_t dma_addr) -{ - return (dma_addr == __phys_to_dma(hwdev, io_tlb_overflow_buffer)); -} - -/* - * Return whether the given device DMA address mask can be supported - * properly. For example, if your device can only drive the low 24-bits - * during bus mastering, then you would pass 0x00ffffff as the mask to - * this function. - */ -int -swiotlb_dma_supported(struct device *hwdev, u64 mask) -{ - return __phys_to_dma(hwdev, io_tlb_end - 1) <= mask; -} - -void *swiotlb_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, - gfp_t gfp, unsigned long attrs) -{ - void *vaddr; - - /* temporary workaround: */ - if (gfp & __GFP_NOWARN) - attrs |= DMA_ATTR_NO_WARN; - - /* - * Don't print a warning when the first allocation attempt fails. - * swiotlb_alloc_coherent() will print a warning when the DMA memory - * allocation ultimately failed. - */ - gfp |= __GFP_NOWARN; - - vaddr = dma_direct_alloc(dev, size, dma_handle, gfp, attrs); - if (!vaddr) - vaddr = swiotlb_alloc_buffer(dev, size, dma_handle, attrs); - return vaddr; -} - -void swiotlb_free(struct device *dev, size_t size, void *vaddr, - dma_addr_t dma_addr, unsigned long attrs) -{ - if (!swiotlb_free_buffer(dev, size, dma_addr)) - dma_direct_free(dev, size, vaddr, dma_addr, attrs); -} - -const struct dma_map_ops swiotlb_dma_ops = { - .mapping_error = swiotlb_dma_mapping_error, - .alloc = swiotlb_alloc, - .free = swiotlb_free, - .sync_single_for_cpu = swiotlb_sync_single_for_cpu, - .sync_single_for_device = swiotlb_sync_single_for_device, - .sync_sg_for_cpu = swiotlb_sync_sg_for_cpu, - .sync_sg_for_device = swiotlb_sync_sg_for_device, - .map_sg = swiotlb_map_sg_attrs, - .unmap_sg = swiotlb_unmap_sg_attrs, - .map_page = swiotlb_map_page, - .unmap_page = swiotlb_unmap_page, - .dma_supported = dma_direct_supported, -}; -- cgit v1.2.3 From a09c591306881dfb04387c6ee7b7e2e4683fa531 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 13 Jun 2018 13:17:26 +0200 Subject: ACPI / LPSS: Avoid PM quirks on suspend and resume from S3 It is reported that commit a192aa923b66a (ACPI / LPSS: Consolidate runtime PM and system sleep handling) introduced a system suspend regression on some machines, but the only functional change made by it was to cause the PM quirks in the LPSS to also be used during system suspend and resume. While that should always work for suspend-to-idle, it turns out to be problematic for S3 (suspend-to-RAM). To address that issue restore the previous S3 suspend and resume behavior of the LPSS to avoid applying PM quirks then. Fixes: a192aa923b66a (ACPI / LPSS: Consolidate runtime PM and system sleep handling) Link: https://bugs.launchpad.net/bugs/1774950 Reported-by: Kai-Heng Feng Tested-by: Kai-Heng Feng Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Andy Shevchenko Acked-by: Mika Westerberg Cc: 4.15+ # 4.15+ --- drivers/acpi/acpi_lpss.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index cb6ac5c65c2e..55e4577b504c 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "internal.h" @@ -944,9 +945,10 @@ static void lpss_iosf_exit_d3_state(void) mutex_unlock(&lpss_iosf_mutex); } -static int acpi_lpss_suspend(struct device *dev, bool wakeup) +static int acpi_lpss_suspend(struct device *dev, bool runtime) { struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + bool wakeup = runtime || device_may_wakeup(dev); int ret; if (pdata->dev_desc->flags & LPSS_SAVE_CTX) @@ -959,13 +961,14 @@ static int acpi_lpss_suspend(struct device *dev, bool wakeup) * wrong status for devices being about to be powered off. See * lpss_iosf_enter_d3_state() for further information. */ - if (lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available()) + if ((runtime || !pm_suspend_via_firmware()) && + lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available()) lpss_iosf_enter_d3_state(); return ret; } -static int acpi_lpss_resume(struct device *dev) +static int acpi_lpss_resume(struct device *dev, bool runtime) { struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); int ret; @@ -974,7 +977,8 @@ static int acpi_lpss_resume(struct device *dev) * This call is kept first to be in symmetry with * acpi_lpss_runtime_suspend() one. */ - if (lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available()) + if ((runtime || !pm_resume_via_firmware()) && + lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available()) lpss_iosf_exit_d3_state(); ret = acpi_dev_resume(dev); @@ -998,12 +1002,12 @@ static int acpi_lpss_suspend_late(struct device *dev) return 0; ret = pm_generic_suspend_late(dev); - return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev)); + return ret ? ret : acpi_lpss_suspend(dev, false); } static int acpi_lpss_resume_early(struct device *dev) { - int ret = acpi_lpss_resume(dev); + int ret = acpi_lpss_resume(dev, false); return ret ? ret : pm_generic_resume_early(dev); } @@ -1018,7 +1022,7 @@ static int acpi_lpss_runtime_suspend(struct device *dev) static int acpi_lpss_runtime_resume(struct device *dev) { - int ret = acpi_lpss_resume(dev); + int ret = acpi_lpss_resume(dev, true); return ret ? ret : pm_generic_runtime_resume(dev); } -- cgit v1.2.3 From 47e5abfb546a3ace23a77453dc2e9db92704c5ac Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 14 Jun 2018 10:01:52 +0200 Subject: PM / core: Fix supplier device runtime PM usage counter imbalance If a device link is added via device_link_add() by the driver of the link's consumer device, the supplier's runtime PM usage counter is going to be dropped by the pm_runtime_put_suppliers() call in driver_probe_device(). However, in that case it is not incremented unless the supplier driver is already present and the link is not stateless. That leads to a runtime PM usage counter imbalance for the supplier device in a few cases. To prevent that from happening, bump up the supplier runtime PM usage counter in device_link_add() for all links with the DL_FLAG_PM_RUNTIME flag set that are added at the consumer probe time. Use pm_runtime_get_noresume() for that as the callers of device_link_add() who want the supplier to be resumed by it are expected to pass DL_FLAG_RPM_ACTIVE in flags to it anyway, but additionally resume the supplier if the link is added during consumer driver probe to retain the existing behavior for the callers depending on it. Fixes: 21d5c57b3726 (PM / runtime: Use device links) Reported-by: Ulf Hansson Reviewed-by: Ulf Hansson Tested-by: Marek Szyprowski Cc: 4.10+ # 4.10+ Signed-off-by: Rafael J. Wysocki --- drivers/base/core.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/base/core.c b/drivers/base/core.c index 36622b52e419..df3e1a44707a 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -236,6 +236,13 @@ struct device_link *device_link_add(struct device *consumer, link->rpm_active = true; } pm_runtime_new_link(consumer); + /* + * If the link is being added by the consumer driver at probe + * time, balance the decrementation of the supplier's runtime PM + * usage counter after consumer probe in driver_probe_device(). + */ + if (consumer->links.status == DL_DEV_PROBING) + pm_runtime_get_noresume(supplier); } get_device(supplier); link->supplier = supplier; @@ -255,12 +262,12 @@ struct device_link *device_link_add(struct device *consumer, switch (consumer->links.status) { case DL_DEV_PROBING: /* - * Balance the decrementation of the supplier's - * runtime PM usage counter after consumer probe - * in driver_probe_device(). + * Some callers expect the link creation during + * consumer driver probe to resume the supplier + * even without DL_FLAG_RPM_ACTIVE. */ if (flags & DL_FLAG_PM_RUNTIME) - pm_runtime_get_sync(supplier); + pm_runtime_resume(supplier); link->status = DL_STATE_CONSUMER_PROBE; break; -- cgit v1.2.3 From f70b359b3830b7a5b72c78136edc740839b67acd Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 1 Jun 2018 10:59:25 +0300 Subject: crypto: chtls - use after free in chtls_pt_recvmsg() We call chtls_free_skb() but then we dereference it on the next lines. Also "skb" can't be NULL, we just dereferenced it on the line before. I have moved the free down a couple lines to fix this issue. Fixes: 17a7d24aa89d ("crypto: chtls - generic handling of data and hdr") Signed-off-by: Dan Carpenter Signed-off-by: Herbert Xu --- drivers/crypto/chelsio/chtls/chtls_io.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/chelsio/chtls/chtls_io.c b/drivers/crypto/chelsio/chtls/chtls_io.c index 51fc6821cbbf..708e232e3cdf 100644 --- a/drivers/crypto/chelsio/chtls/chtls_io.c +++ b/drivers/crypto/chelsio/chtls/chtls_io.c @@ -1548,15 +1548,14 @@ skip_copy: tp->urg_data = 0; if ((avail + offset) >= skb->len) { - if (likely(skb)) - chtls_free_skb(sk, skb); - buffers_freed++; if (ULP_SKB_CB(skb)->flags & ULPCB_FLAG_TLS_HDR) { tp->copied_seq += skb->len; hws->rcvpld = skb->hdr_len; } else { tp->copied_seq += hws->rcvpld; } + chtls_free_skb(sk, skb); + buffers_freed++; hws->copied_seq = 0; if (copied >= target && !skb_peek(&sk->sk_receive_queue)) -- cgit v1.2.3 From 837bf7cc3b7504385ae0e829c72e470dfc27cf6c Mon Sep 17 00:00:00 2001 From: Michael BĂ¼sch Date: Thu, 14 Jun 2018 20:08:11 +0200 Subject: hwrng: core - Always drop the RNG in hwrng_unregister() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit enable_best_rng() is used in hwrng_unregister() to switch away from the currently active RNG, if that is the one currently being removed. However enable_best_rng() might fail, if the next RNG's init routine fails. In that case enable_best_rng() will return an error code and the currently active RNG will remain active. After unregistering this might lead to crashes due to use-after-free. Fix this by dropping the currently active RNG, if enable_best_rng() failed. This will result in no RNG to be active, if the next-best one failed to initialize. This problem was introduced by 142a27f0a731ddcf467546960a5585970ca98e21 Fixes: 142a27f0a731 ("hwrng: core - Reset user selected rng by...") Reported-by: Wirz Tested-by: Wirz Signed-off-by: Michael BĂ¼sch Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu --- drivers/char/hw_random/core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 91bb98c42a1c..aaf9e5afaad4 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -516,11 +516,18 @@ EXPORT_SYMBOL_GPL(hwrng_register); void hwrng_unregister(struct hwrng *rng) { + int err; + mutex_lock(&rng_mutex); list_del(&rng->list); - if (current_rng == rng) - enable_best_rng(); + if (current_rng == rng) { + err = enable_best_rng(); + if (err) { + drop_current_rng(); + cur_rng_set_by_user = 0; + } + } if (list_empty(&rng_list)) { mutex_unlock(&rng_mutex); -- cgit v1.2.3 From a9122886d9848d00a01888116a58624b9ba95cdc Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 17 Jun 2018 08:38:55 +0900 Subject: bluetooth: hci_nokia: Don't include linux/unaligned/le_struct.h directly. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This breaks the build as this header is not meant to be used in this way. ./include/linux/unaligned/access_ok.h:8:28: error: redefinition of ‘get_unaligned_le16’ static __always_inline u16 get_unaligned_le16(const void *p) ^~~~~~~~~~~~~~~~~~ In file included from drivers/bluetooth/hci_nokia.c:32: ./include/linux/unaligned/le_struct.h:7:19: note: previous definition of ‘get_unaligned_le16’ was here static inline u16 get_unaligned_le16(const void *p) Use asm/unaligned.h instead. Signed-off-by: David S. Miller --- drivers/bluetooth/hci_nokia.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/bluetooth/hci_nokia.c b/drivers/bluetooth/hci_nokia.c index 14d159e2042d..2dc33e65d2d0 100644 --- a/drivers/bluetooth/hci_nokia.c +++ b/drivers/bluetooth/hci_nokia.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include -- cgit v1.2.3 From 536e0019b7da4eb3badb4da5acbb70ae29e1b5ef Mon Sep 17 00:00:00 2001 From: Helge Eichelberg Date: Tue, 5 Jun 2018 19:38:32 +0200 Subject: hwmon: (dell-smm) Disable fan support for Dell XPS13 9333 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Calling fan related SMM functions implemented by Dell BIOS firmware on Dell XPS13 9333 freeze kernel for about 500ms. Until Dell fixes it we need to disable fan support for Dell XPS13 9333. Via "force" module param fan support can be enabled. Link: https://bugzilla.kernel.org/show_bug.cgi?id=195751 Signed-off-by: Helge Eichelberg Reviewed-by: Pali RohĂ¡r Signed-off-by: Guenter Roeck --- drivers/hwmon/dell-smm-hwmon.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index bf3bb7e1adab..9d3ef879dc51 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -1074,6 +1074,13 @@ static struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initdata = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Vostro 3360"), }, }, + { + .ident = "Dell XPS13 9333", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"), + }, + }, { } }; -- cgit v1.2.3 From 91bb8f45f73f19a0150c233c0f11cdeb6d71d1e9 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 12 Jun 2018 15:19:35 -0700 Subject: hwmon: (nct6775) Fix loop limit Commit cc66b3038254 ("hwmon: (nct6775) Rework temperature source and label handling") changed a loop limit from "data->temp_label_num - 1" to "32", as part of moving from a string array to a bit mask. This results in the following error, reported by UBSAN. UBSAN: Undefined behaviour in drivers/hwmon/nct6775.c:4179:27 shift exponent 32 is too large for 32-bit type 'long unsigned int' Similar to the original loop, the limit has to be one less than the number of bits. Fixes: cc66b3038254 ("hwmon: (nct6775) Rework temperature source and label handling") Reported-by: Paul Menzel Cc: Paul Menzel Tested-by: Paul Menzel Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 155d4d1d1585..f9d1349c3286 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -4175,7 +4175,7 @@ static int nct6775_probe(struct platform_device *pdev) * The temperature is already monitored if the respective bit in * is set. */ - for (i = 0; i < 32; i++) { + for (i = 0; i < 31; i++) { if (!(data->temp_mask & BIT(i + 1))) continue; if (!reg_temp_alternate[i]) -- cgit v1.2.3 From 3637f12faf507b0a4b8ac1e7115fc99583ab1db3 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Wed, 30 May 2018 09:30:42 +0800 Subject: soc: imx: gpcv2: correct PGC offset Correct MIPI/PCIe/USB_HSIC's PGC offset based on design RTL, the values in the Reference Manual (Rev. 1, 01/2018 and the older ones) are incorrect. The correct offset values should be as below: 0x800 ~ 0x83F: PGC for core0 of A7 platform; 0x840 ~ 0x87F: PGC for core1 of A7 platform; 0x880 ~ 0x8BF: PGC for SCU of A7 platform; 0xA00 ~ 0xA3F: PGC for fastmix/megamix; 0xC00 ~ 0xC3F: PGC for MIPI PHY; 0xC40 ~ 0xC7F: PGC for PCIe_PHY; 0xC80 ~ 0xCBF: PGC for USB OTG1 PHY; 0xCC0 ~ 0xCFF: PGC for USB OTG2 PHY; 0xD00 ~ 0xD3F: PGC for USB HSIC PHY; Signed-off-by: Anson Huang Fixes: 03aa12629fc4 ("soc: imx: Add GPCv2 power gating driver") Acked-by: Andrey Smirnov Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- drivers/soc/imx/gpcv2.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index f4e3bd40c72e..6ef18cf8f243 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c @@ -39,10 +39,15 @@ #define GPC_M4_PU_PDN_FLG 0x1bc - -#define PGC_MIPI 4 -#define PGC_PCIE 5 -#define PGC_USB_HSIC 8 +/* + * The PGC offset values in Reference Manual + * (Rev. 1, 01/2018 and the older ones) GPC chapter's + * GPC_PGC memory map are incorrect, below offset + * values are from design RTL. + */ +#define PGC_MIPI 16 +#define PGC_PCIE 17 +#define PGC_USB_HSIC 20 #define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40) #define GPC_PGC_SR(n) (GPC_PGC_CTRL(n) + 0xc) -- cgit v1.2.3 From 9fcf2b3c1c0276650fea537c71b513d27d929b05 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sun, 17 Jun 2018 10:48:22 +0200 Subject: drm/atmel-hlcdc: check stride values in the first plane The statement always evaluates to true since the struct fields are arrays. This has shown up as a warning when compiling with clang: warning: address of array 'desc->layout.xstride' will always evaluate to 'true' [-Wpointer-bool-conversion] Check for values in the first plane instead. Fixes: 1a396789f65a ("drm: add Atmel HLCDC Display Controller support") Cc: Signed-off-by: Stefan Agner Signed-off-by: Boris Brezillon Link: https://patchwork.freedesktop.org/patch/msgid/20180617084826.31885-1-stefan@agner.ch --- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index e18800ed7cd1..7b8191eae68a 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -875,7 +875,7 @@ static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane, drm_object_attach_property(&plane->base.base, props->alpha, 255); - if (desc->layout.xstride && desc->layout.pstride) { + if (desc->layout.xstride[0] && desc->layout.pstride[0]) { int ret; ret = drm_plane_create_rotation_property(&plane->base, -- cgit v1.2.3 From b23908d3c48a37c46c6a26df2cdeab1610b360ba Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 17 Jun 2018 14:09:42 +0200 Subject: firmware: dmi: Add access to the SKU ID string This is used in some systems from user space for determining the identity of the device. Expose this as a file so that that user-space tools don't need to read from /sys/firmware/dmi/tables/DMI Signed-off-by: Simon Glass Signed-off-by: Jean Delvare --- drivers/firmware/dmi-id.c | 2 ++ drivers/firmware/dmi_scan.c | 1 + include/linux/mod_devicetable.h | 1 + 3 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/dmi-id.c b/drivers/firmware/dmi-id.c index 951b6c79f166..624a11cb07e2 100644 --- a/drivers/firmware/dmi-id.c +++ b/drivers/firmware/dmi-id.c @@ -47,6 +47,7 @@ DEFINE_DMI_ATTR_WITH_SHOW(product_name, 0444, DMI_PRODUCT_NAME); DEFINE_DMI_ATTR_WITH_SHOW(product_version, 0444, DMI_PRODUCT_VERSION); DEFINE_DMI_ATTR_WITH_SHOW(product_serial, 0400, DMI_PRODUCT_SERIAL); DEFINE_DMI_ATTR_WITH_SHOW(product_uuid, 0400, DMI_PRODUCT_UUID); +DEFINE_DMI_ATTR_WITH_SHOW(product_sku, 0444, DMI_PRODUCT_SKU); DEFINE_DMI_ATTR_WITH_SHOW(product_family, 0444, DMI_PRODUCT_FAMILY); DEFINE_DMI_ATTR_WITH_SHOW(board_vendor, 0444, DMI_BOARD_VENDOR); DEFINE_DMI_ATTR_WITH_SHOW(board_name, 0444, DMI_BOARD_NAME); @@ -193,6 +194,7 @@ static void __init dmi_id_init_attr_table(void) ADD_DMI_ATTR(product_serial, DMI_PRODUCT_SERIAL); ADD_DMI_ATTR(product_uuid, DMI_PRODUCT_UUID); ADD_DMI_ATTR(product_family, DMI_PRODUCT_FAMILY); + ADD_DMI_ATTR(product_sku, DMI_PRODUCT_SKU); ADD_DMI_ATTR(board_vendor, DMI_BOARD_VENDOR); ADD_DMI_ATTR(board_name, DMI_BOARD_NAME); ADD_DMI_ATTR(board_version, DMI_BOARD_VERSION); diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 54e66adef252..f2483548cde9 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -447,6 +447,7 @@ static void __init dmi_decode(const struct dmi_header *dm, void *dummy) dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6); dmi_save_ident(dm, DMI_PRODUCT_SERIAL, 7); dmi_save_uuid(dm, DMI_PRODUCT_UUID, 8); + dmi_save_ident(dm, DMI_PRODUCT_SKU, 25); dmi_save_ident(dm, DMI_PRODUCT_FAMILY, 26); break; case 2: /* Base Board Information */ diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 7d361be2e24f..61fbfbc03e5b 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -490,6 +490,7 @@ enum dmi_field { DMI_PRODUCT_VERSION, DMI_PRODUCT_SERIAL, DMI_PRODUCT_UUID, + DMI_PRODUCT_SKU, DMI_PRODUCT_FAMILY, DMI_BOARD_VENDOR, DMI_BOARD_NAME, -- cgit v1.2.3 From fb7298e1669ee84aa76f3483c91f1de4814aac11 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 31 May 2018 11:39:42 +0000 Subject: pinctrl: mediatek: remove redundant return value check of platform_get_resource() Remove unneeded error handling on the result of a call to platform_get_resource() when the value is passed to devm_ioremap_resource(). Signed-off-by: Wei Yongjun Signed-off-by: Linus Walleij --- drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c index b3799695d8db..16ff56f93501 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c @@ -1000,11 +1000,6 @@ static int mtk_eint_init(struct mtk_pinctrl *pctl, struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Unable to get eint resource\n"); - return -ENODEV; - } - pctl->eint->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(pctl->eint->base)) return PTR_ERR(pctl->eint->base); -- cgit v1.2.3 From bc3322bc166a2905bc91f774d7b22773dc7c063a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 7 Jun 2018 13:51:33 -0300 Subject: pinctrl: devicetree: Fix pctldev pointer overwrite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit b89405b6102f ("pinctrl: devicetree: Fix dt_to_map_one_config handling of hogs") causes the pinctrl hog pins to not get initialized on i.MX platforms leaving them with the IOMUX settings untouched. This causes several regressions on i.MX such as: - OV5640 camera driver can not be probed anymore on imx6qdl-sabresd because the camera clock pin is in a pinctrl_hog group and since its pinctrl initialization is skipped, the camera clock is kept in GPIO functionality instead of CLK_CKO function. - Audio stopped working on imx6qdl-wandboard and imx53-qsb for the same reason. Richard Fitzgerald explains the problem: "I see the bug. If the hog node isn't a 1st level child of the pinctrl parent node it will go around the for(;;) loop again but on the first pass I overwrite pctldev with the result of get_pinctrl_dev_from_of_node() so it doesn't point to the pinctrl driver any more." Fix the issue by stashing the original pctldev so it doesn't get overwritten. Fixes: b89405b6102f ("pinctrl: devicetree: Fix dt_to_map_one_config handling of hogs") Cc: Reported-by: Mika Penttilä Reported-by: Steve Longerbeam Suggested-by: Richard Fitzgerald Signed-off-by: Fabio Estevam Reviewed-by: Dong Aisheng Reviewed-by: Richard Fitzgerald Signed-off-by: Linus Walleij --- drivers/pinctrl/devicetree.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index b601039d6c69..c4aa411f5935 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -101,10 +101,11 @@ struct pinctrl_dev *of_pinctrl_get(struct device_node *np) } static int dt_to_map_one_config(struct pinctrl *p, - struct pinctrl_dev *pctldev, + struct pinctrl_dev *hog_pctldev, const char *statename, struct device_node *np_config) { + struct pinctrl_dev *pctldev = NULL; struct device_node *np_pctldev; const struct pinctrl_ops *ops; int ret; @@ -123,8 +124,10 @@ static int dt_to_map_one_config(struct pinctrl *p, return -EPROBE_DEFER; } /* If we're creating a hog we can use the passed pctldev */ - if (pctldev && (np_pctldev == p->dev->of_node)) + if (hog_pctldev && (np_pctldev == p->dev->of_node)) { + pctldev = hog_pctldev; break; + } pctldev = get_pinctrl_dev_from_of_node(np_pctldev); if (pctldev) break; -- cgit v1.2.3 From 7f57871f39912978e95db920ddbbfb2304a4bfbf Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 6 Jun 2018 14:43:38 +0100 Subject: pinctrl: single: Add allocation failure checking of saved_vals Currently saved_vals is being allocated and there is no check for failed allocation (which is more likely than normal when using GFP_ATOMIC). Fix this by checking for a failed allocation and propagating this error return down the the caller chain. Detected by CoverityScan, CID#1469841 ("Dereference null return value") Fixes: 88a1dbdec682 ("pinctrl: pinctrl-single: Add functions to save and restore pinctrl context") Signed-off-by: Colin Ian King Reviewed-by: Johan Hovold Acked-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-single.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index b3153c095199..e5647dac0818 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -1590,8 +1590,11 @@ static int pcs_save_context(struct pcs_device *pcs) mux_bytes = pcs->width / BITS_PER_BYTE; - if (!pcs->saved_vals) + if (!pcs->saved_vals) { pcs->saved_vals = devm_kzalloc(pcs->dev, pcs->size, GFP_ATOMIC); + if (!pcs->saved_vals) + return -ENOMEM; + } switch (pcs->width) { case 64: @@ -1651,8 +1654,13 @@ static int pinctrl_single_suspend(struct platform_device *pdev, if (!pcs) return -EINVAL; - if (pcs->flags & PCS_CONTEXT_LOSS_OFF) - pcs_save_context(pcs); + if (pcs->flags & PCS_CONTEXT_LOSS_OFF) { + int ret; + + ret = pcs_save_context(pcs); + if (ret < 0) + return ret; + } return pinctrl_force_sleep(pcs->pctl); } -- cgit v1.2.3 From 71fd5d07b791b90587f695f048ad82e8d4c1c67e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 8 Jun 2018 12:05:47 +0200 Subject: pinctrl: actions: Fix uninitialized error in owl_pin_config_set() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With gcc 4.1.2: drivers/pinctrl/actions/pinctrl-owl.c: In function ‘owl_pin_config_set’: drivers/pinctrl/actions/pinctrl-owl.c:336: warning: ‘ret’ may be used uninitialized in this function Indeed, if num_configs is zero, the uninitialized value will be returned as an error code. Fix this by preinitializing it to zero. Fixes: 2242ddfbf4d699b5 ("pinctrl: actions: Add Actions S900 pinctrl driver") Signed-off-by: Geert Uytterhoeven Acked-by: Manivannan Sadhasivam Signed-off-by: Linus Walleij --- drivers/pinctrl/actions/pinctrl-owl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/actions/pinctrl-owl.c b/drivers/pinctrl/actions/pinctrl-owl.c index 76243caa08c6..b5c880b50bb3 100644 --- a/drivers/pinctrl/actions/pinctrl-owl.c +++ b/drivers/pinctrl/actions/pinctrl-owl.c @@ -333,7 +333,7 @@ static int owl_pin_config_set(struct pinctrl_dev *pctrldev, unsigned long flags; unsigned int param; u32 reg, bit, width, arg; - int ret, i; + int ret = 0, i; info = &pctrl->soc->padinfo[pin]; -- cgit v1.2.3 From 5f591543a937310e48baf0ee23680be09cdedfb8 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Thu, 14 Jun 2018 16:55:49 +0800 Subject: pinctrl: mt7622: fix a kernel panic when pio don't work as EINT controller The function, external interrupt controller, is made as an optional to mt7622 pinctrl. But if we don't want pio behaves as an external interrupt controller, it would lead to hw->eint not be created properly and then will cause 'kernel NULL pointer' issue when gpiochip try to call .to_irq or .set_config. To fix it, check hw->eint before accessing the member. [ 1.339494] Unable to handle kernel NULL pointer dereference at virtual address 00000010 [ 1.347857] Mem abort info: [ 1.350742] ESR = 0x96000005 [ 1.353905] Exception class = DABT (current EL), IL = 32 bits [ 1.360024] SET = 0, FnV = 0 [ 1.363185] EA = 0, S1PTW = 0 [ 1.366431] Data abort info: [ 1.369405] ISV = 0, ISS = 0x00000005 [ 1.373363] CM = 0, WnR = 0 [ 1.376437] [0000000000000010] user address but active_mm is swapper [ 1.383005] Internal error: Oops: 96000005 [#1] PREEMPT SMP [ 1.388748] Modules linked in: [ 1.391897] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.16.0-rc1+ #344 [ 1.398625] Hardware name: MediaTek MT7622 RFB1 board (DT) [ 1.404279] pstate: 80000005 (Nzcv daif -PAN -UAO) [ 1.409221] pc : mtk_eint_find_irq+0x8/0x24 [ 1.413532] lr : mtk_gpio_to_irq+0x20/0x28 [ 1.417749] sp : ffffff800801baf0 [ 1.421161] x29: ffffff800801baf0 x28: ffffff8008792f40 [ 1.426637] x27: ffffff800886b000 x26: ffffff8008615620 [ 1.432113] x25: ffffffc00e4dbdc8 x24: ffffff80087b8000 [ 1.437589] x23: ffffffc00325a000 x22: ffffffc00325a010 [ 1.443066] x21: ffffffc0033dec18 x20: 00000000ffffffea [ 1.448542] x19: ffffffc00e4db800 x18: 0000000000000130 [ 1.454018] x17: 000000000000000e x16: 0000000000000007 [ 1.459494] x15: ffffff80085ee000 x14: 0000000000000001 [ 1.464970] x13: 0000000000000001 x12: 0000000000000010 [ 1.470446] x11: 0101010101010101 x10: 0000000000000880 [ 1.475922] x9 : ffffff800801b990 x8 : ffffffc0030688e0 [ 1.481399] x7 : ffffff80080c0660 x6 : ffffffc00e4dbbb0 [ 1.486875] x5 : 0000000000000000 x4 : 0000000000000000 [ 1.492351] x3 : ffffff80082a92f4 x2 : 00000000fffffffa [ 1.497826] x1 : 0000000000000051 x0 : 0000000000000000 [ 1.503305] Process swapper/0 (pid: 1, stack limit = 0x0000000054e053bd) [ 1.510210] Call trace: [ 1.512727] mtk_eint_find_irq+0x8/0x24 [ 1.516677] mtk_gpio_to_irq+0x20/0x28 [ 1.520539] gpiod_to_irq+0x48/0x60 [ 1.524135] mmc_gpiod_request_cd_irq+0x3c/0xc4 [ 1.528804] mmc_start_host+0x6c/0x8c [ 1.532575] mmc_add_host+0x58/0x7c [ 1.536168] msdc_drv_probe+0x4fc/0x67c [ 1.540121] platform_drv_probe+0x58/0xa4 [ 1.544251] driver_probe_device+0x204/0x44c [ 1.548649] __driver_attach+0x84/0xf8 [ 1.552512] bus_for_each_dev+0x68/0xa0 [ 1.556461] driver_attach+0x20/0x28 [ 1.560142] bus_add_driver+0xec/0x240 [ 1.564002] driver_register+0x98/0xe4 [ 1.567863] __platform_driver_register+0x48/0x50 [ 1.572711] mt_msdc_driver_init+0x18/0x20 [ 1.576932] do_one_initcall+0x98/0x130 [ 1.580886] kernel_init_freeable+0x13c/0x1d4 [ 1.585375] kernel_init+0x10/0xf8 [ 1.588879] ret_from_fork+0x10/0x18 [ 1.592564] Code: a8c67bfd d65f03c0 a9bf7bfd 910003fd (f9400800) [ 1.598849] ---[ end trace 4bbcb7bc30e98492 ]--- [ 1.603677] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b [ 1.603677] cc: Kevin Hilman Cc: stable@vger.kernel.org Fixes: e6dabd38d8e7 ("pinctrl: mediatek: add EINT support to MT7622 SoC") Signed-off-by: Sean Wang Signed-off-by: Linus Walleij --- drivers/pinctrl/mediatek/pinctrl-mt7622.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/mediatek/pinctrl-mt7622.c b/drivers/pinctrl/mediatek/pinctrl-mt7622.c index ad6da1184c9f..e3f1ab2290fc 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mt7622.c +++ b/drivers/pinctrl/mediatek/pinctrl-mt7622.c @@ -1459,6 +1459,9 @@ static int mtk_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) struct mtk_pinctrl *hw = gpiochip_get_data(chip); unsigned long eint_n; + if (!hw->eint) + return -ENOTSUPP; + eint_n = offset; return mtk_eint_find_irq(hw->eint, eint_n); @@ -1471,7 +1474,8 @@ static int mtk_gpio_set_config(struct gpio_chip *chip, unsigned int offset, unsigned long eint_n; u32 debounce; - if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) + if (!hw->eint || + pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) return -ENOTSUPP; debounce = pinconf_to_config_argument(config); -- cgit v1.2.3 From 2f839823382748664b643daa73f41ee0cc01ced6 Mon Sep 17 00:00:00 2001 From: Karoly Pados Date: Sat, 9 Jun 2018 13:26:08 +0200 Subject: USB: serial: cp210x: add Silicon Labs IDs for Windows Update Silicon Labs defines alternative VID/PID pairs for some chips that when used will automatically install drivers for Windows users without manual intervention. Unfortunately, these IDs are not recognized by the Linux module, so using these IDs improves user experience on one platform but degrades it on Linux. This patch addresses this problem. Signed-off-by: Karoly Pados Cc: stable Signed-off-by: Johan Hovold --- drivers/usb/serial/cp210x.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index eb6c26cbe579..14cf657247b6 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -143,8 +143,11 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x8B34) }, /* Qivicon ZigBee USB Radio Stick */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ + { USB_DEVICE(0x10C4, 0xEA63) }, /* Silicon Labs Windows Update (CP2101-4/CP2102N) */ { USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */ + { USB_DEVICE(0x10C4, 0xEA7A) }, /* Silicon Labs Windows Update (CP2105) */ + { USB_DEVICE(0x10C4, 0xEA7B) }, /* Silicon Labs Windows Update (CP2108) */ { USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */ { USB_DEVICE(0x10C4, 0xF002) }, /* Elan Digital Systems USBwave12 */ { USB_DEVICE(0x10C4, 0xF003) }, /* Elan Digital Systems USBpulse100 */ -- cgit v1.2.3 From 58b3d02f066e5b1480d80bd308c545526eea3250 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Wed, 13 Jun 2018 10:16:47 +0200 Subject: Revert "drm/sun4i: Handle DRM_BUS_FLAG_PIXDATA_*EDGE" This reverts commit 2c17a4368aad2b88b68e4390c819e226cf320f70. The offending commit triggers a run-time fault when accessing the panel element of the sun4i_tcon structure when no such panel is attached. It was apparently assumed in said commit that a panel is always used with the TCON. Although it is often the case, this is not always true. For instance a bridge might be used instead of a panel. This issue was discovered using an A13-OLinuXino, that uses the TCON in RGB mode for a simple DAC-based VGA bridge. Cc: stable@vger.kernel.org Signed-off-by: Paul Kocialkowski Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20180613081647.31183-1-paul.kocialkowski@bootlin.com --- drivers/gpu/drm/sun4i/sun4i_tcon.c | 25 ------------------------- 1 file changed, 25 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index c3d92d537240..8045871335b5 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -17,7 +17,6 @@ #include #include #include -#include #include @@ -350,9 +349,6 @@ static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon, static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, const struct drm_display_mode *mode) { - struct drm_panel *panel = tcon->panel; - struct drm_connector *connector = panel->connector; - struct drm_display_info display_info = connector->display_info; unsigned int bp, hsync, vsync; u8 clk_delay; u32 val = 0; @@ -410,27 +406,6 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, if (mode->flags & DRM_MODE_FLAG_PVSYNC) val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE; - /* - * On A20 and similar SoCs, the only way to achieve Positive Edge - * (Rising Edge), is setting dclk clock phase to 2/3(240°). - * By default TCON works in Negative Edge(Falling Edge), - * this is why phase is set to 0 in that case. - * Unfortunately there's no way to logically invert dclk through - * IO_POL register. - * The only acceptable way to work, triple checked with scope, - * is using clock phase set to 0° for Negative Edge and set to 240° - * for Positive Edge. - * On A33 and similar SoCs there would be a 90° phase option, - * but it divides also dclk by 2. - * Following code is a way to avoid quirks all around TCON - * and DOTCLOCK drivers. - */ - if (display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE) - clk_set_phase(tcon->dclk, 240); - - if (display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE) - clk_set_phase(tcon->dclk, 0); - regmap_update_bits(tcon->regs, SUN4I_TCON0_IO_POL_REG, SUN4I_TCON0_IO_POL_HSYNC_POSITIVE | SUN4I_TCON0_IO_POL_VSYNC_POSITIVE, val); -- cgit v1.2.3 From 24160628a34af962ac99f2f58e547ac3c4cbd26f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 18 Jun 2018 10:24:03 +0200 Subject: USB: serial: cp210x: add CESINEL device ids Add device ids for CESINEL products. Reported-by: Carlos Barcala Lara Cc: stable Signed-off-by: Johan Hovold --- drivers/usb/serial/cp210x.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 14cf657247b6..ee0cc1d90b51 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -95,6 +95,9 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x8156) }, /* B&G H3000 link cable */ { USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */ { USB_DEVICE(0x10C4, 0x815F) }, /* Timewave HamLinkUSB */ + { USB_DEVICE(0x10C4, 0x817C) }, /* CESINEL MEDCAL N Power Quality Monitor */ + { USB_DEVICE(0x10C4, 0x817D) }, /* CESINEL MEDCAL NT Power Quality Monitor */ + { USB_DEVICE(0x10C4, 0x817E) }, /* CESINEL MEDCAL S Power Quality Monitor */ { USB_DEVICE(0x10C4, 0x818B) }, /* AVIT Research USB to TTL */ { USB_DEVICE(0x10C4, 0x819F) }, /* MJS USB Toslink Switcher */ { USB_DEVICE(0x10C4, 0x81A6) }, /* ThinkOptics WavIt */ @@ -112,6 +115,9 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demonstration module */ { USB_DEVICE(0x10C4, 0x8281) }, /* Nanotec Plug & Drive */ { USB_DEVICE(0x10C4, 0x8293) }, /* Telegesis ETRX2USB */ + { USB_DEVICE(0x10C4, 0x82EF) }, /* CESINEL FALCO 6105 AC Power Supply */ + { USB_DEVICE(0x10C4, 0x82F1) }, /* CESINEL MEDCAL EFD Earth Fault Detector */ + { USB_DEVICE(0x10C4, 0x82F2) }, /* CESINEL MEDCAL ST Network Analyzer */ { USB_DEVICE(0x10C4, 0x82F4) }, /* Starizona MicroTouch */ { USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */ { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ @@ -124,7 +130,9 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x8470) }, /* Juniper Networks BX Series System Console */ { USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */ { USB_DEVICE(0x10C4, 0x84B6) }, /* Starizona Hyperion */ + { USB_DEVICE(0x10C4, 0x851E) }, /* CESINEL MEDCAL PT Network Analyzer */ { USB_DEVICE(0x10C4, 0x85A7) }, /* LifeScan OneTouch Verio IQ */ + { USB_DEVICE(0x10C4, 0x85B8) }, /* CESINEL ReCon T Energy Logger */ { USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */ { USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */ { USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */ @@ -134,10 +142,13 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x8857) }, /* CEL EM357 ZigBee USB Stick */ { USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */ { USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */ + { USB_DEVICE(0x10C4, 0x88FB) }, /* CESINEL MEDCAL STII Network Analyzer */ + { USB_DEVICE(0x10C4, 0x8938) }, /* CESINEL MEDCAL S II Network Analyzer */ { USB_DEVICE(0x10C4, 0x8946) }, /* Ketra N1 Wireless Interface */ { USB_DEVICE(0x10C4, 0x8962) }, /* Brim Brothers charging dock */ { USB_DEVICE(0x10C4, 0x8977) }, /* CEL MeshWorks DevKit Device */ { USB_DEVICE(0x10C4, 0x8998) }, /* KCF Technologies PRN */ + { USB_DEVICE(0x10C4, 0x89A4) }, /* CESINEL FTBC Flexible Thyristor Bridge Controller */ { USB_DEVICE(0x10C4, 0x8A2A) }, /* HubZ dual ZigBee and Z-Wave dongle */ { USB_DEVICE(0x10C4, 0x8A5E) }, /* CEL EM3588 ZigBee USB Stick Long Range */ { USB_DEVICE(0x10C4, 0x8B34) }, /* Qivicon ZigBee USB Radio Stick */ -- cgit v1.2.3 From 00908693c481f7298adf8cf4d2ff3dfbea8c375f Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 15 Jun 2017 12:57:30 +0300 Subject: usb: dwc3: pci: add support for Intel IceLake PCI IDs for Intel IceLake. Signed-off-by: Heikki Krogerus Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index c961a94d136b..f57e7c94b8e5 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -34,6 +34,7 @@ #define PCI_DEVICE_ID_INTEL_GLK 0x31aa #define PCI_DEVICE_ID_INTEL_CNPLP 0x9dee #define PCI_DEVICE_ID_INTEL_CNPH 0xa36e +#define PCI_DEVICE_ID_INTEL_ICLLP 0x34ee #define PCI_INTEL_BXT_DSM_GUID "732b85d5-b7a7-4a1b-9ba0-4bbd00ffd511" #define PCI_INTEL_BXT_FUNC_PMU_PWR 4 @@ -289,6 +290,7 @@ static const struct pci_device_id dwc3_pci_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_GLK), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CNPLP), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CNPH), }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICLLP), }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), }, { } /* Terminating Entry */ }; -- cgit v1.2.3 From d52e4d0c0c428bf2ba35074a7495cdb28e2efbae Mon Sep 17 00:00:00 2001 From: Vincent Pelletier Date: Wed, 13 Jun 2018 11:05:06 +0000 Subject: usb: gadget: ffs: Fix BUG when userland exits with submitted AIO transfers This bug happens only when the UDC needs to sleep during usb_ep_dequeue, as is the case for (at least) dwc3. [ 382.200896] BUG: scheduling while atomic: screen/1808/0x00000100 [ 382.207124] 4 locks held by screen/1808: [ 382.211266] #0: (rcu_callback){....}, at: [] rcu_process_callbacks+0x260/0x440 [ 382.219949] #1: (rcu_read_lock_sched){....}, at: [] percpu_ref_switch_to_atomic_rcu+0xb0/0x130 [ 382.230034] #2: (&(&ctx->ctx_lock)->rlock){....}, at: [] free_ioctx_users+0x23/0xd0 [ 382.230096] #3: (&(&ffs->eps_lock)->rlock){....}, at: [] ffs_aio_cancel+0x20/0x60 [usb_f_fs] [ 382.230160] Modules linked in: usb_f_fs libcomposite configfs bnep btsdio bluetooth ecdh_generic brcmfmac brcmutil intel_powerclamp coretemp dwc3 kvm_intel ulpi udc_core kvm irqbypass crc32_pclmul crc32c_intel pcbc dwc3_pci aesni_intel aes_i586 crypto_simd cryptd ehci_pci ehci_hcd gpio_keys usbcore basincove_gpadc industrialio usb_common [ 382.230407] CPU: 1 PID: 1808 Comm: screen Not tainted 4.14.0-edison+ #117 [ 382.230416] Hardware name: Intel Corporation Merrifield/BODEGA BAY, BIOS 542 2015.01.21:18.19.48 [ 382.230425] Call Trace: [ 382.230438] [ 382.230466] dump_stack+0x47/0x62 [ 382.230498] __schedule_bug+0x61/0x80 [ 382.230522] __schedule+0x43/0x7a0 [ 382.230587] schedule+0x5f/0x70 [ 382.230625] dwc3_gadget_ep_dequeue+0x14c/0x270 [dwc3] [ 382.230669] ? do_wait_intr_irq+0x70/0x70 [ 382.230724] usb_ep_dequeue+0x19/0x90 [udc_core] [ 382.230770] ffs_aio_cancel+0x37/0x60 [usb_f_fs] [ 382.230798] kiocb_cancel+0x31/0x40 [ 382.230822] free_ioctx_users+0x4d/0xd0 [ 382.230858] percpu_ref_switch_to_atomic_rcu+0x10a/0x130 [ 382.230881] ? percpu_ref_exit+0x40/0x40 [ 382.230904] rcu_process_callbacks+0x2b3/0x440 [ 382.230965] __do_softirq+0xf8/0x26b [ 382.231011] ? __softirqentry_text_start+0x8/0x8 [ 382.231033] do_softirq_own_stack+0x22/0x30 [ 382.231042] [ 382.231071] irq_exit+0x45/0xc0 [ 382.231089] smp_apic_timer_interrupt+0x13c/0x150 [ 382.231118] apic_timer_interrupt+0x35/0x3c [ 382.231132] EIP: __copy_user_ll+0xe2/0xf0 [ 382.231142] EFLAGS: 00210293 CPU: 1 [ 382.231154] EAX: bfd4508c EBX: 00000004 ECX: 00000003 EDX: f3d8fe50 [ 382.231165] ESI: f3d8fe51 EDI: bfd4508d EBP: f3d8fe14 ESP: f3d8fe08 [ 382.231176] DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068 [ 382.231265] core_sys_select+0x25f/0x320 [ 382.231346] ? __wake_up_common_lock+0x62/0x80 [ 382.231399] ? tty_ldisc_deref+0x13/0x20 [ 382.231438] ? ldsem_up_read+0x1b/0x40 [ 382.231459] ? tty_ldisc_deref+0x13/0x20 [ 382.231479] ? tty_write+0x29f/0x2e0 [ 382.231514] ? n_tty_ioctl+0xe0/0xe0 [ 382.231541] ? tty_write_unlock+0x30/0x30 [ 382.231566] ? __vfs_write+0x22/0x110 [ 382.231604] ? security_file_permission+0x2f/0xd0 [ 382.231635] ? rw_verify_area+0xac/0x120 [ 382.231677] ? vfs_write+0x103/0x180 [ 382.231711] SyS_select+0x87/0xc0 [ 382.231739] ? SyS_write+0x42/0x90 [ 382.231781] do_fast_syscall_32+0xd6/0x1a0 [ 382.231836] entry_SYSENTER_32+0x47/0x71 [ 382.231848] EIP: 0xb7f75b05 [ 382.231857] EFLAGS: 00000246 CPU: 1 [ 382.231868] EAX: ffffffda EBX: 00000400 ECX: bfd4508c EDX: bfd4510c [ 382.231878] ESI: 00000000 EDI: 00000000 EBP: 00000000 ESP: bfd45020 [ 382.231889] DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b [ 382.232281] softirq: huh, entered softirq 9 RCU c10b4d90 with preempt_count 00000100, exited with 00000000? Tested-by: Sam Protsenko Signed-off-by: Vincent Pelletier Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index dce9d12c7981..33e2030503fa 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -215,6 +215,7 @@ struct ffs_io_data { struct mm_struct *mm; struct work_struct work; + struct work_struct cancellation_work; struct usb_ep *ep; struct usb_request *req; @@ -1072,22 +1073,31 @@ ffs_epfile_open(struct inode *inode, struct file *file) return 0; } +static void ffs_aio_cancel_worker(struct work_struct *work) +{ + struct ffs_io_data *io_data = container_of(work, struct ffs_io_data, + cancellation_work); + + ENTER(); + + usb_ep_dequeue(io_data->ep, io_data->req); +} + static int ffs_aio_cancel(struct kiocb *kiocb) { struct ffs_io_data *io_data = kiocb->private; - struct ffs_epfile *epfile = kiocb->ki_filp->private_data; + struct ffs_data *ffs = io_data->ffs; int value; ENTER(); - spin_lock_irq(&epfile->ffs->eps_lock); - - if (likely(io_data && io_data->ep && io_data->req)) - value = usb_ep_dequeue(io_data->ep, io_data->req); - else + if (likely(io_data && io_data->ep && io_data->req)) { + INIT_WORK(&io_data->cancellation_work, ffs_aio_cancel_worker); + queue_work(ffs->io_completion_wq, &io_data->cancellation_work); + value = -EINPROGRESS; + } else { value = -EINVAL; - - spin_unlock_irq(&epfile->ffs->eps_lock); + } return value; } -- cgit v1.2.3 From 1ffba9058737af2ddeebc813faa8ea9b16bc892a Mon Sep 17 00:00:00 2001 From: Minas Harutyunyan Date: Tue, 12 Jun 2018 12:37:29 +0400 Subject: usb: dwc2: gadget: Fix issue in dwc2_gadget_start_isoc() In case of requests queue is empty reset EP target_frame to initial value. This allow restarting ISOC traffic in case when function driver queued requests with interruptions. Tested-by: Zeng Tao Signed-off-by: Minas Harutyunyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index f0d9ccf1d665..bb499fea6100 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -884,6 +884,7 @@ static void dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep *hs_ep) struct dwc2_dma_desc *desc; if (list_empty(&hs_ep->queue)) { + hs_ep->target_frame = TARGET_FRAME_INITIAL; dev_dbg(hsotg->dev, "%s: No requests in queue\n", __func__); return; } -- cgit v1.2.3 From 896e518883f18e601335908192e33426c1f599a4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 31 May 2018 16:45:52 +0200 Subject: usb: dwc3: of-simple: fix use-after-free on remove The clocks have already been explicitly disabled and put as part of remove() so the runtime suspend callback must not be run when balancing the runtime PM usage count before returning. Fixes: 16adc674d0d6 ("usb: dwc3: add generic OF glue layer") Signed-off-by: Johan Hovold Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-of-simple.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index 6b3ccd542bd7..dbeff5e6ad14 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -165,8 +165,9 @@ static int dwc3_of_simple_remove(struct platform_device *pdev) reset_control_put(simple->resets); - pm_runtime_put_sync(dev); pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + pm_runtime_set_suspended(dev); return 0; } -- cgit v1.2.3 From 6e967d7e2c4822eba4847ec09037119a0418aaef Mon Sep 17 00:00:00 2001 From: Zeng Tao Date: Tue, 12 Jun 2018 22:49:06 +0800 Subject: usb: dwc2: gadget: fix packet drop issue in dwc2_gadget_handle_nak In ISOC transfer, when the NAK interrupt happens, we shouldn't complete a usb request, the current flow will complete one usb request with no hardware transfer, this will lead to a packet drop on the usb bus. Acked-by: Minas Harutyunyan Signed-off-by: Zeng Tao Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index bb499fea6100..bb5eb3c8132d 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2818,9 +2818,6 @@ static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep) tmp = dwc2_hsotg_read_frameno(hsotg); if (using_desc_dma(hsotg)) { - dwc2_hsotg_complete_request(hsotg, hs_ep, - get_ep_head(hs_ep), 0); - hs_ep->target_frame = tmp; dwc2_gadget_incr_frame_num(hs_ep); dwc2_gadget_start_isoc_ddma(hs_ep); -- cgit v1.2.3 From 615277779f41a753e68f531613e344c54fdc95bf Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 12 Jun 2018 10:24:48 +0200 Subject: usb: dwc3: Only call clk_bulk_get() on devicetree instantiated devices Commit fe8abf332b8f ("usb: dwc3: support clocks and resets for DWC3 core") adds support for handling clocks and resets in the DWC3 core, so that for platforms following the standard devicetree bindings this does not need to be duplicated in all the different glue layers. These changes intended for devicetree based platforms introduce an uncoditional clk_bulk_get() in the core probe path. This leads to the following error being logged on x86/ACPI systems: [ 26.276783] dwc3 dwc3.3.auto: Failed to get clk 'ref': -2 This commits wraps the clk_bulk_get() in an if (dev->of_node) check so that it only is done on devicetree instantiated devices, fixing this error. Cc: Masahiro Yamada Reviewed-by: Masahiro Yamada Signed-off-by: Hans de Goede Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index ea91310113b9..103807587dc6 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1272,7 +1272,6 @@ static int dwc3_probe(struct platform_device *pdev) if (!dwc->clks) return -ENOMEM; - dwc->num_clks = ARRAY_SIZE(dwc3_core_clks); dwc->dev = dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1307,15 +1306,19 @@ static int dwc3_probe(struct platform_device *pdev) if (IS_ERR(dwc->reset)) return PTR_ERR(dwc->reset); - ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks); - if (ret == -EPROBE_DEFER) - return ret; - /* - * Clocks are optional, but new DT platforms should support all clocks - * as required by the DT-binding. - */ - if (ret) - dwc->num_clks = 0; + if (dev->of_node) { + dwc->num_clks = ARRAY_SIZE(dwc3_core_clks); + + ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks); + if (ret == -EPROBE_DEFER) + return ret; + /* + * Clocks are optional, but new DT platforms should support all + * clocks as required by the DT-binding. + */ + if (ret) + dwc->num_clks = 0; + } ret = reset_control_deassert(dwc->reset); if (ret) -- cgit v1.2.3 From 21cbbc6bb7b4ba6aee303a3b4aef1578253724d8 Mon Sep 17 00:00:00 2001 From: Minas Harutyunyan Date: Tue, 12 Jun 2018 12:27:36 +0400 Subject: usb: dwc2: gadget: fix packet drop issue for ISOC OUT transfers In ISOC OUT transfer, when the OUT token received while EP disabled, we shouldn't complete a usb request. The current flow completed one usb request, this will lead to a packet drop to function driver. Signed-off-by: Minas Harutyunyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index bb5eb3c8132d..fa3b6f361074 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2756,8 +2756,6 @@ static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep) */ tmp = dwc2_hsotg_read_frameno(hsotg); - dwc2_hsotg_complete_request(hsotg, ep, get_ep_head(ep), 0); - if (using_desc_dma(hsotg)) { if (ep->target_frame == TARGET_FRAME_INITIAL) { /* Start first ISO Out */ -- cgit v1.2.3 From 8195a655e5ce09550aff81b2573d9b015d520cb9 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 18 Jun 2018 14:17:16 +0300 Subject: ACPI / EC: Use ec_no_wakeup on Thinkpad X1 Carbon 6th On this system EC interrupt triggers constantly kicking devices out of low power states and thus blocking power management. The system also has a PCIe root port hosting Alpine Ridge Thunderbolt controller and it never gets a chance to go to D3cold because of this. Since the power button works the same regardless if EC interrupt is enabled or not during s2idle, add a quirk for this machine that sets ec_no_wakeup=true preventing spurious wakeups. Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers') diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index bb94cf0731fe..442a9e24f439 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -2037,6 +2037,17 @@ static inline void acpi_ec_query_exit(void) } } +static const struct dmi_system_id acpi_ec_no_wakeup[] = { + { + .ident = "Thinkpad X1 Carbon 6th", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20KGS3JF01"), + }, + }, + { }, +}; + int __init acpi_ec_init(void) { int result; @@ -2047,6 +2058,15 @@ int __init acpi_ec_init(void) if (result) return result; + /* + * Disable EC wakeup on following systems to prevent periodic + * wakeup from EC GPE. + */ + if (dmi_check_system(acpi_ec_no_wakeup)) { + ec_no_wakeup = true; + pr_debug("Disabling EC wakeup on suspend-to-idle\n"); + } + /* Drivers must be started after acpi_ec_query_init() */ dsdt_fail = acpi_bus_register_driver(&acpi_ec_driver); /* -- cgit v1.2.3 From a45fc268db20ecd859bb61e25045912b3194b5e6 Mon Sep 17 00:00:00 2001 From: Hoan Tran Date: Thu, 7 Jun 2018 14:35:01 +0100 Subject: drivers/perf: xgene_pmu: Fix IOB SLOW PMU parser error This patch fixes the below parser error of the IOB SLOW PMU. # perf stat -a -e iob-slow0/cycle-count/ sleep 1 evenf syntax error: 'iob-slow0/cycle-count/' \___ parser error It replaces the "-" character by "_" character inside the PMU name. Signed-off-by: Hoan Tran Signed-off-by: Will Deacon --- drivers/perf/xgene_pmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/perf/xgene_pmu.c b/drivers/perf/xgene_pmu.c index 6bdb1dad805f..0e31f1392a53 100644 --- a/drivers/perf/xgene_pmu.c +++ b/drivers/perf/xgene_pmu.c @@ -1463,7 +1463,7 @@ static char *xgene_pmu_dev_name(struct device *dev, u32 type, int id) case PMU_TYPE_IOB: return devm_kasprintf(dev, GFP_KERNEL, "iob%d", id); case PMU_TYPE_IOB_SLOW: - return devm_kasprintf(dev, GFP_KERNEL, "iob-slow%d", id); + return devm_kasprintf(dev, GFP_KERNEL, "iob_slow%d", id); case PMU_TYPE_MCB: return devm_kasprintf(dev, GFP_KERNEL, "mcb%d", id); case PMU_TYPE_MC: -- cgit v1.2.3 From 109c4d18e57445afcaf728b8716a69375a3daab2 Mon Sep 17 00:00:00 2001 From: Ayan Kumar Halder Date: Fri, 20 Apr 2018 15:44:57 +0100 Subject: drm/arm/malidp: Ensure that the crtcs are shutdown before removing any encoder/connector One needs to ensure that the crtcs are shutdown so that the drm_crtc_state->connector_mask reflects that no connectors are currently active. Further, it reduces the reference count for each connector. This ensures that the connectors and encoders can be cleanly removed either when _unbind is called for the corresponding drivers or by drm_mode_config_cleanup(). We need drm_atomic_helper_shutdown() to be called before component_unbind_all() otherwise the connectors attached to the component device will have the wrong reference count value and will not be cleanly removed. Signed-off-by: Ayan Kumar Halder Acked-by: Liviu Dudau Signed-off-by: Liviu Dudau --- drivers/gpu/drm/arm/malidp_drv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index 8d20faa198cf..0a788d76ed5f 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -278,7 +278,6 @@ static int malidp_init(struct drm_device *drm) static void malidp_fini(struct drm_device *drm) { - drm_atomic_helper_shutdown(drm); drm_mode_config_cleanup(drm); } @@ -646,6 +645,7 @@ vblank_fail: malidp_de_irq_fini(drm); drm->irq_enabled = false; irq_init_fail: + drm_atomic_helper_shutdown(drm); component_unbind_all(dev, drm); bind_fail: of_node_put(malidp->crtc.port); @@ -681,6 +681,7 @@ static void malidp_unbind(struct device *dev) malidp_se_irq_fini(drm); malidp_de_irq_fini(drm); drm->irq_enabled = false; + drm_atomic_helper_shutdown(drm); component_unbind_all(dev, drm); of_node_put(malidp->crtc.port); malidp->crtc.port = NULL; -- cgit v1.2.3 From 89610dc2c235e7b02bb9fba0ce247e12d4dde7cd Mon Sep 17 00:00:00 2001 From: Alison Wang Date: Tue, 24 Apr 2018 10:42:32 +0800 Subject: drm: mali-dp: Enable Global SE interrupts mask for DP500 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the situation that DE and SE aren’t shared the same interrupt number, the Global SE interrupts mask bit MASK_IRQ_EN in MASKIRQ must be set, or else other mask bits will not work and no SE interrupt will occur. This patch enables MASK_IRQ_EN for SE to fix this problem. Signed-off-by: Alison Wang Acked-by: Liviu Dudau Signed-off-by: Liviu Dudau --- drivers/gpu/drm/arm/malidp_hw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c index d789b46dc817..069783e715f1 100644 --- a/drivers/gpu/drm/arm/malidp_hw.c +++ b/drivers/gpu/drm/arm/malidp_hw.c @@ -634,7 +634,8 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = { .vsync_irq = MALIDP500_DE_IRQ_VSYNC, }, .se_irq_map = { - .irq_mask = MALIDP500_SE_IRQ_CONF_MODE, + .irq_mask = MALIDP500_SE_IRQ_CONF_MODE | + MALIDP500_SE_IRQ_GLOBAL, .vsync_irq = 0, }, .dc_irq_map = { -- cgit v1.2.3 From ad7fda2e378f4356df621a39655f7c200b495d81 Mon Sep 17 00:00:00 2001 From: Ayan Kumar Halder Date: Tue, 10 Apr 2018 19:25:03 +0100 Subject: drm/arm/malidp: Preserve LAYER_FORMAT contents when setting format On some Mali-DP processors, the LAYER_FORMAT register contains fields other than the format. These bits were unconditionally cleared when setting the pixel format, whereas they should be preserved at their reset values. Reported-by: Brian Starkey Reported-by: Liviu Dudau Signed-off-by: Ayan Kumar halder Acked-by: Liviu Dudau Signed-off-by: Liviu Dudau --- drivers/gpu/drm/arm/malidp_planes.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index 7a44897c50fe..4af3c1fabb23 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -23,6 +23,7 @@ /* Layer specific register offsets */ #define MALIDP_LAYER_FORMAT 0x000 +#define LAYER_FORMAT_MASK 0x3f #define MALIDP_LAYER_CONTROL 0x004 #define LAYER_ENABLE (1 << 0) #define LAYER_FLOWCFG_MASK 7 @@ -337,7 +338,9 @@ static void malidp_de_plane_update(struct drm_plane *plane, dest_w = plane->state->crtc_w; dest_h = plane->state->crtc_h; - malidp_hw_write(mp->hwdev, ms->format, mp->layer->base); + val = malidp_hw_read(mp->hwdev, mp->layer->base); + val = (val & ~LAYER_FORMAT_MASK) | ms->format; + malidp_hw_write(mp->hwdev, val, mp->layer->base); for (i = 0; i < ms->n_planes; i++) { /* calculate the offset for the layer's plane registers */ -- cgit v1.2.3 From c6cf387ec56c19028333274747bbb4ae145a2d13 Mon Sep 17 00:00:00 2001 From: Ayan Kumar Halder Date: Mon, 18 Jun 2018 18:08:43 +0100 Subject: drm/mali-dp: Rectify the width and height passed to rotmem_required() The width and height needs to be swapped Signed-off-by: Ayan Kumar halder Reviewed-by: Brian Starkey Reviewed-by: Alexandru Gheorghe Acked-by: Liviu Dudau [rebased on top of v4.18-rc1] Signed-off-by: Liviu Dudau --- drivers/gpu/drm/arm/malidp_planes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index 4af3c1fabb23..29409a65d864 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -236,8 +236,8 @@ static int malidp_de_plane_check(struct drm_plane *plane, if (state->rotation & MALIDP_ROTATED_MASK) { int val; - val = mp->hwdev->hw->rotmem_required(mp->hwdev, state->crtc_h, - state->crtc_w, + val = mp->hwdev->hw->rotmem_required(mp->hwdev, state->crtc_w, + state->crtc_h, fb->format->format); if (val < 0) return val; -- cgit v1.2.3 From 7350cdd0257e73a37df57253fb9decd8effacd37 Mon Sep 17 00:00:00 2001 From: Bharat Potnuri Date: Fri, 15 Jun 2018 20:52:33 +0530 Subject: RDMA/core: Save kernel caller name when creating CQ using ib_create_cq() Few kernel applications like SCST-iSER create CQ using ib_create_cq(), where accessing CQ structures using rdma restrack tool leads to below NULL pointer dereference. This patch saves caller kernel module name similar to ib_alloc_cq(). BUG: unable to handle kernel NULL pointer dereference at (null) IP: [] skip_spaces+0x30/0x30 PGD 738bac067 PUD 8533f0067 PMD 0 Oops: 0000 [#1] SMP R10: ffff88017fc03300 R11: 0000000000000246 R12: 0000000000000000 R13: ffff88082fa5a668 R14: ffff88017475a000 R15: 0000000000000000 FS: 00002b32726582c0(0000) GS:ffff88087fc40000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000000 CR3: 00000008491a1000 CR4: 00000000003607e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: [] ? fill_res_name_pid+0x7c/0x90 [ib_core] [] fill_res_cq_entry+0xef/0x170 [ib_core] [] res_get_common_dumpit+0x3c4/0x480 [ib_core] [] nldev_res_get_cq_dumpit+0x13/0x20 [ib_core] [] netlink_dump+0x117/0x2e0 [] __netlink_dump_start+0x1ab/0x230 [] ibnl_rcv_msg+0x11d/0x1f0 [ib_core] [] ? nldev_res_get_mr_dumpit+0x20/0x20 [ib_core] [] ? rdma_nl_multicast+0x30/0x30 [ib_core] [] netlink_rcv_skb+0xa9/0xc0 [] ibnl_rcv+0x98/0xb0 [ib_core] [] netlink_unicast+0xf2/0x1b0 [] netlink_sendmsg+0x31f/0x6a0 [] sock_sendmsg+0xb0/0xf0 [] ? _raw_spin_unlock_bh+0x1e/0x20 [] ? release_sock+0x118/0x170 [] SYSC_sendto+0x121/0x1c0 [] ? sock_alloc_file+0xa0/0x140 [] ? __fd_install+0x25/0x60 [] SyS_sendto+0xe/0x10 [] system_call_fastpath+0x16/0x1b RIP [] skip_spaces+0x30/0x30 RSP CR2: 0000000000000000 Cc: Fixes: f66c8ba4c9fa ("RDMA/core: Save kernel caller name when creating PD and CQ objects") Reviewed-by: Steve Wise Signed-off-by: Potnuri Bharat Teja Reviewed-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/verbs.c | 14 ++++++++------ include/rdma/ib_verbs.h | 13 ++++++++----- 2 files changed, 16 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 0b56828c1319..9d6beb948535 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -1562,11 +1562,12 @@ EXPORT_SYMBOL(ib_destroy_qp); /* Completion queues */ -struct ib_cq *ib_create_cq(struct ib_device *device, - ib_comp_handler comp_handler, - void (*event_handler)(struct ib_event *, void *), - void *cq_context, - const struct ib_cq_init_attr *cq_attr) +struct ib_cq *__ib_create_cq(struct ib_device *device, + ib_comp_handler comp_handler, + void (*event_handler)(struct ib_event *, void *), + void *cq_context, + const struct ib_cq_init_attr *cq_attr, + const char *caller) { struct ib_cq *cq; @@ -1580,12 +1581,13 @@ struct ib_cq *ib_create_cq(struct ib_device *device, cq->cq_context = cq_context; atomic_set(&cq->usecnt, 0); cq->res.type = RDMA_RESTRACK_CQ; + cq->res.kern_name = caller; rdma_restrack_add(&cq->res); } return cq; } -EXPORT_SYMBOL(ib_create_cq); +EXPORT_SYMBOL(__ib_create_cq); int rdma_set_cq_moderation(struct ib_cq *cq, u16 cq_count, u16 cq_period) { diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 2043e1a8f851..4f71d6a073ba 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -3394,11 +3394,14 @@ int ib_process_cq_direct(struct ib_cq *cq, int budget); * * Users can examine the cq structure to determine the actual CQ size. */ -struct ib_cq *ib_create_cq(struct ib_device *device, - ib_comp_handler comp_handler, - void (*event_handler)(struct ib_event *, void *), - void *cq_context, - const struct ib_cq_init_attr *cq_attr); +struct ib_cq *__ib_create_cq(struct ib_device *device, + ib_comp_handler comp_handler, + void (*event_handler)(struct ib_event *, void *), + void *cq_context, + const struct ib_cq_init_attr *cq_attr, + const char *caller); +#define ib_create_cq(device, cmp_hndlr, evt_hndlr, cq_ctxt, cq_attr) \ + __ib_create_cq((device), (cmp_hndlr), (evt_hndlr), (cq_ctxt), (cq_attr), KBUILD_MODNAME) /** * ib_resize_cq - Modifies the capacity of the CQ. -- cgit v1.2.3 From 375dc53d032fc11e98036b5f228ad13f7c5933f5 Mon Sep 17 00:00:00 2001 From: Vijay Immanuel Date: Tue, 12 Jun 2018 18:16:05 -0700 Subject: IB/rxe: Fix missing completion for mem_reg work requests Run the completer task to post a work completion after processing a memory registration or invalidate work request. This covers the case where the memory registration or invalidate was the last work request posted to the qp. Signed-off-by: Vijay Immanuel Reviewed-by: Yonatan Cohen Signed-off-by: Jason Gunthorpe --- drivers/infiniband/sw/rxe/rxe_req.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/infiniband/sw/rxe/rxe_req.c b/drivers/infiniband/sw/rxe/rxe_req.c index 829ecb93661f..8be27238a86e 100644 --- a/drivers/infiniband/sw/rxe/rxe_req.c +++ b/drivers/infiniband/sw/rxe/rxe_req.c @@ -645,6 +645,9 @@ next_wqe: } else { goto exit; } + if ((wqe->wr.send_flags & IB_SEND_SIGNALED) || + qp->sq_sig_type == IB_SIGNAL_ALL_WR) + rxe_run_task(&qp->comp.task, 1); qp->req.wqe_index = next_index(qp->sq.queue, qp->req.wqe_index); goto next_wqe; -- cgit v1.2.3 From 1264f8325e9b8c004f36f1ae7bacd2a46a7ed771 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 18 Jun 2018 18:06:13 +1000 Subject: drm/nouveau/kms/nv50-: cursors always use core channel vram ctxdma Ctxdmas for cursors from all heads are setup in the core channel, and due to us tracking allocated handles per-window, we were failing with -EEXIST on multiple-head setups trying to allocate duplicate handles. The cursor code is hardcoded to use the core channel vram ctxdma already, so just skip ctxdma allocation for cursor fbs to fix the issue. Fixes: 5bca1621c07 ("drm/nouveau/kms/nv50-: move fb ctxdma tracking into windows") Reported-by: Adam Borowski Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv50/curs507a.c | 2 +- drivers/gpu/drm/nouveau/dispnv50/wndw.c | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/dispnv50/curs507a.c b/drivers/gpu/drm/nouveau/dispnv50/curs507a.c index 291c08117ab6..397143b639c6 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/curs507a.c +++ b/drivers/gpu/drm/nouveau/dispnv50/curs507a.c @@ -132,7 +132,7 @@ curs507a_new_(const struct nv50_wimm_func *func, struct nouveau_drm *drm, nvif_object_map(&wndw->wimm.base.user, NULL, 0); wndw->immd = func; - wndw->ctxdma.parent = &disp->core->chan.base.user; + wndw->ctxdma.parent = NULL; return 0; } diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.c b/drivers/gpu/drm/nouveau/dispnv50/wndw.c index 224963b533a6..c5a9bc1af5af 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/wndw.c +++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.c @@ -444,14 +444,17 @@ nv50_wndw_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state) if (ret) return ret; - ctxdma = nv50_wndw_ctxdma_new(wndw, fb); - if (IS_ERR(ctxdma)) { - nouveau_bo_unpin(fb->nvbo); - return PTR_ERR(ctxdma); + if (wndw->ctxdma.parent) { + ctxdma = nv50_wndw_ctxdma_new(wndw, fb); + if (IS_ERR(ctxdma)) { + nouveau_bo_unpin(fb->nvbo); + return PTR_ERR(ctxdma); + } + + asyw->image.handle[0] = ctxdma->object.handle; } asyw->state.fence = reservation_object_get_excl_rcu(fb->nvbo->bo.resv); - asyw->image.handle[0] = ctxdma->object.handle; asyw->image.offset[0] = fb->nvbo->bo.offset; if (wndw->func->prepare) { -- cgit v1.2.3 From 6c3796d130ed2860489885a934dcb7bb334d5eb0 Mon Sep 17 00:00:00 2001 From: "bstroesser@ts.fujitsu.com" Date: Thu, 24 May 2018 18:49:41 +0200 Subject: scsi: target: tcmu: add read length support Generally target core and TCMUser seem to work fine for tape devices and media changers. But there is at least one situation where TCMUser is not able to support sequential access device emulation correctly. The situation is when an initiator sends a SCSI READ CDB with a length that is greater than the length of the tape block to read. We can distinguish two subcases: A) The initiator sent the READ CDB with the SILI bit being set. In this case the sequential access device has to transfer the data from the tape block (only the length of the tape block) and transmit a good status. The current interface between TCMUser and the userspace does not support reduction of the read data size by the userspace program. The patch below fixes this subcase by allowing the userspace program to specify a reduced data size in read direction. B) The initiator sent the READ CDB with the SILI bit not being set. In this case the sequential access device has to transfer the data from the tape block as in A), but additionally has to transmit CHECK CONDITION with the ILI bit set and NO SENSE in the sensebytes. The information field in the sensebytes must contain the residual count. With the below patch a user space program can specify the real read data length and appropriate sensebytes. TCMUser then uses the se_cmd flag SCF_TREAT_READ_AS_NORMAL, to force target core to transmit the real data size and the sensebytes. Note: the flag SCF_TREAT_READ_AS_NORMAL is introduced by Lee Duncan's patch "[PATCH v4] target: transport should handle st FM/EOM/ILI reads" from Tue, 15 May 2018 18:25:24 -0700. Signed-off-by: Bodo Stroesser Acked-by: Mike Christie Reviewed-by: Lee Duncan Signed-off-by: Martin K. Petersen --- drivers/target/target_core_user.c | 44 ++++++++++++++++++++++++++++------- include/uapi/linux/target_core_user.h | 4 +++- 2 files changed, 39 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 7f96dfa32b9c..d8dc3d22051f 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -656,7 +656,7 @@ static void scatter_data_area(struct tcmu_dev *udev, } static void gather_data_area(struct tcmu_dev *udev, struct tcmu_cmd *cmd, - bool bidi) + bool bidi, uint32_t read_len) { struct se_cmd *se_cmd = cmd->se_cmd; int i, dbi; @@ -689,7 +689,7 @@ static void gather_data_area(struct tcmu_dev *udev, struct tcmu_cmd *cmd, for_each_sg(data_sg, sg, data_nents, i) { int sg_remaining = sg->length; to = kmap_atomic(sg_page(sg)) + sg->offset; - while (sg_remaining > 0) { + while (sg_remaining > 0 && read_len > 0) { if (block_remaining == 0) { if (from) kunmap_atomic(from); @@ -701,6 +701,8 @@ static void gather_data_area(struct tcmu_dev *udev, struct tcmu_cmd *cmd, } copy_bytes = min_t(size_t, sg_remaining, block_remaining); + if (read_len < copy_bytes) + copy_bytes = read_len; offset = DATA_BLOCK_SIZE - block_remaining; tcmu_flush_dcache_range(from, copy_bytes); memcpy(to + sg->length - sg_remaining, from + offset, @@ -708,8 +710,11 @@ static void gather_data_area(struct tcmu_dev *udev, struct tcmu_cmd *cmd, sg_remaining -= copy_bytes; block_remaining -= copy_bytes; + read_len -= copy_bytes; } kunmap_atomic(to - sg->offset); + if (read_len == 0) + break; } if (from) kunmap_atomic(from); @@ -1042,6 +1047,8 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry * { struct se_cmd *se_cmd = cmd->se_cmd; struct tcmu_dev *udev = cmd->tcmu_dev; + bool read_len_valid = false; + uint32_t read_len = se_cmd->data_length; /* * cmd has been completed already from timeout, just reclaim @@ -1056,13 +1063,28 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry * pr_warn("TCMU: Userspace set UNKNOWN_OP flag on se_cmd %p\n", cmd->se_cmd); entry->rsp.scsi_status = SAM_STAT_CHECK_CONDITION; - } else if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) { + goto done; + } + + if (se_cmd->data_direction == DMA_FROM_DEVICE && + (entry->hdr.uflags & TCMU_UFLAG_READ_LEN) && entry->rsp.read_len) { + read_len_valid = true; + if (entry->rsp.read_len < read_len) + read_len = entry->rsp.read_len; + } + + if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) { transport_copy_sense_to_cmd(se_cmd, entry->rsp.sense_buffer); - } else if (se_cmd->se_cmd_flags & SCF_BIDI) { + if (!read_len_valid ) + goto done; + else + se_cmd->se_cmd_flags |= SCF_TREAT_READ_AS_NORMAL; + } + if (se_cmd->se_cmd_flags & SCF_BIDI) { /* Get Data-In buffer before clean up */ - gather_data_area(udev, cmd, true); + gather_data_area(udev, cmd, true, read_len); } else if (se_cmd->data_direction == DMA_FROM_DEVICE) { - gather_data_area(udev, cmd, false); + gather_data_area(udev, cmd, false, read_len); } else if (se_cmd->data_direction == DMA_TO_DEVICE) { /* TODO: */ } else if (se_cmd->data_direction != DMA_NONE) { @@ -1070,7 +1092,13 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry * se_cmd->data_direction); } - target_complete_cmd(cmd->se_cmd, entry->rsp.scsi_status); +done: + if (read_len_valid) { + pr_debug("read_len = %d\n", read_len); + target_complete_cmd_with_length(cmd->se_cmd, + entry->rsp.scsi_status, read_len); + } else + target_complete_cmd(cmd->se_cmd, entry->rsp.scsi_status); out: cmd->se_cmd = NULL; @@ -1740,7 +1768,7 @@ static int tcmu_configure_device(struct se_device *dev) /* Initialise the mailbox of the ring buffer */ mb = udev->mb_addr; mb->version = TCMU_MAILBOX_VERSION; - mb->flags = TCMU_MAILBOX_FLAG_CAP_OOOC; + mb->flags = TCMU_MAILBOX_FLAG_CAP_OOOC | TCMU_MAILBOX_FLAG_CAP_READ_LEN; mb->cmdr_off = CMDR_OFF; mb->cmdr_size = udev->cmdr_size; diff --git a/include/uapi/linux/target_core_user.h b/include/uapi/linux/target_core_user.h index 6e299349b158..b7b57967d90f 100644 --- a/include/uapi/linux/target_core_user.h +++ b/include/uapi/linux/target_core_user.h @@ -44,6 +44,7 @@ #define TCMU_MAILBOX_VERSION 2 #define ALIGN_SIZE 64 /* Should be enough for most CPUs */ #define TCMU_MAILBOX_FLAG_CAP_OOOC (1 << 0) /* Out-of-order completions */ +#define TCMU_MAILBOX_FLAG_CAP_READ_LEN (1 << 1) /* Read data length */ struct tcmu_mailbox { __u16 version; @@ -71,6 +72,7 @@ struct tcmu_cmd_entry_hdr { __u16 cmd_id; __u8 kflags; #define TCMU_UFLAG_UNKNOWN_OP 0x1 +#define TCMU_UFLAG_READ_LEN 0x2 __u8 uflags; } __packed; @@ -119,7 +121,7 @@ struct tcmu_cmd_entry { __u8 scsi_status; __u8 __pad1; __u16 __pad2; - __u32 __pad3; + __u32 read_len; char sense_buffer[TCMU_SENSE_BUFFERSIZE]; } rsp; }; -- cgit v1.2.3 From f2233a33dc1fef4aa30dc11e4c676637bf358c3d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 19 Mar 2018 22:50:05 -0400 Subject: scsi: ipr: Eliminate duplicate barriers Driver does both wmb() and writel(). The latter already has a barrier on some architectures like arm64. This ends up with CPU observing two barriers back to back before executing the register write. Drivers should generally assume that the barrier implied by writel() is sufficient for ordering DMA. Remove the extraneous wmb() before it. [mkp: Squashed Arnd's and Sinan's patches] Signed-off-by: Arnd Bergmann Reported-by: Sinan Kaya Acked-by: Brian King Signed-off-by: Martin K. Petersen --- drivers/scsi/ipr.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 0a9b8b387bd2..02d65dce74e5 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -760,7 +760,6 @@ static void ipr_mask_and_clear_interrupts(struct ipr_ioa_cfg *ioa_cfg, ioa_cfg->hrrq[i].allow_interrupts = 0; spin_unlock(&ioa_cfg->hrrq[i]._lock); } - wmb(); /* Set interrupt mask to stop all new interrupts */ if (ioa_cfg->sis64) @@ -8403,7 +8402,6 @@ static int ipr_reset_enable_ioa(struct ipr_cmnd *ipr_cmd) ioa_cfg->hrrq[i].allow_interrupts = 1; spin_unlock(&ioa_cfg->hrrq[i]._lock); } - wmb(); if (ioa_cfg->sis64) { /* Set the adapter to the correct endian mode. */ writel(IPR_ENDIAN_SWAP_KEY, ioa_cfg->regs.endian_swap_reg); -- cgit v1.2.3 From 49d7bd36813ea8e6b4c97b640d24e7fbd44c84f0 Mon Sep 17 00:00:00 2001 From: Mikhail Malygin Date: Wed, 13 Jun 2018 13:05:57 +0000 Subject: scsi: qla2xxx: Spinlock recursion in qla_target The patch reverts changes done in qlt_schedule_sess_for_deletion() to avoid spinlock recursion sess->vha->work_lock should be used instead of ha->tgt.sess_lock, that can be locked in callers: qlt_reset() or qlt_handle_login() [mkp: roll in build warning reported by sfr] Fixes: 1c6cacf4ea6c04 ("scsi: qla2xxx: Fixup locking for session deletion") Cc: #v4.17 Signed-off-by: Mikhail Malygin Reported-by: Mikhail Malygin Tested-by: Mikhail Malygin Acked-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_target.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 0fea2e2326be..1027b0cb7fa3 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -1224,7 +1224,6 @@ static void qla24xx_chk_fcp_state(struct fc_port *sess) void qlt_schedule_sess_for_deletion(struct fc_port *sess) { struct qla_tgt *tgt = sess->tgt; - struct qla_hw_data *ha = sess->vha->hw; unsigned long flags; if (sess->disc_state == DSC_DELETE_PEND) @@ -1241,16 +1240,16 @@ void qlt_schedule_sess_for_deletion(struct fc_port *sess) return; } - spin_lock_irqsave(&ha->tgt.sess_lock, flags); if (sess->deleted == QLA_SESS_DELETED) sess->logout_on_delete = 0; + spin_lock_irqsave(&sess->vha->work_lock, flags); if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { - spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); + spin_unlock_irqrestore(&sess->vha->work_lock, flags); return; } sess->deleted = QLA_SESS_DELETION_IN_PROGRESS; - spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); + spin_unlock_irqrestore(&sess->vha->work_lock, flags); sess->disc_state = DSC_DELETE_PEND; -- cgit v1.2.3 From 52ab9768f723823a71dc659f0fad803a90f80236 Mon Sep 17 00:00:00 2001 From: Luis Henriques Date: Mon, 18 Jun 2018 17:08:03 +0100 Subject: scsi: scsi_debug: Fix memory leak on module unload Since commit 80c49563e250 ("scsi: scsi_debug: implement IMMED bit") there are long delays in F_SYNC_DELAY and F_SSU_DELAY. This can cause a memory leak in schedule_resp(), which can be invoked while unloading the scsi_debug module: free_all_queued() had already freed all sd_dp and schedule_resp will alloc a new one, which will never get freed. Here's the kmemleak report while running xfstests generic/350: unreferenced object 0xffff88007d752b00 (size 128): comm "rmmod", pid 26940, jiffies 4295816945 (age 7.588s) hex dump (first 32 bytes): 00 2b 75 7d 00 88 ff ff 00 00 00 00 00 00 00 00 .+u}............ 00 00 00 00 00 00 00 00 8e 31 a2 34 5f 03 00 00 .........1.4_... backtrace: [<000000002abd83d0>] 0xffffffffa000705e [<000000004c063fda>] scsi_dispatch_cmd+0xc7/0x1a0 [<000000000c119a00>] scsi_request_fn+0x251/0x550 [<000000009de0c736>] __blk_run_queue+0x3f/0x60 [<000000001c4453c8>] blk_execute_rq_nowait+0x98/0xd0 [<00000000d17ec79f>] blk_execute_rq+0x3a/0x50 [<00000000a7654b6e>] scsi_execute+0x113/0x250 [<00000000fd78f7cd>] sd_sync_cache+0x95/0x160 [<0000000024dacb14>] sd_shutdown+0x9b/0xd0 [<00000000e9101710>] sd_remove+0x5f/0xb0 [<00000000c43f0d63>] device_release_driver_internal+0x13c/0x1f0 [<00000000e8ad57b6>] bus_remove_device+0xe9/0x160 [<00000000713a7b8a>] device_del+0x120/0x320 [<00000000e5db670c>] __scsi_remove_device+0x115/0x150 [<00000000eccbef30>] scsi_forget_host+0x20/0x60 [<00000000cd5a0738>] scsi_remove_host+0x6d/0x120 Cc: stable@vger.kernel.org # v4.17+ Signed-off-by: Luis Henriques Acked-by: Douglas Gilbert Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 24d7496cd9e2..364e71861bfd 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -5507,9 +5507,9 @@ static void __exit scsi_debug_exit(void) int k = sdebug_add_host; stop_all_queued(); - free_all_queued(); for (; k; k--) sdebug_remove_adapter(); + free_all_queued(); driver_unregister(&sdebug_driverfs_driver); bus_unregister(&pseudo_lld_bus); root_device_unregister(pseudo_primary); -- cgit v1.2.3 From ee3dbcf963c17775417d2fdf74e60f0c1b563dda Mon Sep 17 00:00:00 2001 From: Ilia Lin Date: Sun, 17 Jun 2018 21:58:42 +0200 Subject: cpufreq: kryo: Fix possible error code dereference In event of error returned by the nvmem_cell_read() non-pointer value may be dereferenced. Fix this with error handling. Additionally free the allocated speedbin buffer, as per the API. Fixes: 9ce36edd1a52 (cpufreq: Add Kryo CPU scaling driver) Signed-off-by: Ilia Lin Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/qcom-cpufreq-kryo.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/cpufreq/qcom-cpufreq-kryo.c b/drivers/cpufreq/qcom-cpufreq-kryo.c index d049fe4b80c4..74b9b93d511b 100644 --- a/drivers/cpufreq/qcom-cpufreq-kryo.c +++ b/drivers/cpufreq/qcom-cpufreq-kryo.c @@ -115,6 +115,8 @@ static int qcom_cpufreq_kryo_probe(struct platform_device *pdev) speedbin = nvmem_cell_read(speedbin_nvmem, &len); nvmem_cell_put(speedbin_nvmem); + if (IS_ERR(speedbin)) + return PTR_ERR(speedbin); switch (msm8996_version) { case MSM8996_V3: @@ -127,6 +129,7 @@ static int qcom_cpufreq_kryo_probe(struct platform_device *pdev) BUG(); break; } + kfree(speedbin); for_each_possible_cpu(cpu) { cpu_dev = get_cpu_device(cpu); -- cgit v1.2.3 From 5ad7346b4ae2d59cfee106ec8e40ee2561476d47 Mon Sep 17 00:00:00 2001 From: Ilia Lin Date: Sun, 17 Jun 2018 22:01:46 +0200 Subject: cpufreq: kryo: Add module remove and exit Add device remove and module exit code to make the driver functioning as a loadable module. Fixes: ac28927659be (cpufreq: kryo: allow building as a loadable module) Signed-off-by: Ilia Lin Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/qcom-cpufreq-kryo.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/qcom-cpufreq-kryo.c b/drivers/cpufreq/qcom-cpufreq-kryo.c index 74b9b93d511b..01bddacf5c3b 100644 --- a/drivers/cpufreq/qcom-cpufreq-kryo.c +++ b/drivers/cpufreq/qcom-cpufreq-kryo.c @@ -42,6 +42,8 @@ enum _msm8996_version { NUM_OF_MSM8996_VERSIONS, }; +struct platform_device *cpufreq_dt_pdev, *kryo_cpufreq_pdev; + static enum _msm8996_version __init qcom_cpufreq_kryo_get_msm_id(void) { size_t len; @@ -74,7 +76,6 @@ static enum _msm8996_version __init qcom_cpufreq_kryo_get_msm_id(void) static int qcom_cpufreq_kryo_probe(struct platform_device *pdev) { struct opp_table *opp_tables[NR_CPUS] = {0}; - struct platform_device *cpufreq_dt_pdev; enum _msm8996_version msm8996_version; struct nvmem_cell *speedbin_nvmem; struct device_node *np; @@ -165,8 +166,15 @@ free_opp: return ret; } +static int qcom_cpufreq_kryo_remove(struct platform_device *pdev) +{ + platform_device_unregister(cpufreq_dt_pdev); + return 0; +} + static struct platform_driver qcom_cpufreq_kryo_driver = { .probe = qcom_cpufreq_kryo_probe, + .remove = qcom_cpufreq_kryo_remove, .driver = { .name = "qcom-cpufreq-kryo", }, @@ -201,8 +209,9 @@ static int __init qcom_cpufreq_kryo_init(void) if (unlikely(ret < 0)) return ret; - ret = PTR_ERR_OR_ZERO(platform_device_register_simple( - "qcom-cpufreq-kryo", -1, NULL, 0)); + kryo_cpufreq_pdev = platform_device_register_simple( + "qcom-cpufreq-kryo", -1, NULL, 0); + ret = PTR_ERR_OR_ZERO(kryo_cpufreq_pdev); if (0 == ret) return 0; @@ -211,5 +220,12 @@ static int __init qcom_cpufreq_kryo_init(void) } module_init(qcom_cpufreq_kryo_init); +static void __init qcom_cpufreq_kryo_exit(void) +{ + platform_device_unregister(kryo_cpufreq_pdev); + platform_driver_unregister(&qcom_cpufreq_kryo_driver); +} +module_exit(qcom_cpufreq_kryo_exit); + MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Kryo CPUfreq driver"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From ff7c9917143b3a6cf2fa61212a32d67cf259bf9c Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Mon, 18 Jun 2018 12:47:45 -0700 Subject: cpufreq: intel_pstate: Fix scaling max/min limits with Turbo 3.0 When scaling max/min settings are changed, internally they are converted to a ratio using the max turbo 1 core turbo frequency. This works fine when 1 core max is same irrespective of the core. But under Turbo 3.0, this will not be the case. For example: Core 0: max turbo pstate: 43 (4.3GHz) Core 1: max turbo pstate: 45 (4.5GHz) In this case 1 core turbo ratio will be maximum of all, so it will be 45 (4.5GHz). Suppose scaling max is set to 4GHz (ratio 40) for all cores ,then on core one it will be = max_state * policy->max / max_freq; = 43 * (4000000/4500000) = 38 (3.8GHz) = 38 which is 200MHz less than the desired. On core2, it will be correctly set to ratio 40 (4GHz). Same holds true for scaling min frequency limit. So this requires usage of correct turbo max frequency for core one, which in this case is 4.3GHz. So we need to adjust per CPU cpu->pstate.turbo_freq using the maximum HWP ratio of that core. This change uses the HWP capability of a core to adjust max turbo frequency. But since Broadwell HWP doesn't use ratios in the HWP capabilities, we have to use legacy max 1 core turbo ratio. This is not a problem as the HWP capabilities don't differ among cores in Broadwell. We need to check for non Broadwell CPU model for applying this change, though. Signed-off-by: Srinivas Pandruvada Cc: 4.6+ # 4.6+ Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 1de5ec8d5ea3..ece120da3353 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -294,6 +294,7 @@ struct pstate_funcs { static struct pstate_funcs pstate_funcs __read_mostly; static int hwp_active __read_mostly; +static int hwp_mode_bdw __read_mostly; static bool per_cpu_limits __read_mostly; static bool hwp_boost __read_mostly; @@ -1413,7 +1414,15 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(); cpu->pstate.scaling = pstate_funcs.get_scaling(); cpu->pstate.max_freq = cpu->pstate.max_pstate * cpu->pstate.scaling; - cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * cpu->pstate.scaling; + + if (hwp_active && !hwp_mode_bdw) { + unsigned int phy_max, current_max; + + intel_pstate_get_hwp_max(cpu->cpu, &phy_max, ¤t_max); + cpu->pstate.turbo_freq = phy_max * cpu->pstate.scaling; + } else { + cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * cpu->pstate.scaling; + } if (pstate_funcs.get_aperf_mperf_shift) cpu->aperf_mperf_shift = pstate_funcs.get_aperf_mperf_shift(); @@ -2467,28 +2476,36 @@ static inline bool intel_pstate_has_acpi_ppc(void) { return false; } static inline void intel_pstate_request_control_from_smm(void) {} #endif /* CONFIG_ACPI */ +#define INTEL_PSTATE_HWP_BROADWELL 0x01 + +#define ICPU_HWP(model, hwp_mode) \ + { X86_VENDOR_INTEL, 6, model, X86_FEATURE_HWP, hwp_mode } + static const struct x86_cpu_id hwp_support_ids[] __initconst = { - { X86_VENDOR_INTEL, 6, X86_MODEL_ANY, X86_FEATURE_HWP }, + ICPU_HWP(INTEL_FAM6_BROADWELL_X, INTEL_PSTATE_HWP_BROADWELL), + ICPU_HWP(INTEL_FAM6_BROADWELL_XEON_D, INTEL_PSTATE_HWP_BROADWELL), + ICPU_HWP(X86_MODEL_ANY, 0), {} }; static int __init intel_pstate_init(void) { + const struct x86_cpu_id *id; int rc; if (no_load) return -ENODEV; - if (x86_match_cpu(hwp_support_ids)) { + id = x86_match_cpu(hwp_support_ids); + if (id) { copy_cpu_funcs(&core_funcs); if (!no_hwp) { hwp_active++; + hwp_mode_bdw = id->driver_data; intel_pstate.attr = hwp_cpufreq_attrs; goto hwp_cpu_matched; } } else { - const struct x86_cpu_id *id; - id = x86_match_cpu(intel_pstate_cpu_ids); if (!id) return -ENODEV; -- cgit v1.2.3 From 12baf7721143c83150fa973484b7b5fcd86b23f0 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Tue, 19 Jun 2018 17:31:24 +1200 Subject: mtd: rawnand: micron: add ONFI_FEATURE_ON_DIE_ECC to supported features Add ONFI_FEATURE_ON_DIE_ECC to the set/get features list for Micron NAND flash. Fixes: 789157e41a06 ("mtd: rawnand: allow vendors to declare (un)supported features") Cc: Signed-off-by: Chris Packham Reviewed-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/nand_micron.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index 0af45b134c0c..5ec4c90a637d 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -66,7 +66,9 @@ static int micron_nand_onfi_init(struct nand_chip *chip) if (p->supports_set_get_features) { set_bit(ONFI_FEATURE_ADDR_READ_RETRY, p->set_feature_list); + set_bit(ONFI_FEATURE_ON_DIE_ECC, p->set_feature_list); set_bit(ONFI_FEATURE_ADDR_READ_RETRY, p->get_feature_list); + set_bit(ONFI_FEATURE_ON_DIE_ECC, p->get_feature_list); } return 0; -- cgit v1.2.3 From 93ef2dc0c4f7acf4253882a03c11f2ca8e41f387 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 6 Jun 2018 14:22:24 +0000 Subject: usb: dwc3: Fix error return code in dwc3_qcom_probe() Fix to return error code -ENODEV from the get device failed error handling case instead of 0, as done elsewhere in this function. Fixes: a4333c3a6ba9 ("usb: dwc3: Add Qualcomm DWC3 glue driver") Signed-off-by: Wei Yongjun Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-qcom.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index b0e67ab2f98c..8854e428ae08 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -490,6 +490,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev) qcom->dwc3 = of_find_device_by_node(dwc3_np); if (!qcom->dwc3) { dev_err(&pdev->dev, "failed to get dwc3 platform device\n"); + ret = -ENODEV; goto depopulate; } -- cgit v1.2.3 From 9925e6ebe5c2da9601037cca73462782900b9190 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 25 May 2018 18:06:20 +0200 Subject: usb: dwc3: qcom: mark PM functions as __maybe_unused The #ifdef guards around these are wrong, resulting in warnings in certain configurations: drivers/usb/dwc3/dwc3-qcom.c:244:12: error: 'dwc3_qcom_resume' defined but not used [-Werror=unused-function] static int dwc3_qcom_resume(struct dwc3_qcom *qcom) ^~~~~~~~~~~~~~~~ drivers/usb/dwc3/dwc3-qcom.c:223:12: error: 'dwc3_qcom_suspend' defined but not used [-Werror=unused-function] static int dwc3_qcom_suspend(struct dwc3_qcom *qcom) This replaces the guards with __maybe_unused annotations to shut up the warnings and give better compile time coverage. Fixes: a4333c3a6ba9 ("usb: dwc3: Add Qualcomm DWC3 glue driver") Reviewed-by: Douglas Anderson Acked-by: Geert Uytterhoeven Signed-off-by: Arnd Bergmann Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-qcom.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 8854e428ae08..a6d0203e40b6 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -548,8 +548,7 @@ static int dwc3_qcom_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int dwc3_qcom_pm_suspend(struct device *dev) +static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev) { struct dwc3_qcom *qcom = dev_get_drvdata(dev); int ret = 0; @@ -561,7 +560,7 @@ static int dwc3_qcom_pm_suspend(struct device *dev) return ret; } -static int dwc3_qcom_pm_resume(struct device *dev) +static int __maybe_unused dwc3_qcom_pm_resume(struct device *dev) { struct dwc3_qcom *qcom = dev_get_drvdata(dev); int ret; @@ -572,23 +571,20 @@ static int dwc3_qcom_pm_resume(struct device *dev) return ret; } -#endif -#ifdef CONFIG_PM -static int dwc3_qcom_runtime_suspend(struct device *dev) +static int __maybe_unused dwc3_qcom_runtime_suspend(struct device *dev) { struct dwc3_qcom *qcom = dev_get_drvdata(dev); return dwc3_qcom_suspend(qcom); } -static int dwc3_qcom_runtime_resume(struct device *dev) +static int __maybe_unused dwc3_qcom_runtime_resume(struct device *dev) { struct dwc3_qcom *qcom = dev_get_drvdata(dev); return dwc3_qcom_resume(qcom); } -#endif static const struct dev_pm_ops dwc3_qcom_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(dwc3_qcom_pm_suspend, dwc3_qcom_pm_resume) -- cgit v1.2.3 From 22bb5cfdf13ae70c6a34508a16cfeee48f162443 Mon Sep 17 00:00:00 2001 From: Artur Petrosyan Date: Wed, 23 May 2018 09:26:08 -0400 Subject: usb: dwc2: Fix host exit from hibernation flow. In case when a hub is connected to DWC2 host auto suspend occurs and host goes to hibernation. When any device connected to hub host hibernation exiting incorrectly. - Added dwc2_hcd_rem_wakeup() function call to exit from suspend state by remote wakeup. - Increase timeout value for port suspend bit to be set. Acked-by: Minas Harutyunyan Signed-off-by: Artur Petrosyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/hcd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index edaf0b6af4f0..bae08a7d7abd 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -5435,7 +5435,7 @@ int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg) dwc2_writel(hprt0, hsotg->regs + HPRT0); /* Wait for the HPRT0.PrtSusp register field to be set */ - if (dwc2_hsotg_wait_bit_set(hsotg, HPRT0, HPRT0_SUSP, 300)) + if (dwc2_hsotg_wait_bit_set(hsotg, HPRT0, HPRT0_SUSP, 3000)) dev_warn(hsotg->dev, "Suspend wasn't generated\n"); /* @@ -5616,6 +5616,8 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, return ret; } + dwc2_hcd_rem_wakeup(hsotg); + hsotg->hibernated = 0; hsotg->bus_suspended = 0; hsotg->lx_state = DWC2_L0; -- cgit v1.2.3 From 8760675932ddb614e83702117d36ea644050c609 Mon Sep 17 00:00:00 2001 From: William Wu Date: Mon, 21 May 2018 18:12:00 +0800 Subject: usb: dwc2: fix the incorrect bitmaps for the ports of multi_tt hub The dwc2_get_ls_map() use ttport to reference into the bitmap if we're on a multi_tt hub. But the bitmaps index from 0 to (hub->maxchild - 1), while the ttport index from 1 to hub->maxchild. This will cause invalid memory access when the number of ttport is hub->maxchild. Without this patch, I can easily meet a Kernel panic issue if connect a low-speed USB mouse with the max port of FE2.1 multi-tt hub (1a40:0201) on rk3288 platform. Fixes: 9f9f09b048f5 ("usb: dwc2: host: Totally redo the microframe scheduler") Cc: Reviewed-by: Douglas Anderson Acked-by: Minas Harutyunyan hminas@synopsys.com> Signed-off-by: William Wu Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/hcd_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index d7c3d6c776d8..9c55d1addba9 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -383,7 +383,7 @@ static unsigned long *dwc2_get_ls_map(struct dwc2_hsotg *hsotg, /* Get the map and adjust if this is a multi_tt hub */ map = qh->dwc_tt->periodic_bitmaps; if (qh->dwc_tt->usb_tt->multi) - map += DWC2_ELEMENTS_PER_LS_BITMAP * qh->ttport; + map += DWC2_ELEMENTS_PER_LS_BITMAP * (qh->ttport - 1); return map; } -- cgit v1.2.3 From af424a410749ed7e0c2bffd3cedbc7c274d0ff6f Mon Sep 17 00:00:00 2001 From: William Wu Date: Fri, 11 May 2018 17:46:31 +0800 Subject: usb: dwc2: alloc dma aligned buffer for isoc split in The commit 3bc04e28a030 ("usb: dwc2: host: Get aligned DMA in a more supported way") rips out a lot of code to simply the allocation of aligned DMA. However, it also introduces a new issue when use isoc split in transfer. In my test case, I connect the dwc2 controller with an usb hs Hub (GL852G-12), and plug an usb fs audio device (Plantronics headset) into the downstream port of Hub. Then use the usb mic to record, we can find noise when playback. It's because that the usb Hub uses an MDATA for the first transaction and a DATA0 for the second transaction for the isoc split in transaction. An typical isoc split in transaction sequence like this: - SSPLIT IN transaction - CSPLIT IN transaction - MDATA packet - CSPLIT IN transaction - DATA0 packet The DMA address of MDATA (urb->dma) is always DWORD-aligned, but the DMA address of DATA0 (urb->dma + qtd->isoc_split_offset) may not be DWORD-aligned, it depends on the qtd->isoc_split_offset (the length of MDATA). In my test case, the length of MDATA is usually unaligned, this cause DATA0 packet transmission error. This patch use kmem_cache to allocate aligned DMA buf for isoc split in transaction. Note that according to usb 2.0 spec, the maximum data payload size is 1023 bytes for each fs isoc ep, and the maximum allowable interrupt data payload size is 64 bytes or less for fs interrupt ep. So we set the size of object to be 1024 bytes in the kmem cache. Tested-by: Gevorg Sahakyan Tested-by: Heiko Stuebner Acked-by: Minas Harutyunyan hminas@synopsys.com> Signed-off-by: William Wu Reviewed-by: Douglas Anderson Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 3 ++ drivers/usb/dwc2/hcd.c | 89 +++++++++++++++++++++++++++++++++++++++++--- drivers/usb/dwc2/hcd.h | 8 ++++ drivers/usb/dwc2/hcd_intr.c | 8 ++++ drivers/usb/dwc2/hcd_queue.c | 3 ++ 5 files changed, 106 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 4a56ac772a3c..71b3b08ad516 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -1004,6 +1004,7 @@ struct dwc2_hregs_backup { * @frame_list_sz: Frame list size * @desc_gen_cache: Kmem cache for generic descriptors * @desc_hsisoc_cache: Kmem cache for hs isochronous descriptors + * @unaligned_cache: Kmem cache for DMA mode to handle non-aligned buf * * These are for peripheral mode: * @@ -1177,6 +1178,8 @@ struct dwc2_hsotg { u32 frame_list_sz; struct kmem_cache *desc_gen_cache; struct kmem_cache *desc_hsisoc_cache; + struct kmem_cache *unaligned_cache; +#define DWC2_KMEM_UNALIGNED_BUF_SIZE 1024 #endif /* CONFIG_USB_DWC2_HOST || CONFIG_USB_DWC2_DUAL_ROLE */ diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index bae08a7d7abd..b1104be3429c 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -1567,11 +1567,20 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg, } if (hsotg->params.host_dma) { - dwc2_writel((u32)chan->xfer_dma, - hsotg->regs + HCDMA(chan->hc_num)); + dma_addr_t dma_addr; + + if (chan->align_buf) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "align_buf\n"); + dma_addr = chan->align_buf; + } else { + dma_addr = chan->xfer_dma; + } + dwc2_writel((u32)dma_addr, hsotg->regs + HCDMA(chan->hc_num)); + if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "Wrote %08lx to HCDMA(%d)\n", - (unsigned long)chan->xfer_dma, chan->hc_num); + (unsigned long)dma_addr, chan->hc_num); } /* Start the split */ @@ -2625,6 +2634,35 @@ static void dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg, } } +static int dwc2_alloc_split_dma_aligned_buf(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh, + struct dwc2_host_chan *chan) +{ + if (!hsotg->unaligned_cache || + chan->max_packet > DWC2_KMEM_UNALIGNED_BUF_SIZE) + return -ENOMEM; + + if (!qh->dw_align_buf) { + qh->dw_align_buf = kmem_cache_alloc(hsotg->unaligned_cache, + GFP_ATOMIC | GFP_DMA); + if (!qh->dw_align_buf) + return -ENOMEM; + } + + qh->dw_align_buf_dma = dma_map_single(hsotg->dev, qh->dw_align_buf, + DWC2_KMEM_UNALIGNED_BUF_SIZE, + DMA_FROM_DEVICE); + + if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) { + dev_err(hsotg->dev, "can't map align_buf\n"); + chan->align_buf = 0; + return -EINVAL; + } + + chan->align_buf = qh->dw_align_buf_dma; + return 0; +} + #define DWC2_USB_DMA_ALIGN 4 struct dma_aligned_buffer { @@ -2802,6 +2840,32 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) /* Set the transfer attributes */ dwc2_hc_init_xfer(hsotg, chan, qtd); + /* For non-dword aligned buffers */ + if (hsotg->params.host_dma && qh->do_split && + chan->ep_is_in && (chan->xfer_dma & 0x3)) { + dev_vdbg(hsotg->dev, "Non-aligned buffer\n"); + if (dwc2_alloc_split_dma_aligned_buf(hsotg, qh, chan)) { + dev_err(hsotg->dev, + "Failed to allocate memory to handle non-aligned buffer\n"); + /* Add channel back to free list */ + chan->align_buf = 0; + chan->multi_count = 0; + list_add_tail(&chan->hc_list_entry, + &hsotg->free_hc_list); + qtd->in_process = 0; + qh->channel = NULL; + return -ENOMEM; + } + } else { + /* + * We assume that DMA is always aligned in non-split + * case or split out case. Warn if not. + */ + WARN_ON_ONCE(hsotg->params.host_dma && + (chan->xfer_dma & 0x3)); + chan->align_buf = 0; + } + if (chan->ep_type == USB_ENDPOINT_XFER_INT || chan->ep_type == USB_ENDPOINT_XFER_ISOC) /* @@ -5246,6 +5310,19 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg) } } + if (hsotg->params.host_dma) { + /* + * Create kmem caches to handle non-aligned buffer + * in Buffer DMA mode. + */ + hsotg->unaligned_cache = kmem_cache_create("dwc2-unaligned-dma", + DWC2_KMEM_UNALIGNED_BUF_SIZE, 4, + SLAB_CACHE_DMA, NULL); + if (!hsotg->unaligned_cache) + dev_err(hsotg->dev, + "unable to create dwc2 unaligned cache\n"); + } + hsotg->otg_port = 1; hsotg->frame_list = NULL; hsotg->frame_list_dma = 0; @@ -5280,8 +5357,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg) return 0; error4: - kmem_cache_destroy(hsotg->desc_gen_cache); + kmem_cache_destroy(hsotg->unaligned_cache); kmem_cache_destroy(hsotg->desc_hsisoc_cache); + kmem_cache_destroy(hsotg->desc_gen_cache); error3: dwc2_hcd_release(hsotg); error2: @@ -5322,8 +5400,9 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) usb_remove_hcd(hcd); hsotg->priv = NULL; - kmem_cache_destroy(hsotg->desc_gen_cache); + kmem_cache_destroy(hsotg->unaligned_cache); kmem_cache_destroy(hsotg->desc_hsisoc_cache); + kmem_cache_destroy(hsotg->desc_gen_cache); dwc2_hcd_release(hsotg); usb_put_hcd(hcd); diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index 7db1ee7e7a77..5502a501f516 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -76,6 +76,8 @@ struct dwc2_qh; * (micro)frame * @xfer_buf: Pointer to current transfer buffer position * @xfer_dma: DMA address of xfer_buf + * @align_buf: In Buffer DMA mode this will be used if xfer_buf is not + * DWORD aligned * @xfer_len: Total number of bytes to transfer * @xfer_count: Number of bytes transferred so far * @start_pkt_count: Packet count at start of transfer @@ -133,6 +135,7 @@ struct dwc2_host_chan { u8 *xfer_buf; dma_addr_t xfer_dma; + dma_addr_t align_buf; u32 xfer_len; u32 xfer_count; u16 start_pkt_count; @@ -302,6 +305,9 @@ struct dwc2_hs_transfer_time { * speed. Note that this is in "schedule slice" which * is tightly packed. * @ntd: Actual number of transfer descriptors in a list + * @dw_align_buf: Used instead of original buffer if its physical address + * is not dword-aligned + * @dw_align_buf_dma: DMA address for dw_align_buf * @qtd_list: List of QTDs for this QH * @channel: Host channel currently processing transfers for this QH * @qh_list_entry: Entry for QH in either the periodic or non-periodic @@ -350,6 +356,8 @@ struct dwc2_qh { struct dwc2_hs_transfer_time hs_transfers[DWC2_HS_SCHEDULE_UFRAMES]; u32 ls_start_schedule_slice; u16 ntd; + u8 *dw_align_buf; + dma_addr_t dw_align_buf_dma; struct list_head qtd_list; struct dwc2_host_chan *channel; struct list_head qh_list_entry; diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index fbea5e3fb947..f3429ac4a6bb 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -950,6 +950,14 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg, frame_desc->actual_length += len; + if (chan->align_buf) { + dev_vdbg(hsotg->dev, "non-aligned buffer\n"); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + DWC2_KMEM_UNALIGNED_BUF_SIZE, DMA_FROM_DEVICE); + memcpy(qtd->urb->buf + (chan->xfer_dma - qtd->urb->dma), + chan->qh->dw_align_buf, len); + } + qtd->isoc_split_offset += len; hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 9c55d1addba9..301ced1618f8 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -1696,6 +1696,9 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) if (qh->desc_list) dwc2_hcd_qh_free_ddma(hsotg, qh); + else if (hsotg->unaligned_cache && qh->dw_align_buf) + kmem_cache_free(hsotg->unaligned_cache, qh->dw_align_buf); + kfree(qh); } -- cgit v1.2.3 From 70c3c8cb83856758025c2a211dd022bc0478922a Mon Sep 17 00:00:00 2001 From: William Wu Date: Fri, 11 May 2018 17:46:32 +0800 Subject: usb: dwc2: fix isoc split in transfer with no data If isoc split in transfer with no data (the length of DATA0 packet is zero), we can't simply return immediately. Because the DATA0 can be the first transaction or the second transaction for the isoc split in transaction. If the DATA0 packet with no data is in the first transaction, we can return immediately. But if the DATA0 packet with no data is in the second transaction of isoc split in transaction sequence, we need to increase the qtd->isoc_frame_index and giveback urb to device driver if needed, otherwise, the MDATA packet will be lost. A typical test case is that connect the dwc2 controller with an usb hs Hub (GL852G-12), and plug an usb fs audio device (Plantronics headset) into the downstream port of Hub. Then use the usb mic to record, we can find noise when playback. In the case, the isoc split in transaction sequence like this: - SSPLIT IN transaction - CSPLIT IN transaction - MDATA packet (176 bytes) - CSPLIT IN transaction - DATA0 packet (0 byte) This patch use both the length of DATA0 and qtd->isoc_split_offset to check if the DATA0 is in the second transaction. Tested-by: Gevorg Sahakyan Tested-by: Heiko Stuebner Acked-by: Minas Harutyunyan hminas@synopsys.com> Signed-off-by: William Wu Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/hcd_intr.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index f3429ac4a6bb..ed7f05cf4906 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -942,9 +942,8 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg, frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; len = dwc2_get_actual_xfer_length(hsotg, chan, chnum, qtd, DWC2_HC_XFER_COMPLETE, NULL); - if (!len) { + if (!len && !qtd->isoc_split_offset) { qtd->complete_split = 0; - qtd->isoc_split_offset = 0; return 0; } -- cgit v1.2.3 From 980900d6318066b9f8314bfb87329a20fd0d1ca4 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Fri, 25 May 2018 17:24:57 +0800 Subject: usb: gadget: composite: fix delayed_status race condition when set_interface It happens when enable debug log, if set_alt() returns USB_GADGET_DELAYED_STATUS and usb_composite_setup_continue() is called before increasing count of @delayed_status, so fix it by using spinlock of @cdev->lock. Signed-off-by: Chunfeng Yun Tested-by: Jay Hsu Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index f242c2bcea81..d2fa071c21b1 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1719,6 +1719,8 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) */ if (w_value && !f->get_alt) break; + + spin_lock(&cdev->lock); value = f->set_alt(f, w_index, w_value); if (value == USB_GADGET_DELAYED_STATUS) { DBG(cdev, @@ -1728,6 +1730,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) DBG(cdev, "delayed_status count %d\n", cdev->delayed_status); } + spin_unlock(&cdev->lock); break; case USB_REQ_GET_INTERFACE: if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) -- cgit v1.2.3 From 9bb073a053f0464ea74a4d4c331fdb7da58568d6 Mon Sep 17 00:00:00 2001 From: Grigor Tovmasyan Date: Thu, 24 May 2018 18:22:30 +0400 Subject: usb: gadget: dwc2: fix memory leak in gadget_init() Freed allocated request for ep0 to prevent memory leak in case when dwc2_driver_probe() failed. Cc: Stefan Wahren Cc: Marek Szyprowski Tested-by: Stefan Wahren Tested-by: Marek Szyprowski Acked-by: Minas Harutyunyan Signed-off-by: Grigor Tovmasyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index fa3b6f361074..76e4f2e7d947 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -4735,9 +4735,11 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg) } ret = usb_add_gadget_udc(dev, &hsotg->gadget); - if (ret) + if (ret) { + dwc2_hsotg_ep_free_request(&hsotg->eps_out[0]->ep, + hsotg->ctrl_req); return ret; - + } dwc2_hsotg_dump(hsotg); return 0; @@ -4751,6 +4753,7 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg) int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg) { usb_del_gadget_udc(&hsotg->gadget); + dwc2_hsotg_ep_free_request(&hsotg->eps_out[0]->ep, hsotg->ctrl_req); return 0; } -- cgit v1.2.3 From 1d8e5c00275825fc42aaa5597dab1d0b5b26bb64 Mon Sep 17 00:00:00 2001 From: Minas Harutyunyan Date: Wed, 23 May 2018 16:24:44 +0400 Subject: dwc2: gadget: Fix ISOC IN DDMA PID bitfield value calculation PID bitfield in descriptor should be set based on particular request length, not based on EP's mc value. PID value can't be set to 0 even request length is 0. Signed-off-by: Minas Harutyunyan Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 76e4f2e7d947..a0f82cca2d9a 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -812,6 +812,7 @@ static int dwc2_gadget_fill_isoc_desc(struct dwc2_hsotg_ep *hs_ep, u32 index; u32 maxsize = 0; u32 mask = 0; + u8 pid = 0; maxsize = dwc2_gadget_get_desc_params(hs_ep, &mask); @@ -840,7 +841,11 @@ static int dwc2_gadget_fill_isoc_desc(struct dwc2_hsotg_ep *hs_ep, ((len << DEV_DMA_NBYTES_SHIFT) & mask)); if (hs_ep->dir_in) { - desc->status |= ((hs_ep->mc << DEV_DMA_ISOC_PID_SHIFT) & + if (len) + pid = DIV_ROUND_UP(len, hs_ep->ep.maxpacket); + else + pid = 1; + desc->status |= ((pid << DEV_DMA_ISOC_PID_SHIFT) & DEV_DMA_ISOC_PID_MASK) | ((len % hs_ep->ep.maxpacket) ? DEV_DMA_SHORT : 0) | -- cgit v1.2.3 From c5c2a97b3ac7d1ec19e7cff9e38caca6afefc3de Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Thu, 14 Jun 2018 15:56:08 +0200 Subject: PM / OPP: Update voltage in case freq == old_freq This commit fixes a rare but possible case when the clk rate is updated without update of the regulator voltage. At boot up, CPUfreq checks if the system is running at the right freq. This is a sanity check in case a bootloader set clk rate that is outside of freq table present with cpufreq core. In such cases system can be unstable so better to change it to a freq that is preset in freq-table. The CPUfreq takes next freq that is >= policy->cur and this is our target_freq that needs to be set now. dev_pm_opp_set_rate(dev, target_freq) checks the target_freq and the old_freq (a current rate). If these are equal it returns early. If not, it searches for OPP (old_opp) that fits best to old_freq (not listed in the table) and updates old_freq (!). Here, we can end up with old_freq = old_opp.rate = target_freq, which is not handled in _generic_set_opp_regulator(). It's supposed to update voltage only when freq > old_freq || freq > old_freq. if (freq > old_freq) { ret = _set_opp_voltage(dev, reg, new_supply); [...] if (freq < old_freq) { ret = _set_opp_voltage(dev, reg, new_supply); if (ret) It results in, no voltage update while clk rate is updated. Example: freq-table = { 1000MHz 1.15V 666MHZ 1.10V 333MHz 1.05V } boot-up-freq = 800MHz # not listed in freq-table freq = target_freq = 1GHz old_freq = 800Mhz old_opp = _find_freq_ceil(opp_table, &old_freq); #(old_freq is modified!) old_freq = 1GHz Fixes: 6a0712f6f199 ("PM / OPP: Add dev_pm_opp_set_rate()") Cc: 4.6+ # v4.6+ Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/opp/core.c b/drivers/opp/core.c index ab2f3fead6b1..31ff03dbeb83 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -598,7 +598,7 @@ static int _generic_set_opp_regulator(const struct opp_table *opp_table, } /* Scaling up? Scale voltage before frequency */ - if (freq > old_freq) { + if (freq >= old_freq) { ret = _set_opp_voltage(dev, reg, new_supply); if (ret) goto restore_voltage; -- cgit v1.2.3 From 3f77f244d8ec28e3a0a81240ffac7d626390060c Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Mon, 18 Jun 2018 22:41:03 +0200 Subject: mtd: rawnand: mxc: set spare area size register explicitly The v21 version of the NAND flash controller contains a Spare Area Size Register (SPAS) at offset 0x10. Its setting defaults to the maximum spare area size of 218 bytes. The size that is set in this register is used by the controller when it calculates the ECC bytes internally in hardware. Usually, this register is updated from settings in the IIM fuses when the system is booting from NAND flash. For other boot media, however, the SPAS register remains at the default setting, which may not work for the particular flash chip on the board. The same goes for flash chips whose configuration cannot be set in the IIM fuses (e.g. chips with 2k sector size and 128 bytes spare area size can't be configured in the IIM fuses on imx25 systems). Set the SPAS register explicitly during the preset operation. Derive the register value from mtd->oobsize that was detected during probe by decoding the flash chip's ID bytes. While at it, rename the define for the spare area register's offset to NFC_V21_RSLTSPARE_AREA. The register at offset 0x10 on v1 controllers is different from the register on v21 controllers. Fixes: d484018 ("mtd: mxc_nand: set NFC registers after reset") Cc: stable@vger.kernel.org Signed-off-by: Martin Kaiser Reviewed-by: Sascha Hauer Reviewed-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/mxc_nand.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index 45786e707b7b..26cef218bb43 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -48,7 +48,7 @@ #define NFC_V1_V2_CONFIG (host->regs + 0x0a) #define NFC_V1_V2_ECC_STATUS_RESULT (host->regs + 0x0c) #define NFC_V1_V2_RSLTMAIN_AREA (host->regs + 0x0e) -#define NFC_V1_V2_RSLTSPARE_AREA (host->regs + 0x10) +#define NFC_V21_RSLTSPARE_AREA (host->regs + 0x10) #define NFC_V1_V2_WRPROT (host->regs + 0x12) #define NFC_V1_UNLOCKSTART_BLKADDR (host->regs + 0x14) #define NFC_V1_UNLOCKEND_BLKADDR (host->regs + 0x16) @@ -1274,6 +1274,9 @@ static void preset_v2(struct mtd_info *mtd) writew(config1, NFC_V1_V2_CONFIG1); /* preset operation */ + /* spare area size in 16-bit half-words */ + writew(mtd->oobsize / 2, NFC_V21_RSLTSPARE_AREA); + /* Unlock the internal RAM Buffer */ writew(0x2, NFC_V1_V2_CONFIG); -- cgit v1.2.3 From 84c029a73327cef571eaa61c7d6e67e8031b52ec Mon Sep 17 00:00:00 2001 From: Zhouyang Jia Date: Fri, 15 Jun 2018 07:34:52 +0800 Subject: xen: add error handling for xenbus_printf When xenbus_printf fails, the lack of error-handling code may cause unexpected results. This patch adds error-handling code after calling xenbus_printf. Signed-off-by: Zhouyang Jia Reviewed-by: Boris Ostrovsky Signed-off-by: Juergen Gross --- drivers/xen/manage.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index 8835065029d3..c93d8ef8df34 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -289,8 +289,15 @@ static void sysrq_handler(struct xenbus_watch *watch, const char *path, return; } - if (sysrq_key != '\0') - xenbus_printf(xbt, "control", "sysrq", "%c", '\0'); + if (sysrq_key != '\0') { + err = xenbus_printf(xbt, "control", "sysrq", "%c", '\0'); + if (err) { + pr_err("%s: Error %d writing sysrq in control/sysrq\n", + __func__, err); + xenbus_transaction_end(xbt, 1); + return; + } + } err = xenbus_transaction_end(xbt, 0); if (err == -EAGAIN) @@ -342,7 +349,12 @@ static int setup_shutdown_watcher(void) continue; snprintf(node, FEATURE_PATH_SIZE, "feature-%s", shutdown_handlers[idx].command); - xenbus_printf(XBT_NIL, "control", node, "%u", 1); + err = xenbus_printf(XBT_NIL, "control", node, "%u", 1); + if (err) { + pr_err("%s: Error %d writing %s\n", __func__, + err, node); + return err; + } } return 0; -- cgit v1.2.3 From 6e3cc2a6e259f49f5817d1561109832eff90f8e4 Mon Sep 17 00:00:00 2001 From: Oleksandr Andrushchenko Date: Fri, 1 Jun 2018 14:41:24 +0300 Subject: xen/grant-table: Export gnttab_{alloc|free}_pages as GPL Only gnttab_{alloc|free}_pages are exported as EXPORT_SYMBOL while all the rest are exported as EXPORT_SYMBOL_GPL, thus effectively making it not possible for non-GPL driver modules to use grant table module. Export gnttab_{alloc|free}_pages as EXPORT_SYMBOL_GPL so all the exports are aligned. Signed-off-by: Oleksandr Andrushchenko Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross --- drivers/xen/grant-table.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 27be107d6480..ba36ff3e4903 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -799,7 +799,7 @@ int gnttab_alloc_pages(int nr_pages, struct page **pages) return 0; } -EXPORT_SYMBOL(gnttab_alloc_pages); +EXPORT_SYMBOL_GPL(gnttab_alloc_pages); /** * gnttab_free_pages - free pages allocated by gnttab_alloc_pages() @@ -820,7 +820,7 @@ void gnttab_free_pages(int nr_pages, struct page **pages) } free_xenballooned_pages(nr_pages, pages); } -EXPORT_SYMBOL(gnttab_free_pages); +EXPORT_SYMBOL_GPL(gnttab_free_pages); /* Handling of paged out grant targets (GNTST_eagain) */ #define MAX_DELAY 256 -- cgit v1.2.3 From 7ba33e1c9d1e03f442b161c701d1f811ea13c75e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 10 Jun 2018 20:43:09 +0100 Subject: drm/i915: Apply batch location restrictions before pinning We special case the position of the batch within the GTT to prevent negative self-relocation deltas from underflowing. However, that restriction is being applied after a trial pin of the batch in its current position. Thus we are not rejecting an invalid location if the batch has been used before, leading to an assertion if we happen to need to rearrange the entire payload. In the worst case, this may cause a GPU hang on gen7 or perhaps missing state. References: https://bugs.freedesktop.org/show_bug.cgi?id=105720 Fixes: 2889caa92321 ("drm/i915: Eliminate lots of iterations over the execobjects array") Signed-off-by: Chris Wilson Cc: Joonas Lahtinen Cc: Martin Peres Link: https://patchwork.freedesktop.org/patch/msgid/20180610194325.13467-2-chris@chris-wilson.co.uk Reviewed-by: Joonas Lahtinen (cherry picked from commit 746c8f143afad7aaa66c484485fc39888d437a3f) Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 49 ++++++++++++++++-------------- 1 file changed, 27 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index f627a8c47c58..22df17c8ca9b 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -489,7 +489,9 @@ eb_validate_vma(struct i915_execbuffer *eb, } static int -eb_add_vma(struct i915_execbuffer *eb, unsigned int i, struct i915_vma *vma) +eb_add_vma(struct i915_execbuffer *eb, + unsigned int i, unsigned batch_idx, + struct i915_vma *vma) { struct drm_i915_gem_exec_object2 *entry = &eb->exec[i]; int err; @@ -522,6 +524,24 @@ eb_add_vma(struct i915_execbuffer *eb, unsigned int i, struct i915_vma *vma) eb->flags[i] = entry->flags; vma->exec_flags = &eb->flags[i]; + /* + * SNA is doing fancy tricks with compressing batch buffers, which leads + * to negative relocation deltas. Usually that works out ok since the + * relocate address is still positive, except when the batch is placed + * very low in the GTT. Ensure this doesn't happen. + * + * Note that actual hangs have only been observed on gen7, but for + * paranoia do it everywhere. + */ + if (i == batch_idx) { + if (!(eb->flags[i] & EXEC_OBJECT_PINNED)) + eb->flags[i] |= __EXEC_OBJECT_NEEDS_BIAS; + if (eb->reloc_cache.has_fence) + eb->flags[i] |= EXEC_OBJECT_NEEDS_FENCE; + + eb->batch = vma; + } + err = 0; if (eb_pin_vma(eb, entry, vma)) { if (entry->offset != vma->node.start) { @@ -716,7 +736,7 @@ static int eb_lookup_vmas(struct i915_execbuffer *eb) { struct radix_tree_root *handles_vma = &eb->ctx->handles_vma; struct drm_i915_gem_object *obj; - unsigned int i; + unsigned int i, batch; int err; if (unlikely(i915_gem_context_is_closed(eb->ctx))) @@ -728,6 +748,8 @@ static int eb_lookup_vmas(struct i915_execbuffer *eb) INIT_LIST_HEAD(&eb->relocs); INIT_LIST_HEAD(&eb->unbound); + batch = eb_batch_index(eb); + for (i = 0; i < eb->buffer_count; i++) { u32 handle = eb->exec[i].handle; struct i915_lut_handle *lut; @@ -770,33 +792,16 @@ static int eb_lookup_vmas(struct i915_execbuffer *eb) lut->handle = handle; add_vma: - err = eb_add_vma(eb, i, vma); + err = eb_add_vma(eb, i, batch, vma); if (unlikely(err)) goto err_vma; GEM_BUG_ON(vma != eb->vma[i]); GEM_BUG_ON(vma->exec_flags != &eb->flags[i]); + GEM_BUG_ON(drm_mm_node_allocated(&vma->node) && + eb_vma_misplaced(&eb->exec[i], vma, eb->flags[i])); } - /* take note of the batch buffer before we might reorder the lists */ - i = eb_batch_index(eb); - eb->batch = eb->vma[i]; - GEM_BUG_ON(eb->batch->exec_flags != &eb->flags[i]); - - /* - * SNA is doing fancy tricks with compressing batch buffers, which leads - * to negative relocation deltas. Usually that works out ok since the - * relocate address is still positive, except when the batch is placed - * very low in the GTT. Ensure this doesn't happen. - * - * Note that actual hangs have only been observed on gen7, but for - * paranoia do it everywhere. - */ - if (!(eb->flags[i] & EXEC_OBJECT_PINNED)) - eb->flags[i] |= __EXEC_OBJECT_NEEDS_BIAS; - if (eb->reloc_cache.has_fence) - eb->flags[i] |= EXEC_OBJECT_NEEDS_FENCE; - eb->args->flags |= __EXEC_VALIDATED; return eb_reserve(eb); -- cgit v1.2.3 From a5bfcdf0e16b33c1690ded31f863466136480ddc Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 11 Jun 2018 16:33:32 +0100 Subject: drm/i915/execlists: Avoid putting the error pointer On allocation error, do not jump to the unwind handler that tries to free the error pointer. Reported-by: Lionel Landwerlin Fixes: a89d1f921c15 ("drm/i915: Split i915_gem_timeline into individual timelines") Signed-off-by: Chris Wilson Cc: Lionel Landwerlin Cc: Tvrtko Ursulin Reviewed-by: Lionel Landwerlin Link: https://patchwork.freedesktop.org/patch/msgid/20180611153332.14824-1-chris@chris-wilson.co.uk (cherry picked from commit 467d35789e5a4f47428b65ef711b30fdabbb0fd4) Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/intel_lrc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 15434cad5430..f3968580e5e2 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -2641,10 +2641,8 @@ static int execlists_context_deferred_alloc(struct i915_gem_context *ctx, context_size += LRC_HEADER_PAGES * PAGE_SIZE; ctx_obj = i915_gem_object_create(ctx->i915, context_size); - if (IS_ERR(ctx_obj)) { - ret = PTR_ERR(ctx_obj); - goto error_deref_obj; - } + if (IS_ERR(ctx_obj)) + return PTR_ERR(ctx_obj); vma = i915_vma_instance(ctx_obj, &ctx->i915->ggtt.base, NULL); if (IS_ERR(vma)) { -- cgit v1.2.3 From 541ab84d2b6ea79021d5df0b54d81600334fa2a4 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Thu, 24 May 2018 15:54:03 +0300 Subject: drm/i915: Allow DBLSCAN user modes with eDP/LVDS/DSI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When encountering a connector with the scaling mode property both intel and modesetting ddxs sometimes add tons of DBLSCAN modes to the output's mode list. The idea presumably being that since the output will be going through the panel fitter anyway we can pretend to use any kind of mode. Sadly that means we can't reject user modes with the DBLSCAN flag until we know whether we're going to be using the panel's native mode or the user mode directly. Doing otherwise means X clients using xf86vidmode/xrandr will get a protocol error (and often self terminate as a result) when the kernel refuses to use the requested mode with the DBLSCAN flag. To undo the regression we'll move the DBLSCAN checks into the connector->mode_valid() and encoder->compute_config() hooks. Cc: stable@vger.kernel.org Cc: Vito Caputo Reported-by: Vito Caputo Fixes: e995ca0b8139 ("drm/i915: Provide a device level .mode_valid() hook") References: https://lkml.org/lkml/2018/5/21/715 Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180524125403.23445-1-ville.syrjala@linux.intel.com Reviewed-by: Maarten Lankhorst Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=106804 Tested-by: Arkadiusz Miskiewicz (cherry picked from commit e4dd27aadd205417a2e9ea9902b698a0252ec3a0) Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/intel_crt.c | 20 ++++++++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 16 +++++++++++++--- drivers/gpu/drm/i915/intel_dp.c | 6 ++++++ drivers/gpu/drm/i915/intel_dp_mst.c | 6 ++++++ drivers/gpu/drm/i915/intel_dsi.c | 6 ++++++ drivers/gpu/drm/i915/intel_dvo.c | 6 ++++++ drivers/gpu/drm/i915/intel_hdmi.c | 6 ++++++ drivers/gpu/drm/i915/intel_lvds.c | 5 +++++ drivers/gpu/drm/i915/intel_sdvo.c | 6 ++++++ drivers/gpu/drm/i915/intel_tv.c | 12 ++++++++++-- 10 files changed, 84 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index de0e22322c76..072b326d5ee0 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -304,6 +304,9 @@ intel_crt_mode_valid(struct drm_connector *connector, int max_dotclk = dev_priv->max_dotclk_freq; int max_clock; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + if (mode->clock < 25000) return MODE_CLOCK_LOW; @@ -337,6 +340,12 @@ static bool intel_crt_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { + struct drm_display_mode *adjusted_mode = + &pipe_config->base.adjusted_mode; + + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) + return false; + return true; } @@ -344,6 +353,12 @@ static bool pch_crt_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { + struct drm_display_mode *adjusted_mode = + &pipe_config->base.adjusted_mode; + + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) + return false; + pipe_config->has_pch_encoder = true; return true; @@ -354,6 +369,11 @@ static bool hsw_crt_compute_config(struct intel_encoder *encoder, struct drm_connector_state *conn_state) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct drm_display_mode *adjusted_mode = + &pipe_config->base.adjusted_mode; + + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) + return false; pipe_config->has_pch_encoder = true; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index dee3a8e659f1..2cc6faa1daa8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -14469,12 +14469,22 @@ static enum drm_mode_status intel_mode_valid(struct drm_device *dev, const struct drm_display_mode *mode) { + /* + * Can't reject DBLSCAN here because Xorg ddxen can add piles + * of DBLSCAN modes to the output's mode list when they detect + * the scaling mode property on the connector. And they don't + * ask the kernel to validate those modes in any way until + * modeset time at which point the client gets a protocol error. + * So in order to not upset those clients we silently ignore the + * DBLSCAN flag on such connectors. For other connectors we will + * reject modes with the DBLSCAN flag in encoder->compute_config(). + * And we always reject DBLSCAN modes in connector->mode_valid() + * as we never want such modes on the connector's mode list. + */ + if (mode->vscan > 1) return MODE_NO_VSCAN; - if (mode->flags & DRM_MODE_FLAG_DBLSCAN) - return MODE_NO_DBLESCAN; - if (mode->flags & DRM_MODE_FLAG_HSKEW) return MODE_H_ILLEGAL; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 8320f0e8e3be..6cce43a6eaad 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -420,6 +420,9 @@ intel_dp_mode_valid(struct drm_connector *connector, int max_rate, mode_rate, max_lanes, max_link_clock; int max_dotclk; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + max_dotclk = intel_dp_downstream_max_dotclock(intel_dp); if (intel_dp_is_edp(intel_dp) && fixed_mode) { @@ -1862,6 +1865,9 @@ intel_dp_compute_config(struct intel_encoder *encoder, conn_state->scaling_mode); } + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) + return false; + if ((IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) && adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) return false; diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c index 9e6956c08688..5890500a3a8b 100644 --- a/drivers/gpu/drm/i915/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/intel_dp_mst.c @@ -48,6 +48,9 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, bool reduce_m_n = drm_dp_has_quirk(&intel_dp->desc, DP_DPCD_QUIRK_LIMITED_M_N); + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) + return false; + pipe_config->has_pch_encoder = false; bpp = 24; if (intel_dp->compliance.test_data.bpc) { @@ -366,6 +369,9 @@ intel_dp_mst_mode_valid(struct drm_connector *connector, if (!intel_dp) return MODE_ERROR; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + max_link_clock = intel_dp_max_link_rate(intel_dp); max_lanes = intel_dp_max_lane_count(intel_dp); diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index cf39ca90d887..f349b3920199 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -326,6 +326,9 @@ static bool intel_dsi_compute_config(struct intel_encoder *encoder, conn_state->scaling_mode); } + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) + return false; + /* DSI uses short packets for sync events, so clear mode flags for DSI */ adjusted_mode->flags = 0; @@ -1266,6 +1269,9 @@ intel_dsi_mode_valid(struct drm_connector *connector, DRM_DEBUG_KMS("\n"); + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + if (fixed_mode) { if (mode->hdisplay > fixed_mode->hdisplay) return MODE_PANEL; diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index a70d767313aa..61d908e0df0e 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -219,6 +219,9 @@ intel_dvo_mode_valid(struct drm_connector *connector, int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; int target_clock = mode->clock; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + /* XXX: Validate clock range */ if (fixed_mode) { @@ -254,6 +257,9 @@ static bool intel_dvo_compute_config(struct intel_encoder *encoder, if (fixed_mode) intel_fixed_panel_mode(fixed_mode, adjusted_mode); + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) + return false; + return true; } diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index ee929f31f7db..d8cb53ef4351 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1557,6 +1557,9 @@ intel_hdmi_mode_valid(struct drm_connector *connector, bool force_dvi = READ_ONCE(to_intel_digital_connector_state(connector->state)->force_audio) == HDMI_AUDIO_OFF_DVI; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + clock = mode->clock; if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) @@ -1677,6 +1680,9 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, int desired_bpp; bool force_dvi = intel_conn_state->force_audio == HDMI_AUDIO_OFF_DVI; + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) + return false; + pipe_config->has_hdmi_sink = !force_dvi && intel_hdmi->has_hdmi_sink; if (pipe_config->has_hdmi_sink) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index d278f24ba6ae..48f618dc9abb 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -380,6 +380,8 @@ intel_lvds_mode_valid(struct drm_connector *connector, struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; int max_pixclk = to_i915(connector->dev)->max_dotclk_freq; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; if (mode->hdisplay > fixed_mode->hdisplay) return MODE_PANEL; if (mode->vdisplay > fixed_mode->vdisplay) @@ -429,6 +431,9 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, intel_fixed_panel_mode(intel_connector->panel.fixed_mode, adjusted_mode); + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) + return false; + if (HAS_PCH_SPLIT(dev_priv)) { pipe_config->has_pch_encoder = true; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 25005023c243..26975df4e593 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1160,6 +1160,9 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder, adjusted_mode); } + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) + return false; + /* * Make the CRTC code factor in the SDVO pixel multiplier. The * SDVO device will factor out the multiplier during mode_set. @@ -1621,6 +1624,9 @@ intel_sdvo_mode_valid(struct drm_connector *connector, struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector); int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + if (intel_sdvo->pixel_clock_min > mode->clock) return MODE_CLOCK_LOW; diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 885fc3809f7f..b55b5c157e38 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -850,6 +850,9 @@ intel_tv_mode_valid(struct drm_connector *connector, const struct tv_mode *tv_mode = intel_tv_mode_find(connector->state); int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + if (mode->clock > max_dotclk) return MODE_CLOCK_HIGH; @@ -877,16 +880,21 @@ intel_tv_compute_config(struct intel_encoder *encoder, struct drm_connector_state *conn_state) { const struct tv_mode *tv_mode = intel_tv_mode_find(conn_state); + struct drm_display_mode *adjusted_mode = + &pipe_config->base.adjusted_mode; if (!tv_mode) return false; - pipe_config->base.adjusted_mode.crtc_clock = tv_mode->clock; + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) + return false; + + adjusted_mode->crtc_clock = tv_mode->clock; DRM_DEBUG_KMS("forcing bpc to 8 for TV\n"); pipe_config->pipe_bpp = 8*3; /* TV has it's own notion of sync and other mode flags, so clear them. */ - pipe_config->base.adjusted_mode.flags = 0; + adjusted_mode->flags = 0; /* * FIXME: We don't check whether the input mode is actually what we want -- cgit v1.2.3 From 4dc055c9cc8b3dac966b54d3cd5cf463a988299b Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Mon, 11 Jun 2018 23:02:55 +0300 Subject: drm/i915: Fix PIPESTAT irq ack on i965/g4x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On i965/g4x IIR is edge triggered. So in order for IIR to notice that there is still a pending interrupt we have to force and edge in ISR. For the ISR/IIR pipe event bits we can do that by temporarily clearing all the PIPESTAT enable bits when we ack the status bits. This will force the ISR pipe event bit low, and it can then go back high when we restore the PIPESTAT enable bits. This avoids the following race: 1. stat = read(PIPESTAT) 2. an enabled PIPESTAT status bit goes high 3. write(PIPESTAT, enable|stat); 4. write(IIR, PIPE_EVENT) The end result is IIR==0 and ISR!=0. This can lead to nasty vblank wait/flip_done timeouts if another interrupt source doesn't trick us into looking at the PIPESTAT status bits despite the IIR PIPE_EVENT bit being low. Before i965 IIR was level triggered so this problem can't actually happen there. And curiously VLV/CHV went back to the level triggered scheme as well. But for simplicity we'll use the same i965/g4x compatible code for all platforms. Cc: stable@vger.kernel.org Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=106033 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=105225 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=106030 Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180611200258.27121-1-ville.syrjala@linux.intel.com Reviewed-by: Chris Wilson (cherry picked from commit 132c27c97cb958f637dc05adc35a61b47779bcd8) Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/i915_irq.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index f9bc3aaa90d0..4a02747ac658 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1893,9 +1893,17 @@ static void i9xx_pipestat_irq_ack(struct drm_i915_private *dev_priv, /* * Clear the PIPE*STAT regs before the IIR + * + * Toggle the enable bits to make sure we get an + * edge in the ISR pipe event bit if we don't clear + * all the enabled status bits. Otherwise the edge + * triggered IIR on i965/g4x wouldn't notice that + * an interrupt is still pending. */ - if (pipe_stats[pipe]) - I915_WRITE(reg, enable_mask | pipe_stats[pipe]); + if (pipe_stats[pipe]) { + I915_WRITE(reg, pipe_stats[pipe]); + I915_WRITE(reg, enable_mask); + } } spin_unlock(&dev_priv->irq_lock); } -- cgit v1.2.3 From 1e34f1d36804be1a446212a33ca5397bf0e5acdd Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 13 Jun 2018 19:05:52 +0300 Subject: drm/i915: Disallow interlaced modes on g4x DP outputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Looks like interlaced DP output doesn't work on g4x either. Not all that surprising considering we already established that interlaced DP output is busted on VLV/CHV. Cc: stable@vger.kernel.org Signed-off-by: Ville Syrjälä Reviewed-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20180613160553.11664-1-ville.syrjala@linux.intel.com (cherry picked from commit 929168c5f3df5d9ea0ef426c33e971157d045eab) Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/intel_dp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 6cce43a6eaad..2bdfe4b2d6dc 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1868,7 +1868,7 @@ intel_dp_compute_config(struct intel_encoder *encoder, if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) return false; - if ((IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) && + if (HAS_GMCH_DISPLAY(dev_priv) && adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) return false; @@ -6343,7 +6343,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, drm_connector_init(dev, connector, &intel_dp_connector_funcs, type); drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs); - if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv)) + if (!HAS_GMCH_DISPLAY(dev_priv)) connector->interlace_allowed = true; connector->doublescan_allowed = 0; -- cgit v1.2.3 From 4dccc4d517481282e84335c7acbfd7a1481004b8 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 13 Jun 2018 19:05:53 +0300 Subject: drm/i915: Turn off g4x DP port in .post_disable() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While Bspec doesn't list a specific sequence for turning off the DP port on g4x we are getting an underrun if the port is disabled in the .disable() hook. Looks like the pipe stops when the port stops, and by that time the plane disable may not have completed yet. Also the plane(s) seem to end up in some wonky state when this happens as they also signal another underrun immediately after we turn them back on during the next enable sequence. We could add a vblank wait in .disable() to avoid wedging the planes, but I assume we're still tripping up the pipe in some way. So it seems better to me to just follow the ILK+ sequence and turn off the DP port in .post_disable() instead. This sequence doesn't seem to suffer from this problem. Could be it was always the intended sequence for DP and the gen4 bspec was just never updated to include it. Originally we used the bad sequence even on ilk+, but I changed that in commit 08aff3fe26ae ("drm/i915: Move DP port disable to post_disable for pch platforms") as it was causing issues on those platforms as well. I left out g4x then only because I didn't have the hardware to test it. Now that I do it's fairly clear that the ilk+ sequence is also the right choice for g4x. v2: Fix whitespace fail (Jani) Mention the ilk+ commit (Jani) Cc: stable@vger.kernel.org Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180613160553.11664-2-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula (cherry picked from commit 51a9f6dfc00d35f927ecfaf6f0ae8ebaba39b3fe) Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/intel_dp.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 2bdfe4b2d6dc..16faea30114a 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2788,16 +2788,6 @@ static void intel_disable_dp(struct intel_encoder *encoder, static void g4x_disable_dp(struct intel_encoder *encoder, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *old_conn_state) -{ - intel_disable_dp(encoder, old_crtc_state, old_conn_state); - - /* disable the port before the pipe on g4x */ - intel_dp_link_down(encoder, old_crtc_state); -} - -static void ilk_disable_dp(struct intel_encoder *encoder, - const struct intel_crtc_state *old_crtc_state, - const struct drm_connector_state *old_conn_state) { intel_disable_dp(encoder, old_crtc_state, old_conn_state); } @@ -2813,13 +2803,19 @@ static void vlv_disable_dp(struct intel_encoder *encoder, intel_disable_dp(encoder, old_crtc_state, old_conn_state); } -static void ilk_post_disable_dp(struct intel_encoder *encoder, +static void g4x_post_disable_dp(struct intel_encoder *encoder, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *old_conn_state) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); enum port port = encoder->port; + /* + * Bspec does not list a specific disable sequence for g4x DP. + * Follow the ilk+ sequence (disable pipe before the port) for + * g4x DP as it does not suffer from underruns like the normal + * g4x modeset sequence (disable pipe after the port). + */ intel_dp_link_down(encoder, old_crtc_state); /* Only ilk+ has port A */ @@ -6442,15 +6438,11 @@ bool intel_dp_init(struct drm_i915_private *dev_priv, intel_encoder->enable = vlv_enable_dp; intel_encoder->disable = vlv_disable_dp; intel_encoder->post_disable = vlv_post_disable_dp; - } else if (INTEL_GEN(dev_priv) >= 5) { - intel_encoder->pre_enable = g4x_pre_enable_dp; - intel_encoder->enable = g4x_enable_dp; - intel_encoder->disable = ilk_disable_dp; - intel_encoder->post_disable = ilk_post_disable_dp; } else { intel_encoder->pre_enable = g4x_pre_enable_dp; intel_encoder->enable = g4x_enable_dp; intel_encoder->disable = g4x_disable_dp; + intel_encoder->post_disable = g4x_post_disable_dp; } intel_dig_port->dp.output_reg = output_reg; -- cgit v1.2.3 From bc64e05408cafe3668e7460834935ea3f1764f31 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Fri, 15 Jun 2018 13:44:29 +0300 Subject: drm/i915: Fix context ban and hang accounting for client If client is smart or lucky enough to create a new context after each hang, our context banning mechanism will never catch up, and as a result of that it will be saved from client banning. This can result in a never ending streak of gpu hangs caused by bad or malicious client, preventing access from other legit gpu clients. Fix this by always incrementing per client ban score if it hangs in short successions regardless of context ban scoring. The exception are non bannable contexts. They remain detached from client ban scoring mechanism. v2: xchg timestamp, tidyup (Chris) v3: comment, bannable & banned together (Chris) Fixes: b083a0870c79 ("drm/i915: Add per client max context ban limit") Cc: Chris Wilson Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Link: https://patchwork.freedesktop.org/patch/msgid/20180615104429.31477-1-mika.kuoppala@linux.intel.com (cherry picked from commit 14921f3cef85b0167a9145e5f29b9dfc3b2a84dc) Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/i915_drv.h | 21 ++++++++---- drivers/gpu/drm/i915/i915_gem.c | 57 +++++++++++++++++++++++---------- drivers/gpu/drm/i915/i915_gem_context.c | 2 +- 3 files changed, 55 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 34c125e2d90c..7014a96546f4 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -340,14 +340,21 @@ struct drm_i915_file_private { unsigned int bsd_engine; -/* Client can have a maximum of 3 contexts banned before - * it is denied of creating new contexts. As one context - * ban needs 4 consecutive hangs, and more if there is - * progress in between, this is a last resort stop gap measure - * to limit the badly behaving clients access to gpu. +/* + * Every context ban increments per client ban score. Also + * hangs in short succession increments ban score. If ban threshold + * is reached, client is considered banned and submitting more work + * will fail. This is a stop gap measure to limit the badly behaving + * clients access to gpu. Note that unbannable contexts never increment + * the client ban score. */ -#define I915_MAX_CLIENT_CONTEXT_BANS 3 - atomic_t context_bans; +#define I915_CLIENT_SCORE_HANG_FAST 1 +#define I915_CLIENT_FAST_HANG_JIFFIES (60 * HZ) +#define I915_CLIENT_SCORE_CONTEXT_BAN 3 +#define I915_CLIENT_SCORE_BANNED 9 + /** ban_score: Accumulated score of all ctx bans and fast hangs. */ + atomic_t ban_score; + unsigned long hang_timestamp; }; /* Interface history: diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 3704f4c0c2c9..d44ad7bc1e94 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2933,32 +2933,54 @@ i915_gem_object_pwrite_gtt(struct drm_i915_gem_object *obj, return 0; } +static void i915_gem_client_mark_guilty(struct drm_i915_file_private *file_priv, + const struct i915_gem_context *ctx) +{ + unsigned int score; + unsigned long prev_hang; + + if (i915_gem_context_is_banned(ctx)) + score = I915_CLIENT_SCORE_CONTEXT_BAN; + else + score = 0; + + prev_hang = xchg(&file_priv->hang_timestamp, jiffies); + if (time_before(jiffies, prev_hang + I915_CLIENT_FAST_HANG_JIFFIES)) + score += I915_CLIENT_SCORE_HANG_FAST; + + if (score) { + atomic_add(score, &file_priv->ban_score); + + DRM_DEBUG_DRIVER("client %s: gained %u ban score, now %u\n", + ctx->name, score, + atomic_read(&file_priv->ban_score)); + } +} + static void i915_gem_context_mark_guilty(struct i915_gem_context *ctx) { - bool banned; + unsigned int score; + bool banned, bannable; atomic_inc(&ctx->guilty_count); - banned = false; - if (i915_gem_context_is_bannable(ctx)) { - unsigned int score; + bannable = i915_gem_context_is_bannable(ctx); + score = atomic_add_return(CONTEXT_SCORE_GUILTY, &ctx->ban_score); + banned = score >= CONTEXT_SCORE_BAN_THRESHOLD; - score = atomic_add_return(CONTEXT_SCORE_GUILTY, - &ctx->ban_score); - banned = score >= CONTEXT_SCORE_BAN_THRESHOLD; + DRM_DEBUG_DRIVER("context %s: guilty %d, score %u, ban %s\n", + ctx->name, atomic_read(&ctx->guilty_count), + score, yesno(banned && bannable)); - DRM_DEBUG_DRIVER("context %s marked guilty (score %d) banned? %s\n", - ctx->name, score, yesno(banned)); - } - if (!banned) + /* Cool contexts don't accumulate client ban score */ + if (!bannable) return; - i915_gem_context_set_banned(ctx); - if (!IS_ERR_OR_NULL(ctx->file_priv)) { - atomic_inc(&ctx->file_priv->context_bans); - DRM_DEBUG_DRIVER("client %s has had %d context banned\n", - ctx->name, atomic_read(&ctx->file_priv->context_bans)); - } + if (banned) + i915_gem_context_set_banned(ctx); + + if (!IS_ERR_OR_NULL(ctx->file_priv)) + i915_gem_client_mark_guilty(ctx->file_priv, ctx); } static void i915_gem_context_mark_innocent(struct i915_gem_context *ctx) @@ -5736,6 +5758,7 @@ int i915_gem_open(struct drm_i915_private *i915, struct drm_file *file) INIT_LIST_HEAD(&file_priv->mm.request_list); file_priv->bsd_engine = -1; + file_priv->hang_timestamp = jiffies; ret = i915_gem_context_open(i915, file); if (ret) diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 33f8a4b3c981..060335d3d9e0 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -652,7 +652,7 @@ int i915_gem_switch_to_kernel_context(struct drm_i915_private *dev_priv) static bool client_is_banned(struct drm_i915_file_private *file_priv) { - return atomic_read(&file_priv->context_bans) > I915_MAX_CLIENT_CONTEXT_BANS; + return atomic_read(&file_priv->ban_score) >= I915_CLIENT_SCORE_BANNED; } int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, -- cgit v1.2.3 From 7a3727f385dc64773db1c144f6b15c1e9d4735bb Mon Sep 17 00:00:00 2001 From: Kenneth Graunke Date: Fri, 15 Jun 2018 20:06:05 +0100 Subject: drm/i915: Enable provoking vertex fix on Gen9 systems. The SF and clipper units mishandle the provoking vertex in some cases, which can cause misrendering with shaders that use flat shaded inputs. There are chicken bits in 3D_CHICKEN3 (for SF) and FF_SLICE_CHICKEN (for the clipper) that work around the issue. These registers are unfortunately not part of the logical context (even the power context), and so we must reload them every time we start executing in a context. Bugzilla: https://bugs.freedesktop.org/103047 Signed-off-by: Kenneth Graunke Signed-off-by: Chris Wilson Link: https://patchwork.freedesktop.org/patch/msgid/20180615190605.16238-1-chris@chris-wilson.co.uk Reviewed-by: Joonas Lahtinen Cc: stable@vger.kernel.org (cherry picked from commit b77422f80337d363eed60c8c48db9cb6e33085c9) Signed-off-by: Jani Nikula --- drivers/gpu/drm/i915/i915_reg.h | 5 +++++ drivers/gpu/drm/i915/intel_lrc.c | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index f11bb213ec07..7720569f2024 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2425,12 +2425,17 @@ enum i915_power_well_id { #define _3D_CHICKEN _MMIO(0x2084) #define _3D_CHICKEN_HIZ_PLANE_DISABLE_MSAA_4X_SNB (1 << 10) #define _3D_CHICKEN2 _MMIO(0x208c) + +#define FF_SLICE_CHICKEN _MMIO(0x2088) +#define FF_SLICE_CHICKEN_CL_PROVOKING_VERTEX_FIX (1 << 1) + /* Disables pipelining of read flushes past the SF-WIZ interface. * Required on all Ironlake steppings according to the B-Spec, but the * particular danger of not doing so is not specified. */ # define _3D_CHICKEN2_WM_READ_PIPELINED (1 << 14) #define _3D_CHICKEN3 _MMIO(0x2090) +#define _3D_CHICKEN_SF_PROVOKING_VERTEX_FIX (1 << 12) #define _3D_CHICKEN_SF_DISABLE_OBJEND_CULL (1 << 10) #define _3D_CHICKEN3_AA_LINE_QUALITY_FIX_ENABLE (1 << 5) #define _3D_CHICKEN3_SF_DISABLE_FASTCLIP_CULL (1 << 5) diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index f3968580e5e2..7c4c8fb1dae4 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -1545,11 +1545,21 @@ static u32 *gen9_init_indirectctx_bb(struct intel_engine_cs *engine, u32 *batch) /* WaFlushCoherentL3CacheLinesAtContextSwitch:skl,bxt,glk */ batch = gen8_emit_flush_coherentl3_wa(engine, batch); + *batch++ = MI_LOAD_REGISTER_IMM(3); + /* WaDisableGatherAtSetShaderCommonSlice:skl,bxt,kbl,glk */ - *batch++ = MI_LOAD_REGISTER_IMM(1); *batch++ = i915_mmio_reg_offset(COMMON_SLICE_CHICKEN2); *batch++ = _MASKED_BIT_DISABLE( GEN9_DISABLE_GATHER_AT_SET_SHADER_COMMON_SLICE); + + /* BSpec: 11391 */ + *batch++ = i915_mmio_reg_offset(FF_SLICE_CHICKEN); + *batch++ = _MASKED_BIT_ENABLE(FF_SLICE_CHICKEN_CL_PROVOKING_VERTEX_FIX); + + /* BSpec: 11299 */ + *batch++ = i915_mmio_reg_offset(_3D_CHICKEN3); + *batch++ = _MASKED_BIT_ENABLE(_3D_CHICKEN_SF_PROVOKING_VERTEX_FIX); + *batch++ = MI_NOOP; /* WaClearSlmSpaceAtContextSwitch:kbl */ -- cgit v1.2.3 From 93efbd39870474cc536b9caf4a6efeb03b0bc56f Mon Sep 17 00:00:00 2001 From: Zhouyang Jia Date: Sat, 16 Jun 2018 01:05:01 +0800 Subject: scsi: xen-scsifront: add error handling for xenbus_printf When xenbus_printf fails, the lack of error-handling code may cause unexpected results. This patch adds error-handling code after calling xenbus_printf. Signed-off-by: Zhouyang Jia Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross --- drivers/scsi/xen-scsifront.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/xen-scsifront.c b/drivers/scsi/xen-scsifront.c index 36f59a1be7e9..61389bdc7926 100644 --- a/drivers/scsi/xen-scsifront.c +++ b/drivers/scsi/xen-scsifront.c @@ -654,10 +654,17 @@ static int scsifront_dev_reset_handler(struct scsi_cmnd *sc) static int scsifront_sdev_configure(struct scsi_device *sdev) { struct vscsifrnt_info *info = shost_priv(sdev->host); + int err; - if (info && current == info->curr) - xenbus_printf(XBT_NIL, info->dev->nodename, + if (info && current == info->curr) { + err = xenbus_printf(XBT_NIL, info->dev->nodename, info->dev_state_path, "%d", XenbusStateConnected); + if (err) { + xenbus_dev_error(info->dev, err, + "%s: writing dev_state_path", __func__); + return err; + } + } return 0; } @@ -665,10 +672,15 @@ static int scsifront_sdev_configure(struct scsi_device *sdev) static void scsifront_sdev_destroy(struct scsi_device *sdev) { struct vscsifrnt_info *info = shost_priv(sdev->host); + int err; - if (info && current == info->curr) - xenbus_printf(XBT_NIL, info->dev->nodename, + if (info && current == info->curr) { + err = xenbus_printf(XBT_NIL, info->dev->nodename, info->dev_state_path, "%d", XenbusStateClosed); + if (err) + xenbus_dev_error(info->dev, err, + "%s: writing dev_state_path", __func__); + } } static struct scsi_host_template scsifront_sht = { @@ -1003,9 +1015,12 @@ static void scsifront_do_lun_hotplug(struct vscsifrnt_info *info, int op) if (scsi_add_device(info->host, chn, tgt, lun)) { dev_err(&dev->dev, "scsi_add_device\n"); - xenbus_printf(XBT_NIL, dev->nodename, + err = xenbus_printf(XBT_NIL, dev->nodename, info->dev_state_path, "%d", XenbusStateClosed); + if (err) + xenbus_dev_error(dev, err, + "%s: writing dev_state_path", __func__); } break; case VSCSIFRONT_OP_DEL_LUN: @@ -1019,10 +1034,14 @@ static void scsifront_do_lun_hotplug(struct vscsifrnt_info *info, int op) } break; case VSCSIFRONT_OP_READD_LUN: - if (device_state == XenbusStateConnected) - xenbus_printf(XBT_NIL, dev->nodename, + if (device_state == XenbusStateConnected) { + err = xenbus_printf(XBT_NIL, dev->nodename, info->dev_state_path, "%d", XenbusStateConnected); + if (err) + xenbus_dev_error(dev, err, + "%s: writing dev_state_path", __func__); + } break; default: break; -- cgit v1.2.3 From 7c63ca24c878e0051c91904b72174029320ef4bd Mon Sep 17 00:00:00 2001 From: Zhouyang Jia Date: Sat, 16 Jun 2018 08:14:37 +0800 Subject: xen/scsiback: add error handling for xenbus_printf When xenbus_printf fails, the lack of error-handling code may cause unexpected results. This patch adds error-handling code after calling xenbus_printf. Signed-off-by: Zhouyang Jia Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross --- drivers/xen/xen-scsiback.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index 7bc88fd43cfc..e2f3e8b0fba9 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -1012,6 +1012,7 @@ static void scsiback_do_add_lun(struct vscsibk_info *info, const char *state, { struct v2p_entry *entry; unsigned long flags; + int err; if (try) { spin_lock_irqsave(&info->v2p_lock, flags); @@ -1027,8 +1028,11 @@ static void scsiback_do_add_lun(struct vscsibk_info *info, const char *state, scsiback_del_translation_entry(info, vir); } } else if (!try) { - xenbus_printf(XBT_NIL, info->dev->nodename, state, + err = xenbus_printf(XBT_NIL, info->dev->nodename, state, "%d", XenbusStateClosed); + if (err) + xenbus_dev_error(info->dev, err, + "%s: writing %s", __func__, state); } } @@ -1067,8 +1071,11 @@ static void scsiback_do_1lun_hotplug(struct vscsibk_info *info, int op, snprintf(str, sizeof(str), "vscsi-devs/%s/p-dev", ent); val = xenbus_read(XBT_NIL, dev->nodename, str, NULL); if (IS_ERR(val)) { - xenbus_printf(XBT_NIL, dev->nodename, state, + err = xenbus_printf(XBT_NIL, dev->nodename, state, "%d", XenbusStateClosed); + if (err) + xenbus_dev_error(info->dev, err, + "%s: writing %s", __func__, state); return; } strlcpy(phy, val, VSCSI_NAMELEN); @@ -1079,8 +1086,11 @@ static void scsiback_do_1lun_hotplug(struct vscsibk_info *info, int op, err = xenbus_scanf(XBT_NIL, dev->nodename, str, "%u:%u:%u:%u", &vir.hst, &vir.chn, &vir.tgt, &vir.lun); if (XENBUS_EXIST_ERR(err)) { - xenbus_printf(XBT_NIL, dev->nodename, state, + err = xenbus_printf(XBT_NIL, dev->nodename, state, "%d", XenbusStateClosed); + if (err) + xenbus_dev_error(info->dev, err, + "%s: writing %s", __func__, state); return; } -- cgit v1.2.3 From 0cc61e64e21cfc24fa0d938fd148aba4a595163b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 19 Jun 2018 18:40:14 +0200 Subject: block: fix timeout changes for legacy request drivers blk_mq_complete_request can only be called for blk-mq drivers, but when removing the BLK_EH_HANDLED return value, two legacy request timeout methods incorrectly got switched to call blk_mq_complete_request. Call __blk_complete_request instead to reinstance the previous behavior. For that __blk_complete_request needs to be exported. Fixes: 1fc2b62e ("scsi_transport_fc: complete requests from ->timeout") Fixes: 0df0bb08 ("null_blk: complete requests from ->timeout") Reported-by: Jianchao Wang Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/blk-softirq.c | 1 + drivers/block/null_blk.c | 2 +- drivers/scsi/scsi_transport_fc.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/block/blk-softirq.c b/block/blk-softirq.c index 01e2b353a2b9..15c1f5e12eb8 100644 --- a/block/blk-softirq.c +++ b/block/blk-softirq.c @@ -144,6 +144,7 @@ do_local: local_irq_restore(flags); } +EXPORT_SYMBOL(__blk_complete_request); /** * blk_complete_request - end I/O on a request diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c index 2bdadd7f1454..3d8bdbe9bd35 100644 --- a/drivers/block/null_blk.c +++ b/drivers/block/null_blk.c @@ -1365,7 +1365,7 @@ static blk_qc_t null_queue_bio(struct request_queue *q, struct bio *bio) static enum blk_eh_timer_return null_rq_timed_out_fn(struct request *rq) { pr_info("null: rq %p timed out\n", rq); - blk_mq_complete_request(rq); + __blk_complete_request(rq); return BLK_EH_DONE; } diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 1da3d71e9f61..13948102ca29 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -3592,7 +3592,7 @@ fc_bsg_job_timeout(struct request *req) /* the blk_end_sync_io() doesn't check the error */ if (inflight) - blk_mq_complete_request(req); + __blk_complete_request(req); return BLK_EH_DONE; } -- cgit v1.2.3 From 91c822c33066b7c4f8cc47d7532f47e3bb89979b Mon Sep 17 00:00:00 2001 From: Rajan Vaja Date: Mon, 18 Jun 2018 13:01:02 +0530 Subject: drm/amd/pp: Fix uninitialized variable Initialize variable to 0 before performing logical OR operation. Reviewed-by: Rex Zhu Signed-off-by: Rajan Vaja Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/powerplay/hwmgr/vega10_powertune.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_powertune.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_powertune.c index dbe4b1f66784..22364875a943 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_powertune.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_powertune.c @@ -1090,7 +1090,7 @@ static int vega10_disable_se_edc_config(struct pp_hwmgr *hwmgr) static int vega10_enable_psm_gc_edc_config(struct pp_hwmgr *hwmgr) { struct amdgpu_device *adev = hwmgr->adev; - int result; + int result = 0; uint32_t num_se = 0; uint32_t count, data; -- cgit v1.2.3 From 6fa39bc1e01dab8b4f54b23e95a181a2ed5a2d38 Mon Sep 17 00:00:00 2001 From: Michel Dänzer Date: Fri, 8 Jun 2018 12:58:15 +0200 Subject: drm/amdgpu: Use kvmalloc_array for allocating VRAM manager nodes array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It can be quite big, and there's no need for it to be physically contiguous. This is less likely to fail under memory pressure (has actually happened while running piglit). Cc: stable@vger.kernel.org Signed-off-by: Michel Dänzer Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index 9aca653bec07..9c47e860e5e6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -135,7 +135,8 @@ static int amdgpu_vram_mgr_new(struct ttm_mem_type_manager *man, num_nodes = DIV_ROUND_UP(mem->num_pages, pages_per_node); } - nodes = kcalloc(num_nodes, sizeof(*nodes), GFP_KERNEL); + nodes = kvmalloc_array(num_nodes, sizeof(*nodes), + GFP_KERNEL | __GFP_ZERO); if (!nodes) return -ENOMEM; @@ -190,7 +191,7 @@ error: drm_mm_remove_node(&nodes[i]); spin_unlock(&mgr->lock); - kfree(nodes); + kvfree(nodes); return r == -ENOSPC ? 0 : r; } @@ -229,7 +230,7 @@ static void amdgpu_vram_mgr_del(struct ttm_mem_type_manager *man, atomic64_sub(usage, &mgr->usage); atomic64_sub(vis_usage, &mgr->vis_usage); - kfree(mem->mm_node); + kvfree(mem->mm_node); mem->mm_node = NULL; } -- cgit v1.2.3 From d9fda248046ac035f18a6e663f2f9245b4bf9470 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Tue, 8 May 2018 11:33:42 -0400 Subject: drm/amdgpu: Don't default to DC support for Kaveri and older We've had a number of users report failures to detect and light up display with DC with LVDS and VGA. These connector types are not currently supported with DC. I'd like to add support but unfortunately don't have a system with LVDS or VGA available. In order not to cause regressions we should probably fallback to the non-DC driver for ASICs that support VGA and LVDS. These ASICs are: * Bonaire * Kabini * Kaveri * Mullins ASIC support can always be force enabled with amdgpu.dc=1 v2: Keep Hawaii on DC v3: Added Mullins to the list Cc: stable@vger.kernel.org Signed-off-by: Harry Wentland Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 3317d1536f4f..6e5284e6c028 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -2158,10 +2158,18 @@ bool amdgpu_device_asic_has_dc_support(enum amd_asic_type asic_type) switch (asic_type) { #if defined(CONFIG_DRM_AMD_DC) case CHIP_BONAIRE: - case CHIP_HAWAII: case CHIP_KAVERI: case CHIP_KABINI: case CHIP_MULLINS: + /* + * We have systems in the wild with these ASICs that require + * LVDS and VGA support which is not supported with DC. + * + * Fallback to the non-DC driver here by default so as not to + * cause regressions. + */ + return amdgpu_dc > 0; + case CHIP_HAWAII: case CHIP_CARRIZO: case CHIP_STONEY: case CHIP_POLARIS10: -- cgit v1.2.3 From 5c53d19b76dccbaf340b11acb837d40c0789049d Mon Sep 17 00:00:00 2001 From: James Zhu Date: Mon, 18 Jun 2018 13:46:16 -0400 Subject: drm/amdgpu:All UVD instances share one idle_work handle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All UVD instanses have only one dpm control, so it is better to share one idle_work handle. Signed-off-by: James Zhu Reviewed-by: Alex Deucher Reviewed-by: Christian König Tested-by: Stefan Agner Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 14 +++++++------- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index bcf68f80bbf0..3ff08e326838 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -130,7 +130,7 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev) unsigned version_major, version_minor, family_id; int i, j, r; - INIT_DELAYED_WORK(&adev->uvd.inst->idle_work, amdgpu_uvd_idle_work_handler); + INIT_DELAYED_WORK(&adev->uvd.idle_work, amdgpu_uvd_idle_work_handler); switch (adev->asic_type) { #ifdef CONFIG_DRM_AMDGPU_CIK @@ -314,12 +314,12 @@ int amdgpu_uvd_suspend(struct amdgpu_device *adev) void *ptr; int i, j; + cancel_delayed_work_sync(&adev->uvd.idle_work); + for (j = 0; j < adev->uvd.num_uvd_inst; ++j) { if (adev->uvd.inst[j].vcpu_bo == NULL) continue; - cancel_delayed_work_sync(&adev->uvd.inst[j].idle_work); - /* only valid for physical mode */ if (adev->asic_type < CHIP_POLARIS10) { for (i = 0; i < adev->uvd.max_handles; ++i) @@ -1145,7 +1145,7 @@ int amdgpu_uvd_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle, static void amdgpu_uvd_idle_work_handler(struct work_struct *work) { struct amdgpu_device *adev = - container_of(work, struct amdgpu_device, uvd.inst->idle_work.work); + container_of(work, struct amdgpu_device, uvd.idle_work.work); unsigned fences = 0, i, j; for (i = 0; i < adev->uvd.num_uvd_inst; ++i) { @@ -1167,7 +1167,7 @@ static void amdgpu_uvd_idle_work_handler(struct work_struct *work) AMD_CG_STATE_GATE); } } else { - schedule_delayed_work(&adev->uvd.inst->idle_work, UVD_IDLE_TIMEOUT); + schedule_delayed_work(&adev->uvd.idle_work, UVD_IDLE_TIMEOUT); } } @@ -1179,7 +1179,7 @@ void amdgpu_uvd_ring_begin_use(struct amdgpu_ring *ring) if (amdgpu_sriov_vf(adev)) return; - set_clocks = !cancel_delayed_work_sync(&adev->uvd.inst->idle_work); + set_clocks = !cancel_delayed_work_sync(&adev->uvd.idle_work); if (set_clocks) { if (adev->pm.dpm_enabled) { amdgpu_dpm_enable_uvd(adev, true); @@ -1196,7 +1196,7 @@ void amdgpu_uvd_ring_begin_use(struct amdgpu_ring *ring) void amdgpu_uvd_ring_end_use(struct amdgpu_ring *ring) { if (!amdgpu_sriov_vf(ring->adev)) - schedule_delayed_work(&ring->adev->uvd.inst->idle_work, UVD_IDLE_TIMEOUT); + schedule_delayed_work(&ring->adev->uvd.idle_work, UVD_IDLE_TIMEOUT); } /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.h index b1579fba134c..8b23a1b00c76 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.h @@ -44,7 +44,6 @@ struct amdgpu_uvd_inst { void *saved_bo; atomic_t handles[AMDGPU_MAX_UVD_HANDLES]; struct drm_file *filp[AMDGPU_MAX_UVD_HANDLES]; - struct delayed_work idle_work; struct amdgpu_ring ring; struct amdgpu_ring ring_enc[AMDGPU_MAX_UVD_ENC_RINGS]; struct amdgpu_irq_src irq; @@ -62,6 +61,7 @@ struct amdgpu_uvd { bool address_64_bit; bool use_ctx_buf; struct amdgpu_uvd_inst inst[AMDGPU_MAX_UVD_INSTANCES]; + struct delayed_work idle_work; }; int amdgpu_uvd_sw_init(struct amdgpu_device *adev); -- cgit v1.2.3 From 34d6d59986abb1d2cb5415a49b6c50f51ba1d2e4 Mon Sep 17 00:00:00 2001 From: Michel Dänzer Date: Fri, 15 Jun 2018 11:06:56 +0200 Subject: drm/amdgpu: Update pin_size values before unpinning BO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At least in theory, ttm_bo_validate may move the BO, in which case the pin_size accounting would be inconsistent with when the BO was pinned. Cc: stable@vger.kernel.org Signed-off-by: Michel Dänzer Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index 5e4e1bd90383..026140f08ee9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -790,15 +790,6 @@ int amdgpu_bo_unpin(struct amdgpu_bo *bo) bo->pin_count--; if (bo->pin_count) return 0; - for (i = 0; i < bo->placement.num_placement; i++) { - bo->placements[i].lpfn = 0; - bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; - } - r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); - if (unlikely(r)) { - dev_err(adev->dev, "%p validate failed for unpin\n", bo); - goto error; - } if (bo->tbo.mem.mem_type == TTM_PL_VRAM) { adev->vram_pin_size -= amdgpu_bo_size(bo); @@ -808,7 +799,14 @@ int amdgpu_bo_unpin(struct amdgpu_bo *bo) adev->gart_pin_size -= amdgpu_bo_size(bo); } -error: + for (i = 0; i < bo->placement.num_placement; i++) { + bo->placements[i].lpfn = 0; + bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; + } + r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); + if (unlikely(r)) + dev_err(adev->dev, "%p validate failed for unpin\n", bo); + return r; } -- cgit v1.2.3 From 5e9244ff585239630f15f8ad8e676bc91a94ca9e Mon Sep 17 00:00:00 2001 From: Michel Dänzer Date: Tue, 12 Jun 2018 12:07:33 +0200 Subject: drm/amdgpu: Refactor amdgpu_vram_mgr_bo_invisible_size helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Preparation for the following fix, no functional change intended. Cc: stable@vger.kernel.org Signed-off-by: Michel Dänzer Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 6 ++---- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index 026140f08ee9..3526efa8960e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -762,8 +762,7 @@ int amdgpu_bo_pin_restricted(struct amdgpu_bo *bo, u32 domain, domain = amdgpu_mem_type_to_domain(bo->tbo.mem.mem_type); if (domain == AMDGPU_GEM_DOMAIN_VRAM) { adev->vram_pin_size += amdgpu_bo_size(bo); - if (bo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS) - adev->invisible_pin_size += amdgpu_bo_size(bo); + adev->invisible_pin_size += amdgpu_vram_mgr_bo_invisible_size(bo); } else if (domain == AMDGPU_GEM_DOMAIN_GTT) { adev->gart_pin_size += amdgpu_bo_size(bo); } @@ -793,8 +792,7 @@ int amdgpu_bo_unpin(struct amdgpu_bo *bo) if (bo->tbo.mem.mem_type == TTM_PL_VRAM) { adev->vram_pin_size -= amdgpu_bo_size(bo); - if (bo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS) - adev->invisible_pin_size -= amdgpu_bo_size(bo); + adev->invisible_pin_size -= amdgpu_vram_mgr_bo_invisible_size(bo); } else if (bo->tbo.mem.mem_type == TTM_PL_TT) { adev->gart_pin_size -= amdgpu_bo_size(bo); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h index e969c879d87e..e5da4654b630 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h @@ -73,6 +73,7 @@ bool amdgpu_gtt_mgr_has_gart_addr(struct ttm_mem_reg *mem); uint64_t amdgpu_gtt_mgr_usage(struct ttm_mem_type_manager *man); int amdgpu_gtt_mgr_recover(struct ttm_mem_type_manager *man); +u64 amdgpu_vram_mgr_bo_invisible_size(struct amdgpu_bo *bo); uint64_t amdgpu_vram_mgr_usage(struct ttm_mem_type_manager *man); uint64_t amdgpu_vram_mgr_vis_usage(struct ttm_mem_type_manager *man); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index 9c47e860e5e6..ae0049c6c52c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -96,6 +96,22 @@ static u64 amdgpu_vram_mgr_vis_size(struct amdgpu_device *adev, adev->gmc.visible_vram_size : end) - start; } +/** + * amdgpu_vram_mgr_bo_invisible_size - CPU invisible BO size + * + * @bo: &amdgpu_bo buffer object (must be in VRAM) + * + * Returns: + * How much of the given &amdgpu_bo buffer object lies in CPU invisible VRAM. + */ +u64 amdgpu_vram_mgr_bo_invisible_size(struct amdgpu_bo *bo) +{ + if (bo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS) + return amdgpu_bo_size(bo); + + return 0; +} + /** * amdgpu_vram_mgr_new - allocate new ranges * -- cgit v1.2.3 From 7303b39e46b2f523334591f05fd9566cf929eb26 Mon Sep 17 00:00:00 2001 From: Michel Dänzer Date: Thu, 14 Jun 2018 13:02:07 +0200 Subject: drm/amdgpu: Make amdgpu_vram_mgr_bo_invisible_size always accurate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even BOs with AMDGPU_GEM_CREATE_NO_CPU_ACCESS may end up at least partially in CPU visible VRAM, in particular when all VRAM is visible. v2: * Don't take VRAM mgr spinlock, not needed (Christian König) * Make loop logic simpler and clearer. Cc: stable@vger.kernel.org Signed-off-by: Michel Dänzer Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index ae0049c6c52c..b6333f92ba45 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -106,10 +106,26 @@ static u64 amdgpu_vram_mgr_vis_size(struct amdgpu_device *adev, */ u64 amdgpu_vram_mgr_bo_invisible_size(struct amdgpu_bo *bo) { - if (bo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS) + struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); + struct ttm_mem_reg *mem = &bo->tbo.mem; + struct drm_mm_node *nodes = mem->mm_node; + unsigned pages = mem->num_pages; + u64 usage = 0; + + if (adev->gmc.visible_vram_size == adev->gmc.real_vram_size) + return 0; + + if (mem->start >= adev->gmc.visible_vram_size >> PAGE_SHIFT) return amdgpu_bo_size(bo); - return 0; + while (nodes && pages) { + usage += nodes->size << PAGE_SHIFT; + usage -= amdgpu_vram_mgr_vis_size(adev, nodes); + pages -= nodes->size; + ++nodes; + } + + return usage; } /** -- cgit v1.2.3 From 4f9de4df901fb84709fe3a864dfa4eaf35700f68 Mon Sep 17 00:00:00 2001 From: Sudarsana Reddy Kalluru Date: Mon, 18 Jun 2018 21:58:00 -0700 Subject: qed: Fix possible memory leak in Rx error path handling. Memory for packet buffers need to be freed in the error paths as there is no consumer (e.g., upper layer) for such packets and that memory will never get freed. The issue was uncovered when port was attacked with flood of isatap packets, these are multicast packets hence were directed at all the PFs. For foce PF, this meant they were routed to the ll2 module which in turn drops such packets. Fixes: 0a7fb11c ("qed: Add Light L2 support") Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Ariel Elior Signed-off-by: Michal Kalderon Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_ll2.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c index c97ebd681c47..012973d75ad0 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c @@ -201,8 +201,9 @@ void qed_ll2b_complete_rx_packet(void *cxt, struct qed_ll2_comp_rx_data *data) skb = build_skb(buffer->data, 0); if (!skb) { - rc = -ENOMEM; - goto out_post; + DP_INFO(cdev, "Failed to build SKB\n"); + kfree(buffer->data); + goto out_post1; } data->u.placement_offset += NET_SKB_PAD; @@ -224,8 +225,14 @@ void qed_ll2b_complete_rx_packet(void *cxt, struct qed_ll2_comp_rx_data *data) cdev->ll2->cbs->rx_cb(cdev->ll2->cb_cookie, skb, data->opaque_data_0, data->opaque_data_1); + } else { + DP_VERBOSE(p_hwfn, (NETIF_MSG_RX_STATUS | NETIF_MSG_PKTDATA | + QED_MSG_LL2 | QED_MSG_STORAGE), + "Dropping the packet\n"); + kfree(buffer->data); } +out_post1: /* Update Buffer information and update FW producer */ buffer->data = new_data; buffer->phys_addr = new_phys_addr; -- cgit v1.2.3 From 3935a70968820c3994db4de7e6e1c7e814bff875 Mon Sep 17 00:00:00 2001 From: Sudarsana Reddy Kalluru Date: Mon, 18 Jun 2018 21:58:01 -0700 Subject: qed: Add sanity check for SIMD fastpath handler. Avoid calling a SIMD fastpath handler if it is NULL. The check is needed to handle an unlikely scenario where unsolicited interrupt is destined to a PF in INTa mode. Fixes: fe56b9e6a ("qed: Add module with basic common support") Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Ariel Elior Signed-off-by: Michal Kalderon Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_main.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index b04d57ca5176..5c10fd7210c3 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -567,8 +567,16 @@ static irqreturn_t qed_single_int(int irq, void *dev_instance) /* Fastpath interrupts */ for (j = 0; j < 64; j++) { if ((0x2ULL << j) & status) { - hwfn->simd_proto_handler[j].func( - hwfn->simd_proto_handler[j].token); + struct qed_simd_fp_handler *p_handler = + &hwfn->simd_proto_handler[j]; + + if (p_handler->func) + p_handler->func(p_handler->token); + else + DP_NOTICE(hwfn, + "Not calling fastpath handler as it is NULL [handler #%d, status 0x%llx]\n", + j, status); + status &= ~(0x2ULL << j); rc = IRQ_HANDLED; } -- cgit v1.2.3 From ff54d5cd9ec15546abc870452dd0b66eef4b4606 Mon Sep 17 00:00:00 2001 From: Sudarsana Reddy Kalluru Date: Mon, 18 Jun 2018 21:58:02 -0700 Subject: qed: Do not advertise DCBX_LLD_MANAGED capability. Do not advertise DCBX_LLD_MANAGED capability i.e., do not allow external agent to manage the dcbx/lldp negotiation. MFW acts as lldp agent for qed* devices, and no other lldp agent is allowed to coexist with mfw. Also updated a debug print, to not to display the redundant info. Fixes: a1d8d8a51 ("qed: Add dcbnl support.") Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Ariel Elior Signed-off-by: Michal Kalderon Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dcbx.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c index 8f31406ec894..f0b01385d5cb 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c @@ -255,9 +255,8 @@ qed_dcbx_get_app_protocol_type(struct qed_hwfn *p_hwfn, *type = DCBX_PROTOCOL_ROCE_V2; } else { *type = DCBX_MAX_PROTOCOL_TYPE; - DP_ERR(p_hwfn, - "No action required, App TLV id = 0x%x app_prio_bitmap = 0x%x\n", - id, app_prio_bitmap); + DP_ERR(p_hwfn, "No action required, App TLV entry = 0x%x\n", + app_prio_bitmap); return false; } @@ -1479,8 +1478,8 @@ static u8 qed_dcbnl_getcap(struct qed_dev *cdev, int capid, u8 *cap) *cap = 0x80; break; case DCB_CAP_ATTR_DCBX: - *cap = (DCB_CAP_DCBX_LLD_MANAGED | DCB_CAP_DCBX_VER_CEE | - DCB_CAP_DCBX_VER_IEEE | DCB_CAP_DCBX_STATIC); + *cap = (DCB_CAP_DCBX_VER_CEE | DCB_CAP_DCBX_VER_IEEE | + DCB_CAP_DCBX_STATIC); break; default: *cap = false; @@ -1548,8 +1547,6 @@ static u8 qed_dcbnl_getdcbx(struct qed_dev *cdev) if (!dcbx_info) return 0; - if (dcbx_info->operational.enabled) - mode |= DCB_CAP_DCBX_LLD_MANAGED; if (dcbx_info->operational.ieee) mode |= DCB_CAP_DCBX_VER_IEEE; if (dcbx_info->operational.cee) -- cgit v1.2.3 From c51818d5b793302b0923ade9856574ac28b9333b Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 19 Jun 2018 14:33:54 -0700 Subject: bpf, xdp, i40e: fix i40e_build_skb skb reserve and truesize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using skb_reserve(skb, I40E_SKB_PAD + (xdp->data - xdp->data_hard_start)) is clearly wrong since I40E_SKB_PAD already points to the offset where the original xdp->data was sitting since xdp->data_hard_start is defined as xdp->data - i40e_rx_offset(rx_ring) where latter offsets to I40E_SKB_PAD when build skb is used. However, also before cc5b114dcf98 ("bpf, i40e: add meta data support") this seems broken since bpf_xdp_adjust_head() helper could have been used to alter headroom and enlarge / shrink the frame and with that the assumption that the xdp->data remains unchanged does not hold and would push a bogus packet to upper stack. ixgbe got this right in 924708081629 ("ixgbe: add XDP support for pass and drop actions"). In any case, fix it by removing the I40E_SKB_PAD from both skb_reserve() and truesize calculation. Fixes: cc5b114dcf98 ("bpf, i40e: add meta data support") Fixes: 0c8493d90b6b ("i40e: add XDP support for pass and drop actions") Reported-by: Keith Busch Reported-by: Toshiaki Makita Signed-off-by: Daniel Borkmann Cc: Björn Töpel Cc: John Fastabend Tested-by: Keith Busch Acked-by: John Fastabend Acked-by: Alexander Duyck Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 8ffb7454e67c..ed6dbcfd4e96 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -2103,9 +2103,8 @@ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2; #else unsigned int truesize = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + - SKB_DATA_ALIGN(I40E_SKB_PAD + - (xdp->data_end - - xdp->data_hard_start)); + SKB_DATA_ALIGN(xdp->data_end - + xdp->data_hard_start); #endif struct sk_buff *skb; @@ -2124,7 +2123,7 @@ static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, return NULL; /* update pointers within the skb to store the data */ - skb_reserve(skb, I40E_SKB_PAD + (xdp->data - xdp->data_hard_start)); + skb_reserve(skb, xdp->data - xdp->data_hard_start); __skb_put(skb, xdp->data_end - xdp->data); if (metasize) skb_metadata_set(skb, metasize); -- cgit v1.2.3 From 87975a0117815b9b63527e8b8d9a9dffa6913132 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 19 Jun 2018 15:08:31 +0930 Subject: net/ncsi: Silence debug messages In normal operation we see this series of messages as the host drives the network device: ftgmac100 1e660000.ethernet eth0: NCSI: LSC AEN - channel 0 state down ftgmac100 1e660000.ethernet eth0: NCSI: suspending channel 0 ftgmac100 1e660000.ethernet eth0: NCSI: configuring channel 0 ftgmac100 1e660000.ethernet eth0: NCSI: channel 0 link down after config ftgmac100 1e660000.ethernet eth0: NCSI interface down ftgmac100 1e660000.ethernet eth0: NCSI: LSC AEN - channel 0 state up ftgmac100 1e660000.ethernet eth0: NCSI: configuring channel 0 ftgmac100 1e660000.ethernet eth0: NCSI interface up ftgmac100 1e660000.ethernet eth0: NCSI: LSC AEN - channel 0 state down ftgmac100 1e660000.ethernet eth0: NCSI: suspending channel 0 ftgmac100 1e660000.ethernet eth0: NCSI: configuring channel 0 ftgmac100 1e660000.ethernet eth0: NCSI: channel 0 link down after config ftgmac100 1e660000.ethernet eth0: NCSI interface down ftgmac100 1e660000.ethernet eth0: NCSI: LSC AEN - channel 0 state up ftgmac100 1e660000.ethernet eth0: NCSI: configuring channel 0 ftgmac100 1e660000.ethernet eth0: NCSI interface up This makes all of these messages netdev_dbg. They are still useful to debug eg. misbehaving network device firmware, but we do not need them filling up the kernel logs in normal operation. Acked-by: Samuel Mendoza-Jonas Signed-off-by: Joel Stanley Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 4 ++-- net/ncsi/ncsi-aen.c | 4 ++-- net/ncsi/ncsi-manage.c | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 78db8e62a83f..ed6c76d20b45 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1735,8 +1735,8 @@ static void ftgmac100_ncsi_handler(struct ncsi_dev *nd) if (unlikely(nd->state != ncsi_dev_state_functional)) return; - netdev_info(nd->dev, "NCSI interface %s\n", - nd->link_up ? "up" : "down"); + netdev_dbg(nd->dev, "NCSI interface %s\n", + nd->link_up ? "up" : "down"); } static void ftgmac100_setup_clk(struct ftgmac100 *priv) diff --git a/net/ncsi/ncsi-aen.c b/net/ncsi/ncsi-aen.c index e7b05de1e6d1..f899ed61bb57 100644 --- a/net/ncsi/ncsi-aen.c +++ b/net/ncsi/ncsi-aen.c @@ -73,8 +73,8 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, ncm->data[2] = data; ncm->data[4] = ntohl(lsc->oem_status); - netdev_info(ndp->ndev.dev, "NCSI: LSC AEN - channel %u state %s\n", - nc->id, data & 0x1 ? "up" : "down"); + netdev_dbg(ndp->ndev.dev, "NCSI: LSC AEN - channel %u state %s\n", + nc->id, data & 0x1 ? "up" : "down"); chained = !list_empty(&nc->link); state = nc->state; diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c index 5561e221b71f..616441c2b54f 100644 --- a/net/ncsi/ncsi-manage.c +++ b/net/ncsi/ncsi-manage.c @@ -816,9 +816,9 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) } else { hot_nc = NULL; nc->state = NCSI_CHANNEL_INACTIVE; - netdev_warn(ndp->ndev.dev, - "NCSI: channel %u link down after config\n", - nc->id); + netdev_dbg(ndp->ndev.dev, + "NCSI: channel %u link down after config\n", + nc->id); } spin_unlock_irqrestore(&nc->lock, flags); @@ -1199,14 +1199,14 @@ int ncsi_process_next_channel(struct ncsi_dev_priv *ndp) switch (old_state) { case NCSI_CHANNEL_INACTIVE: ndp->ndev.state = ncsi_dev_state_config; - netdev_info(ndp->ndev.dev, "NCSI: configuring channel %u\n", - nc->id); + netdev_dbg(ndp->ndev.dev, "NCSI: configuring channel %u\n", + nc->id); ncsi_configure_channel(ndp); break; case NCSI_CHANNEL_ACTIVE: ndp->ndev.state = ncsi_dev_state_suspend; - netdev_info(ndp->ndev.dev, "NCSI: suspending channel %u\n", - nc->id); + netdev_dbg(ndp->ndev.dev, "NCSI: suspending channel %u\n", + nc->id); ncsi_suspend_channel(ndp); break; default: -- cgit v1.2.3 From 3256d29fc7aecdf99feb1cb9475ed2252769a8a7 Mon Sep 17 00:00:00 2001 From: Govindarajulu Varadarajan Date: Tue, 19 Jun 2018 08:15:24 -0700 Subject: enic: initialize enic->rfs_h.lock in enic_probe lockdep spotted that we are using rfs_h.lock in enic_get_rxnfc() without initializing. rfs_h.lock is initialized in enic_open(). But ethtool_ops can be called when interface is down. Move enic_rfs_flw_tbl_init to enic_probe. INFO: trying to register non-static key. the code is fine but needs lockdep annotation. turning off the locking correctness validator. CPU: 18 PID: 1189 Comm: ethtool Not tainted 4.17.0-rc7-devel+ #27 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.11.0-20171110_100015-anatol 04/01/2014 Call Trace: dump_stack+0x85/0xc0 register_lock_class+0x550/0x560 ? __handle_mm_fault+0xa8b/0x1100 __lock_acquire+0x81/0x670 lock_acquire+0xb9/0x1e0 ? enic_get_rxnfc+0x139/0x2b0 [enic] _raw_spin_lock_bh+0x38/0x80 ? enic_get_rxnfc+0x139/0x2b0 [enic] enic_get_rxnfc+0x139/0x2b0 [enic] ethtool_get_rxnfc+0x8d/0x1c0 dev_ethtool+0x16c8/0x2400 ? __mutex_lock+0x64d/0xa00 ? dev_load+0x6a/0x150 dev_ioctl+0x253/0x4b0 sock_do_ioctl+0x9a/0x130 sock_ioctl+0x1af/0x350 do_vfs_ioctl+0x8e/0x670 ? syscall_trace_enter+0x1e2/0x380 ksys_ioctl+0x60/0x90 __x64_sys_ioctl+0x16/0x20 do_syscall_64+0x5a/0x170 entry_SYSCALL_64_after_hwframe+0x49/0xbe Signed-off-by: Govindarajulu Varadarajan Signed-off-by: David S. Miller --- drivers/net/ethernet/cisco/enic/enic_clsf.c | 3 +-- drivers/net/ethernet/cisco/enic/enic_main.c | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/cisco/enic/enic_clsf.c b/drivers/net/ethernet/cisco/enic/enic_clsf.c index 973c1fb70d09..99038dfc7fbe 100644 --- a/drivers/net/ethernet/cisco/enic/enic_clsf.c +++ b/drivers/net/ethernet/cisco/enic/enic_clsf.c @@ -79,7 +79,6 @@ void enic_rfs_flw_tbl_init(struct enic *enic) enic->rfs_h.max = enic->config.num_arfs; enic->rfs_h.free = enic->rfs_h.max; enic->rfs_h.toclean = 0; - enic_rfs_timer_start(enic); } void enic_rfs_flw_tbl_free(struct enic *enic) @@ -88,7 +87,6 @@ void enic_rfs_flw_tbl_free(struct enic *enic) enic_rfs_timer_stop(enic); spin_lock_bh(&enic->rfs_h.lock); - enic->rfs_h.free = 0; for (i = 0; i < (1 << ENIC_RFS_FLW_BITSHIFT); i++) { struct hlist_head *hhead; struct hlist_node *tmp; @@ -99,6 +97,7 @@ void enic_rfs_flw_tbl_free(struct enic *enic) enic_delfltr(enic, n->fltr_id); hlist_del(&n->node); kfree(n); + enic->rfs_h.free++; } } spin_unlock_bh(&enic->rfs_h.lock); diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 30d2eaa18c04..e6ad581eadd8 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -1971,7 +1971,7 @@ static int enic_open(struct net_device *netdev) vnic_intr_unmask(&enic->intr[i]); enic_notify_timer_start(enic); - enic_rfs_flw_tbl_init(enic); + enic_rfs_timer_start(enic); return 0; @@ -2904,6 +2904,7 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) timer_setup(&enic->notify_timer, enic_notify_timer, 0); + enic_rfs_flw_tbl_init(enic); enic_set_rx_coal_setting(enic); INIT_WORK(&enic->reset, enic_reset); INIT_WORK(&enic->tx_hang_reset, enic_tx_hang_reset); -- cgit v1.2.3 From 4e8439aa34802deab11cee68b0ecb18f887fb153 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sun, 17 Jun 2018 23:40:53 +0200 Subject: net: hamradio: use eth_broadcast_addr The array bpq_eth_addr is only used to get the size of an address, whereas the bcast_addr is used to set the broadcast address. This leads to a warning when using clang: drivers/net/hamradio/bpqether.c:94:13: warning: variable 'bpq_eth_addr' is not needed and will not be emitted [-Wunneeded-internal-declaration] static char bpq_eth_addr[6]; ^ Remove both variables and use the common eth_broadcast_addr to set the broadcast address. Signed-off-by: Stefan Agner Signed-off-by: David S. Miller --- drivers/net/hamradio/bpqether.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index f347fd9c5b28..777fa59f5e0c 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -89,10 +89,6 @@ static const char banner[] __initconst = KERN_INFO \ "AX.25: bpqether driver version 004\n"; -static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; - -static char bpq_eth_addr[6]; - static int bpq_rcv(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); static int bpq_device_event(struct notifier_block *, unsigned long, void *); @@ -501,8 +497,8 @@ static int bpq_new_device(struct net_device *edev) bpq->ethdev = edev; bpq->axdev = ndev; - memcpy(bpq->dest_addr, bcast_addr, sizeof(bpq_eth_addr)); - memcpy(bpq->acpt_addr, bcast_addr, sizeof(bpq_eth_addr)); + eth_broadcast_addr(bpq->dest_addr); + eth_broadcast_addr(bpq->acpt_addr); err = register_netdevice(ndev); if (err) -- cgit v1.2.3 From 548feb33c598dfaf9f8e066b842441ac49b84a8a Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 18 Jun 2018 16:15:57 +0800 Subject: ipvlan: use ETH_MAX_MTU as max mtu Similar to the fixes on team and bonding, this restores the ability to set an ipvlan device's mtu to anything higher than 1500. Fixes: 91572088e3fd ("net: use core MTU range checking in core net infra") Signed-off-by: Xin Long Signed-off-by: David S. Miller --- drivers/net/ipvlan/ipvlan_main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index 4377c26f714d..d02f0a7c534e 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -693,6 +693,7 @@ void ipvlan_link_setup(struct net_device *dev) { ether_setup(dev); + dev->max_mtu = ETH_MAX_MTU; dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING); dev->priv_flags |= IFF_UNICAST_FLT | IFF_NO_QUEUE; dev->netdev_ops = &ipvlan_netdev_ops; -- cgit v1.2.3 From e5223438280d76ef782592cf643e09441140d14c Mon Sep 17 00:00:00 2001 From: Liran Alon Date: Mon, 18 Jun 2018 15:04:05 +0300 Subject: net: net_failover: fix typo in net_failover_slave_register() Sync both unicast and multicast lists instead of unicast twice. Fixes: cfc80d9a116 ("net: Introduce net_failover driver") Reviewed-by: Joao Martins Signed-off-by: Liran Alon Signed-off-by: David S. Miller --- drivers/net/net_failover.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/net_failover.c b/drivers/net/net_failover.c index 83f7420ddea5..4f390fa557e4 100644 --- a/drivers/net/net_failover.c +++ b/drivers/net/net_failover.c @@ -527,7 +527,7 @@ static int net_failover_slave_register(struct net_device *slave_dev, netif_addr_lock_bh(failover_dev); dev_uc_sync_multiple(slave_dev, failover_dev); - dev_uc_sync_multiple(slave_dev, failover_dev); + dev_mc_sync_multiple(slave_dev, failover_dev); netif_addr_unlock_bh(failover_dev); err = vlan_vids_add_by_dev(slave_dev, failover_dev); -- cgit v1.2.3 From f696a21c229ac3e85bc239efc52f4530b43002c5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 18 Jun 2018 16:20:39 +0200 Subject: ptp: replace getnstimeofday64() with ktime_get_real_ts64() getnstimeofday64() is deprecated and getting replaced throughout the kernel with ktime_get_*() based helpers for a more consistent interface. The two functions do the exact same thing, so this is just a cosmetic change. Signed-off-by: Arnd Bergmann Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/ptp/ptp_chardev.c | 4 ++-- drivers/ptp/ptp_qoriq.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c index 767c485af59b..547dbdac9d54 100644 --- a/drivers/ptp/ptp_chardev.c +++ b/drivers/ptp/ptp_chardev.c @@ -221,7 +221,7 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) } pct = &sysoff->ts[0]; for (i = 0; i < sysoff->n_samples; i++) { - getnstimeofday64(&ts); + ktime_get_real_ts64(&ts); pct->sec = ts.tv_sec; pct->nsec = ts.tv_nsec; pct++; @@ -230,7 +230,7 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) pct->nsec = ts.tv_nsec; pct++; } - getnstimeofday64(&ts); + ktime_get_real_ts64(&ts); pct->sec = ts.tv_sec; pct->nsec = ts.tv_nsec; if (copy_to_user((void __user *)arg, sysoff, sizeof(*sysoff))) diff --git a/drivers/ptp/ptp_qoriq.c b/drivers/ptp/ptp_qoriq.c index 1468a1642b49..e8652c148c52 100644 --- a/drivers/ptp/ptp_qoriq.c +++ b/drivers/ptp/ptp_qoriq.c @@ -374,7 +374,7 @@ static int qoriq_ptp_probe(struct platform_device *dev) pr_err("ioremap ptp registers failed\n"); goto no_ioremap; } - getnstimeofday64(&now); + ktime_get_real_ts64(&now); ptp_qoriq_settime(&qoriq_ptp->caps, &now); tmr_ctrl = -- cgit v1.2.3 From 56f772279a762984f6e9ebbf24a7c829faba5712 Mon Sep 17 00:00:00 2001 From: Govindarajulu Varadarajan Date: Mon, 18 Jun 2018 10:01:05 -0700 Subject: enic: do not overwrite error code In failure path, we overwrite err to what vnic_rq_disable() returns. In case it returns 0, enic_open() returns success in case of error. Reported-by: Ben Hutchings Fixes: e8588e268509 ("enic: enable rq before updating rq descriptors") Signed-off-by: Govindarajulu Varadarajan Signed-off-by: David S. Miller --- drivers/net/ethernet/cisco/enic/enic_main.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index e6ad581eadd8..90c645b8538e 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -1920,7 +1920,7 @@ static int enic_open(struct net_device *netdev) { struct enic *enic = netdev_priv(netdev); unsigned int i; - int err; + int err, ret; err = enic_request_intr(enic); if (err) { @@ -1977,10 +1977,9 @@ static int enic_open(struct net_device *netdev) err_out_free_rq: for (i = 0; i < enic->rq_count; i++) { - err = vnic_rq_disable(&enic->rq[i]); - if (err) - return err; - vnic_rq_clean(&enic->rq[i], enic_free_rq_buf); + ret = vnic_rq_disable(&enic->rq[i]); + if (!ret) + vnic_rq_clean(&enic->rq[i], enic_free_rq_buf); } enic_dev_notify_unset(enic); err_out_free_intr: -- cgit v1.2.3 From dc45519eb181b5687ac8382361a8aa085acd1fe1 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 19 Jun 2018 14:44:00 +0200 Subject: net: ethernet: fix suspend/resume in davinci_emac This patch reverts commit 3243ff2a05ec ("net: ethernet: davinci_emac: Deduplicate bus_find_device() by name matching") and adds a comment which should stop anyone from reintroducing the same "fix" in the future. We can't use bus_find_device_by_name() here because the device name is not guaranteed to be 'davinci_mdio'. On some systems it can be 'davinci_mdio.0' so we need to use strncmp() against the first part of the string to correctly match it. Fixes: 3243ff2a05ec ("net: ethernet: davinci_emac: Deduplicate bus_find_device() by name matching") Cc: stable@vger.kernel.org Signed-off-by: Bartosz Golaszewski Acked-by: Lukas Wunner Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/davinci_emac.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 06d7c9e4dcda..a1a6445b5a7e 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1385,6 +1385,11 @@ static int emac_devioctl(struct net_device *ndev, struct ifreq *ifrq, int cmd) return -EOPNOTSUPP; } +static int match_first_device(struct device *dev, void *data) +{ + return !strncmp(dev_name(dev), "davinci_mdio", 12); +} + /** * emac_dev_open - EMAC device open * @ndev: The DaVinci EMAC network adapter @@ -1484,8 +1489,14 @@ static int emac_dev_open(struct net_device *ndev) /* use the first phy on the bus if pdata did not give us a phy id */ if (!phydev && !priv->phy_id) { - phy = bus_find_device_by_name(&mdio_bus_type, NULL, - "davinci_mdio"); + /* NOTE: we can't use bus_find_device_by_name() here because + * the device name is not guaranteed to be 'davinci_mdio'. On + * some systems it can be 'davinci_mdio.0' so we need to use + * strncmp() against the first part of the string to correctly + * match it. + */ + phy = bus_find_device(&mdio_bus_type, NULL, NULL, + match_first_device); if (phy) { priv->phy_id = dev_name(phy); if (!priv->phy_id || !*priv->phy_id) -- cgit v1.2.3 From 2aee167c3675b088c86f648f834e793a0085e04d Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 19 Jun 2018 16:14:30 +0200 Subject: net/usb/drivers: Remove useless hrtimer_active check The code does: if (hrtimer_active(&t)) hrtimer_cancel(&t); However, hrtimer_cancel() checks if the timer is active, so the test above is pointless. Signed-off-by: Daniel Lezcano Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index b0e8b9613054..1eaec648bd1f 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -967,8 +967,7 @@ void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf) atomic_set(&ctx->stop, 1); - if (hrtimer_active(&ctx->tx_timer)) - hrtimer_cancel(&ctx->tx_timer); + hrtimer_cancel(&ctx->tx_timer); tasklet_kill(&ctx->bh); -- cgit v1.2.3 From 12b03558cef6d655d0d394f5e98a6fd07c1f6c0f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 19 Jun 2018 19:18:50 -0700 Subject: net: sungem: fix rx checksum support After commit 88078d98d1bb ("net: pskb_trim_rcsum() and CHECKSUM_COMPLETE are friends"), sungem owners reported the infamous "eth0: hw csum failure" message. CHECKSUM_COMPLETE has in fact never worked for this driver, but this was masked by the fact that upper stacks had to strip the FCS, and therefore skb->ip_summed was set back to CHECKSUM_NONE before my recent change. Driver configures a number of bytes to skip when the chip computes the checksum, and for some reason only half of the Ethernet header was skipped. Then a second problem is that we should strip the FCS by default, unless the driver is updated to eventually support NETIF_F_RXFCS in the future. Finally, a driver should check if NETIF_F_RXCSUM feature is enabled or not, so that the admin can turn off rx checksum if wanted. Many thanks to Andreas Schwab and Mathieu Malaterre for their help in debugging this issue. Signed-off-by: Eric Dumazet Reported-by: Meelis Roos Reported-by: Mathieu Malaterre Reported-by: Andreas Schwab Tested-by: Andreas Schwab Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sungem.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/sun/sungem.c b/drivers/net/ethernet/sun/sungem.c index 7a16d40a72d1..b9221fc1674d 100644 --- a/drivers/net/ethernet/sun/sungem.c +++ b/drivers/net/ethernet/sun/sungem.c @@ -60,8 +60,7 @@ #include #include "sungem.h" -/* Stripping FCS is causing problems, disabled for now */ -#undef STRIP_FCS +#define STRIP_FCS #define DEFAULT_MSG (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -435,7 +434,7 @@ static int gem_rxmac_reset(struct gem *gp) writel(desc_dma & 0xffffffff, gp->regs + RXDMA_DBLOW); writel(RX_RING_SIZE - 4, gp->regs + RXDMA_KICK); val = (RXDMA_CFG_BASE | (RX_OFFSET << 10) | - ((14 / 2) << 13) | RXDMA_CFG_FTHRESH_128); + (ETH_HLEN << 13) | RXDMA_CFG_FTHRESH_128); writel(val, gp->regs + RXDMA_CFG); if (readl(gp->regs + GREG_BIFCFG) & GREG_BIFCFG_M66EN) writel(((5 & RXDMA_BLANK_IPKTS) | @@ -760,7 +759,6 @@ static int gem_rx(struct gem *gp, int work_to_do) struct net_device *dev = gp->dev; int entry, drops, work_done = 0; u32 done; - __sum16 csum; if (netif_msg_rx_status(gp)) printk(KERN_DEBUG "%s: rx interrupt, done: %d, rx_new: %d\n", @@ -855,9 +853,13 @@ static int gem_rx(struct gem *gp, int work_to_do) skb = copy_skb; } - csum = (__force __sum16)htons((status & RXDCTRL_TCPCSUM) ^ 0xffff); - skb->csum = csum_unfold(csum); - skb->ip_summed = CHECKSUM_COMPLETE; + if (likely(dev->features & NETIF_F_RXCSUM)) { + __sum16 csum; + + csum = (__force __sum16)htons((status & RXDCTRL_TCPCSUM) ^ 0xffff); + skb->csum = csum_unfold(csum); + skb->ip_summed = CHECKSUM_COMPLETE; + } skb->protocol = eth_type_trans(skb, gp->dev); napi_gro_receive(&gp->napi, skb); @@ -1761,7 +1763,7 @@ static void gem_init_dma(struct gem *gp) writel(0, gp->regs + TXDMA_KICK); val = (RXDMA_CFG_BASE | (RX_OFFSET << 10) | - ((14 / 2) << 13) | RXDMA_CFG_FTHRESH_128); + (ETH_HLEN << 13) | RXDMA_CFG_FTHRESH_128); writel(val, gp->regs + RXDMA_CFG); writel(desc_dma >> 32, gp->regs + RXDMA_DBHI); @@ -2985,8 +2987,8 @@ static int gem_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_drvdata(pdev, dev); /* We can do scatter/gather and HW checksum */ - dev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM; - dev->features |= dev->hw_features | NETIF_F_RXCSUM; + dev->hw_features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM; + dev->features = dev->hw_features; if (pci_using_dac) dev->features |= NETIF_F_HIGHDMA; -- cgit v1.2.3 From bc8a2d9bcbf1ca548b1deb315d14e1da81945bea Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 19 Jun 2018 10:35:38 -0500 Subject: net: stmmac: socfpga: add additional ocp reset line for Stratix10 The Stratix10 platform has an additional reset line, OCP(Open Core Protocol), that also needs to get deasserted for the stmmac ethernet controller to work. Thus we need to update the Kconfig to include ARCH_STRATIX10 in order to build dwmac-socfpga. Also, remove the redundant check for the reset controller pointer. The reset driver already checks for the pointer and returns 0 if the pointer is NULL. Signed-off-by: Dinh Nguyen Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/Kconfig | 2 +- drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index cb5b0f58c395..edf20361ea5f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -111,7 +111,7 @@ config DWMAC_ROCKCHIP config DWMAC_SOCFPGA tristate "SOCFPGA dwmac support" default ARCH_SOCFPGA - depends on OF && (ARCH_SOCFPGA || COMPILE_TEST) + depends on OF && (ARCH_SOCFPGA || ARCH_STRATIX10 || COMPILE_TEST) select MFD_SYSCON help Support for ethernet controller on Altera SOCFPGA diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c index 6e359572b9f0..5b3b06a0a3bf 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c @@ -55,6 +55,7 @@ struct socfpga_dwmac { struct device *dev; struct regmap *sys_mgr_base_addr; struct reset_control *stmmac_rst; + struct reset_control *stmmac_ocp_rst; void __iomem *splitter_base; bool f2h_ptp_ref_clk; struct tse_pcs pcs; @@ -262,8 +263,8 @@ static int socfpga_dwmac_set_phy_mode(struct socfpga_dwmac *dwmac) val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII; /* Assert reset to the enet controller before changing the phy mode */ - if (dwmac->stmmac_rst) - reset_control_assert(dwmac->stmmac_rst); + reset_control_assert(dwmac->stmmac_ocp_rst); + reset_control_assert(dwmac->stmmac_rst); regmap_read(sys_mgr_base_addr, reg_offset, &ctrl); ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << reg_shift); @@ -288,8 +289,8 @@ static int socfpga_dwmac_set_phy_mode(struct socfpga_dwmac *dwmac) /* Deassert reset for the phy configuration to be sampled by * the enet controller, and operation to start in requested mode */ - if (dwmac->stmmac_rst) - reset_control_deassert(dwmac->stmmac_rst); + reset_control_deassert(dwmac->stmmac_ocp_rst); + reset_control_deassert(dwmac->stmmac_rst); if (phymode == PHY_INTERFACE_MODE_SGMII) { if (tse_pcs_init(dwmac->pcs.tse_pcs_base, &dwmac->pcs) != 0) { dev_err(dwmac->dev, "Unable to initialize TSE PCS"); @@ -324,6 +325,15 @@ static int socfpga_dwmac_probe(struct platform_device *pdev) goto err_remove_config_dt; } + dwmac->stmmac_ocp_rst = devm_reset_control_get_optional(dev, "stmmaceth-ocp"); + if (IS_ERR(dwmac->stmmac_ocp_rst)) { + ret = PTR_ERR(dwmac->stmmac_ocp_rst); + dev_err(dev, "error getting reset control of ocp %d\n", ret); + goto err_remove_config_dt; + } + + reset_control_deassert(dwmac->stmmac_ocp_rst); + ret = socfpga_dwmac_parse_data(dwmac, dev); if (ret) { dev_err(dev, "Unable to parse OF data\n"); -- cgit v1.2.3 From b6cfffa7ad923c73f317ea50fd4ebcb3b4b6669c Mon Sep 17 00:00:00 2001 From: Bhadram Varka Date: Sun, 17 Jun 2018 20:02:05 +0530 Subject: stmmac: fix DMA channel hang in half-duplex mode HW does not support Half-duplex mode in multi-queue scenario. Fix it by not advertising the Half-Duplex mode if multi-queue enabled. Signed-off-by: Bhadram Varka Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index e79b0d7b388a..cba46b62a1cd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -928,6 +928,7 @@ static void stmmac_check_pcs_mode(struct stmmac_priv *priv) static int stmmac_init_phy(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); + u32 tx_cnt = priv->plat->tx_queues_to_use; struct phy_device *phydev; char phy_id_fmt[MII_BUS_ID_SIZE + 3]; char bus_id[MII_BUS_ID_SIZE]; @@ -968,6 +969,15 @@ static int stmmac_init_phy(struct net_device *dev) phydev->advertising &= ~(SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full); + /* + * Half-duplex mode not supported with multiqueue + * half-duplex can only works with single queue + */ + if (tx_cnt > 1) + phydev->supported &= ~(SUPPORTED_1000baseT_Half | + SUPPORTED_100baseT_Half | + SUPPORTED_10baseT_Half); + /* * Broken HW is sometimes missing the pull-up resistor on the * MDIO line, which results in reads to non-existent devices returning -- cgit v1.2.3 From 4bff980f920693693d7a529c06a1bd1e7f77603a Mon Sep 17 00:00:00 2001 From: Rodrigo Rivas Costa Date: Tue, 22 May 2018 22:10:06 +0200 Subject: HID: steam: use hid_device.driver_data instead of hid_set_drvdata() When creating the low-level hidraw device, the reference to steam_device was stored using hid_set_drvdata(). But this value is not guaranteed to be kept when set before calling probe. If this pointer is reset, it crashes when opening the emulated hidraw device. It looks like hid_set_drvdata() is for users "avobe" this hid_device, while hid_device.driver_data it for users "below" this one. In this case, we are creating a virtual hidraw device, so we must use hid_device.driver_data. Signed-off-by: Rodrigo Rivas Costa Tested-by: Mariusz Ceier Signed-off-by: Jiri Kosina --- drivers/hid/hid-steam.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c index cb86cc834201..0422ec2b13d2 100644 --- a/drivers/hid/hid-steam.c +++ b/drivers/hid/hid-steam.c @@ -573,7 +573,7 @@ static bool steam_is_valve_interface(struct hid_device *hdev) static int steam_client_ll_parse(struct hid_device *hdev) { - struct steam_device *steam = hid_get_drvdata(hdev); + struct steam_device *steam = hdev->driver_data; return hid_parse_report(hdev, steam->hdev->dev_rdesc, steam->hdev->dev_rsize); @@ -590,7 +590,7 @@ static void steam_client_ll_stop(struct hid_device *hdev) static int steam_client_ll_open(struct hid_device *hdev) { - struct steam_device *steam = hid_get_drvdata(hdev); + struct steam_device *steam = hdev->driver_data; int ret; ret = hid_hw_open(steam->hdev); @@ -605,7 +605,7 @@ static int steam_client_ll_open(struct hid_device *hdev) static void steam_client_ll_close(struct hid_device *hdev) { - struct steam_device *steam = hid_get_drvdata(hdev); + struct steam_device *steam = hdev->driver_data; mutex_lock(&steam->mutex); steam->client_opened = false; @@ -623,7 +623,7 @@ static int steam_client_ll_raw_request(struct hid_device *hdev, size_t count, unsigned char report_type, int reqtype) { - struct steam_device *steam = hid_get_drvdata(hdev); + struct steam_device *steam = hdev->driver_data; return hid_hw_raw_request(steam->hdev, reportnum, buf, count, report_type, reqtype); @@ -710,7 +710,7 @@ static int steam_probe(struct hid_device *hdev, ret = PTR_ERR(steam->client_hdev); goto client_hdev_fail; } - hid_set_drvdata(steam->client_hdev, steam); + steam->client_hdev->driver_data = steam; /* * With the real steam controller interface, do not connect hidraw. -- cgit v1.2.3 From ebeaa367548e9e92dd9374b9464ff6e7d157117b Mon Sep 17 00:00:00 2001 From: Even Xu Date: Fri, 12 Feb 2016 04:11:34 +0800 Subject: HID: intel_ish-hid: ipc: register more pm callbacks to support hibernation Current ISH driver only registers suspend/resume PM callbacks which don't support hibernation (suspend to disk). Basically after hiberation, the ISH can't resume properly and user may not see sensor events (for example: screen rotation may not work). User will not see a crash or panic or anything except the following message in log: hid-sensor-hub 001F:8086:22D8.0001: timeout waiting for response from ISHTP device So this patch adds support for S4/hiberbation to ISH by using the SIMPLE_DEV_PM_OPS() MACRO instead of struct dev_pm_ops directly. The suspend and resume functions will now be used for both suspend to RAM and hibernation. If power management is disabled, SIMPLE_DEV_PM_OPS will do nothing, the suspend and resume related functions won't be used, so mark them as __maybe_unused to clarify that this is the intended behavior, and remove #ifdefs for power management. Cc: stable@vger.kernel.org Signed-off-by: Even Xu Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ipc/pci-ish.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index 582e449be9fe..a2c53ea3b5ed 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -205,8 +205,7 @@ static void ish_remove(struct pci_dev *pdev) kfree(ishtp_dev); } -#ifdef CONFIG_PM -static struct device *ish_resume_device; +static struct device __maybe_unused *ish_resume_device; /* 50ms to get resume response */ #define WAIT_FOR_RESUME_ACK_MS 50 @@ -220,7 +219,7 @@ static struct device *ish_resume_device; * in that case a simple resume message is enough, others we need * a reset sequence. */ -static void ish_resume_handler(struct work_struct *work) +static void __maybe_unused ish_resume_handler(struct work_struct *work) { struct pci_dev *pdev = to_pci_dev(ish_resume_device); struct ishtp_device *dev = pci_get_drvdata(pdev); @@ -262,7 +261,7 @@ static void ish_resume_handler(struct work_struct *work) * * Return: 0 to the pm core */ -static int ish_suspend(struct device *device) +static int __maybe_unused ish_suspend(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct ishtp_device *dev = pci_get_drvdata(pdev); @@ -288,7 +287,7 @@ static int ish_suspend(struct device *device) return 0; } -static DECLARE_WORK(resume_work, ish_resume_handler); +static __maybe_unused DECLARE_WORK(resume_work, ish_resume_handler); /** * ish_resume() - ISH resume callback * @device: device pointer @@ -297,7 +296,7 @@ static DECLARE_WORK(resume_work, ish_resume_handler); * * Return: 0 to the pm core */ -static int ish_resume(struct device *device) +static int __maybe_unused ish_resume(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct ishtp_device *dev = pci_get_drvdata(pdev); @@ -311,21 +310,14 @@ static int ish_resume(struct device *device) return 0; } -static const struct dev_pm_ops ish_pm_ops = { - .suspend = ish_suspend, - .resume = ish_resume, -}; -#define ISHTP_ISH_PM_OPS (&ish_pm_ops) -#else -#define ISHTP_ISH_PM_OPS NULL -#endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(ish_pm_ops, ish_suspend, ish_resume); static struct pci_driver ish_driver = { .name = KBUILD_MODNAME, .id_table = ish_pci_tbl, .probe = ish_probe, .remove = ish_remove, - .driver.pm = ISHTP_ISH_PM_OPS, + .driver.pm = &ish_pm_ops, }; module_pci_driver(ish_driver); -- cgit v1.2.3 From d471b6b22d37bf9928c6d0202bdaaf76583b8b61 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Tue, 12 Jun 2018 13:42:46 -0700 Subject: HID: wacom: Correct logical maximum Y for 2nd-gen Intuos Pro large The HID descriptor for the 2nd-gen Intuos Pro large (PTH-860) contains a typo which defines an incorrect logical maximum Y value. This causes a small portion of the bottom of the tablet to become unusable (both because the area is below the "bottom" of the tablet and because 'wacom_wac_event' ignores out-of-range values). It also results in a skewed aspect ratio. To fix this, we add a quirk to 'wacom_usage_mapping' which overwrites the data with the correct value. Signed-off-by: Jason Gerecke CC: stable@vger.kernel.org # v4.10+ Signed-off-by: Jiri Kosina --- drivers/hid/wacom_sys.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index ee7a37eb159a..545986cfb978 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -395,6 +395,14 @@ static void wacom_usage_mapping(struct hid_device *hdev, } } + /* 2nd-generation Intuos Pro Large has incorrect Y maximum */ + if (hdev->vendor == USB_VENDOR_ID_WACOM && + hdev->product == 0x0358 && + WACOM_PEN_FIELD(field) && + wacom_equivalent_usage(usage->hid) == HID_GD_Y) { + field->logical_maximum = 43200; + } + switch (usage->hid) { case HID_GD_X: features->x_max = field->logical_maximum; -- cgit v1.2.3 From 3d0641015bf73aaa1cb54c936674959e7805070f Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Tue, 19 Jun 2018 15:34:09 +0300 Subject: nvme-rdma: fix possible double free condition when failing to create a controller Failures after nvme_init_ctrl will defer resource cleanups to .free_ctrl when the reference is released, hence we should not free the controller queues for these failures. Fix that by moving controller queues allocation before controller initialization and correctly freeing them for failures before initialization and skip them for failures after initialization. Signed-off-by: Sagi Grimberg Signed-off-by: Christoph Hellwig --- drivers/nvme/host/rdma.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index c9424da0d23e..bcb0e5d6343d 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -888,9 +888,9 @@ static void nvme_rdma_free_ctrl(struct nvme_ctrl *nctrl) list_del(&ctrl->list); mutex_unlock(&nvme_rdma_ctrl_mutex); - kfree(ctrl->queues); nvmf_free_options(nctrl->opts); free_ctrl: + kfree(ctrl->queues); kfree(ctrl); } @@ -1932,11 +1932,6 @@ static struct nvme_ctrl *nvme_rdma_create_ctrl(struct device *dev, goto out_free_ctrl; } - ret = nvme_init_ctrl(&ctrl->ctrl, dev, &nvme_rdma_ctrl_ops, - 0 /* no quirks, we're perfect! */); - if (ret) - goto out_free_ctrl; - INIT_DELAYED_WORK(&ctrl->reconnect_work, nvme_rdma_reconnect_ctrl_work); INIT_WORK(&ctrl->err_work, nvme_rdma_error_recovery_work); @@ -1950,14 +1945,19 @@ static struct nvme_ctrl *nvme_rdma_create_ctrl(struct device *dev, ctrl->queues = kcalloc(ctrl->ctrl.queue_count, sizeof(*ctrl->queues), GFP_KERNEL); if (!ctrl->queues) - goto out_uninit_ctrl; + goto out_free_ctrl; + + ret = nvme_init_ctrl(&ctrl->ctrl, dev, &nvme_rdma_ctrl_ops, + 0 /* no quirks, we're perfect! */); + if (ret) + goto out_kfree_queues; changed = nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING); WARN_ON_ONCE(!changed); ret = nvme_rdma_configure_admin_queue(ctrl, true); if (ret) - goto out_kfree_queues; + goto out_uninit_ctrl; /* sanity check icdoff */ if (ctrl->ctrl.icdoff) { @@ -2014,14 +2014,14 @@ static struct nvme_ctrl *nvme_rdma_create_ctrl(struct device *dev, out_remove_admin_queue: nvme_rdma_destroy_admin_queue(ctrl, true); -out_kfree_queues: - kfree(ctrl->queues); out_uninit_ctrl: nvme_uninit_ctrl(&ctrl->ctrl); nvme_put_ctrl(&ctrl->ctrl); if (ret > 0) ret = -EIO; return ERR_PTR(ret); +out_kfree_queues: + kfree(ctrl->queues); out_free_ctrl: kfree(ctrl); return ERR_PTR(ret); -- cgit v1.2.3 From 94e42213cc1ae41c57819539c0130f8dfc69d718 Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Tue, 19 Jun 2018 15:34:10 +0300 Subject: nvme-rdma: fix possible free of a non-allocated async event buffer If nvme_rdma_configure_admin_queue fails before we allocated the async event buffer, we will falsly free it because nvme_rdma_free_queue is freeing it. Fix it by allocating the buffer right after nvme_rdma_alloc_queue and free it right before nvme_rdma_queue_free to maintain orderly reverse cleanup sequence. Reported-by: Israel Rukshin Signed-off-by: Sagi Grimberg Reviewed-by: Max Gurtovoy Signed-off-by: Christoph Hellwig --- drivers/nvme/host/rdma.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index bcb0e5d6343d..f9affb71ac85 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -560,12 +560,6 @@ static void nvme_rdma_free_queue(struct nvme_rdma_queue *queue) if (!test_and_clear_bit(NVME_RDMA_Q_ALLOCATED, &queue->flags)) return; - if (nvme_rdma_queue_idx(queue) == 0) { - nvme_rdma_free_qe(queue->device->dev, - &queue->ctrl->async_event_sqe, - sizeof(struct nvme_command), DMA_TO_DEVICE); - } - nvme_rdma_destroy_queue_ib(queue); rdma_destroy_id(queue->cm_id); } @@ -739,6 +733,8 @@ static void nvme_rdma_destroy_admin_queue(struct nvme_rdma_ctrl *ctrl, blk_cleanup_queue(ctrl->ctrl.admin_q); nvme_rdma_free_tagset(&ctrl->ctrl, ctrl->ctrl.admin_tagset); } + nvme_rdma_free_qe(ctrl->device->dev, &ctrl->async_event_sqe, + sizeof(struct nvme_command), DMA_TO_DEVICE); nvme_rdma_free_queue(&ctrl->queues[0]); } @@ -755,11 +751,16 @@ static int nvme_rdma_configure_admin_queue(struct nvme_rdma_ctrl *ctrl, ctrl->max_fr_pages = nvme_rdma_get_max_fr_pages(ctrl->device->dev); + error = nvme_rdma_alloc_qe(ctrl->device->dev, &ctrl->async_event_sqe, + sizeof(struct nvme_command), DMA_TO_DEVICE); + if (error) + goto out_free_queue; + if (new) { ctrl->ctrl.admin_tagset = nvme_rdma_alloc_tagset(&ctrl->ctrl, true); if (IS_ERR(ctrl->ctrl.admin_tagset)) { error = PTR_ERR(ctrl->ctrl.admin_tagset); - goto out_free_queue; + goto out_free_async_qe; } ctrl->ctrl.admin_q = blk_mq_init_queue(&ctrl->admin_tag_set); @@ -795,12 +796,6 @@ static int nvme_rdma_configure_admin_queue(struct nvme_rdma_ctrl *ctrl, if (error) goto out_stop_queue; - error = nvme_rdma_alloc_qe(ctrl->queues[0].device->dev, - &ctrl->async_event_sqe, sizeof(struct nvme_command), - DMA_TO_DEVICE); - if (error) - goto out_stop_queue; - return 0; out_stop_queue: @@ -811,6 +806,9 @@ out_cleanup_queue: out_free_tagset: if (new) nvme_rdma_free_tagset(&ctrl->ctrl, ctrl->ctrl.admin_tagset); +out_free_async_qe: + nvme_rdma_free_qe(ctrl->device->dev, &ctrl->async_event_sqe, + sizeof(struct nvme_command), DMA_TO_DEVICE); out_free_queue: nvme_rdma_free_queue(&ctrl->queues[0]); return error; -- cgit v1.2.3 From c947657b15379505a9bba36a02005882b66abe57 Mon Sep 17 00:00:00 2001 From: Israel Rukshin Date: Tue, 19 Jun 2018 15:34:11 +0300 Subject: nvme-rdma: Fix command completion race at error recovery The race is between completing the request at error recovery work and rdma completions. If we cancel the request before getting the good rdma completion we get a NULL deref of the request MR at nvme_rdma_process_nvme_rsp(). When Canceling the request we return its mr to the mr pool (set mr to NULL) and also unmap its data. Canceling the requests while the rdma queues are active is not safe. Because rdma queues are active and we get good rdma completions that can use the mr pointer which may be NULL. Completing the request too soon may lead also to performing DMA to/from user buffers which might have been already unmapped. The commit fixes the race by draining the QP before starting the abort commands mechanism. Signed-off-by: Israel Rukshin Reviewed-by: Max Gurtovoy Signed-off-by: Sagi Grimberg Signed-off-by: Christoph Hellwig --- drivers/nvme/host/rdma.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index f9affb71ac85..2815749f4dfb 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -728,7 +728,6 @@ out: static void nvme_rdma_destroy_admin_queue(struct nvme_rdma_ctrl *ctrl, bool remove) { - nvme_rdma_stop_queue(&ctrl->queues[0]); if (remove) { blk_cleanup_queue(ctrl->ctrl.admin_q); nvme_rdma_free_tagset(&ctrl->ctrl, ctrl->ctrl.admin_tagset); @@ -817,7 +816,6 @@ out_free_queue: static void nvme_rdma_destroy_io_queues(struct nvme_rdma_ctrl *ctrl, bool remove) { - nvme_rdma_stop_io_queues(ctrl); if (remove) { blk_cleanup_queue(ctrl->ctrl.connect_q); nvme_rdma_free_tagset(&ctrl->ctrl, ctrl->ctrl.tagset); @@ -947,6 +945,7 @@ static void nvme_rdma_reconnect_ctrl_work(struct work_struct *work) return; destroy_admin: + nvme_rdma_stop_queue(&ctrl->queues[0]); nvme_rdma_destroy_admin_queue(ctrl, false); requeue: dev_info(ctrl->ctrl.device, "Failed reconnect attempt %d\n", @@ -963,12 +962,14 @@ static void nvme_rdma_error_recovery_work(struct work_struct *work) if (ctrl->ctrl.queue_count > 1) { nvme_stop_queues(&ctrl->ctrl); + nvme_rdma_stop_io_queues(ctrl); blk_mq_tagset_busy_iter(&ctrl->tag_set, nvme_cancel_request, &ctrl->ctrl); nvme_rdma_destroy_io_queues(ctrl, false); } blk_mq_quiesce_queue(ctrl->ctrl.admin_q); + nvme_rdma_stop_queue(&ctrl->queues[0]); blk_mq_tagset_busy_iter(&ctrl->admin_tag_set, nvme_cancel_request, &ctrl->ctrl); nvme_rdma_destroy_admin_queue(ctrl, false); @@ -1734,6 +1735,7 @@ static void nvme_rdma_shutdown_ctrl(struct nvme_rdma_ctrl *ctrl, bool shutdown) { if (ctrl->ctrl.queue_count > 1) { nvme_stop_queues(&ctrl->ctrl); + nvme_rdma_stop_io_queues(ctrl); blk_mq_tagset_busy_iter(&ctrl->tag_set, nvme_cancel_request, &ctrl->ctrl); nvme_rdma_destroy_io_queues(ctrl, shutdown); @@ -1745,6 +1747,7 @@ static void nvme_rdma_shutdown_ctrl(struct nvme_rdma_ctrl *ctrl, bool shutdown) nvme_disable_ctrl(&ctrl->ctrl, ctrl->ctrl.cap); blk_mq_quiesce_queue(ctrl->ctrl.admin_q); + nvme_rdma_stop_queue(&ctrl->queues[0]); blk_mq_tagset_busy_iter(&ctrl->admin_tag_set, nvme_cancel_request, &ctrl->ctrl); blk_mq_unquiesce_queue(ctrl->ctrl.admin_q); @@ -2011,6 +2014,7 @@ static struct nvme_ctrl *nvme_rdma_create_ctrl(struct device *dev, return &ctrl->ctrl; out_remove_admin_queue: + nvme_rdma_stop_queue(&ctrl->queues[0]); nvme_rdma_destroy_admin_queue(ctrl, true); out_uninit_ctrl: nvme_uninit_ctrl(&ctrl->ctrl); -- cgit v1.2.3 From 5e77d61cbc7e766778037127dab69e6410a8fc48 Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Tue, 19 Jun 2018 15:34:13 +0300 Subject: nvme-rdma: don't override opts->queue_size That is user argument, and theoretically controller limits can change over time (over reconnects/resets). Instead, use the sqsize controller attribute to check queue depth boundaries and use it to the tagset allocation. Signed-off-by: Sagi Grimberg Signed-off-by: Christoph Hellwig --- drivers/nvme/host/rdma.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index 2815749f4dfb..9544625c0b7d 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -692,7 +692,7 @@ static struct blk_mq_tag_set *nvme_rdma_alloc_tagset(struct nvme_ctrl *nctrl, set = &ctrl->tag_set; memset(set, 0, sizeof(*set)); set->ops = &nvme_rdma_mq_ops; - set->queue_depth = nctrl->opts->queue_size; + set->queue_depth = nctrl->sqsize + 1; set->reserved_tags = 1; /* fabric connect */ set->numa_node = NUMA_NO_NODE; set->flags = BLK_MQ_F_SHOULD_MERGE; @@ -1975,20 +1975,19 @@ static struct nvme_ctrl *nvme_rdma_create_ctrl(struct device *dev, goto out_remove_admin_queue; } - if (opts->queue_size > ctrl->ctrl.maxcmd) { - /* warn if maxcmd is lower than queue_size */ - dev_warn(ctrl->ctrl.device, - "queue_size %zu > ctrl maxcmd %u, clamping down\n", - opts->queue_size, ctrl->ctrl.maxcmd); - opts->queue_size = ctrl->ctrl.maxcmd; - } - + /* only warn if argument is too large here, will clamp later */ if (opts->queue_size > ctrl->ctrl.sqsize + 1) { - /* warn if sqsize is lower than queue_size */ dev_warn(ctrl->ctrl.device, "queue_size %zu > ctrl sqsize %u, clamping down\n", opts->queue_size, ctrl->ctrl.sqsize + 1); - opts->queue_size = ctrl->ctrl.sqsize + 1; + } + + /* warn if maxcmd is lower than sqsize+1 */ + if (ctrl->ctrl.sqsize + 1 > ctrl->ctrl.maxcmd) { + dev_warn(ctrl->ctrl.device, + "sqsize %u > ctrl maxcmd %u, clamping down\n", + ctrl->ctrl.sqsize + 1, ctrl->ctrl.maxcmd); + ctrl->ctrl.sqsize = ctrl->ctrl.maxcmd - 1; } if (opts->nr_io_queues) { -- cgit v1.2.3 From d68a90e148f5a82aa67654c5012071e31c0e4baa Mon Sep 17 00:00:00 2001 From: Max Gurtuvoy Date: Tue, 19 Jun 2018 15:45:33 +0300 Subject: nvmet: reset keep alive timer in controller enable Controllers that are not yet enabled should not really enforce keep alive timeouts, but we still want to track a timeout and cleanup in case a host died before it enabled the controller. Hence, simply reset the keep alive timer when the controller is enabled. Suggested-by: Max Gurtovoy Signed-off-by: Sagi Grimberg Signed-off-by: Christoph Hellwig --- drivers/nvme/target/core.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index a03da764ecae..74d4b785d2da 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -686,6 +686,14 @@ static void nvmet_start_ctrl(struct nvmet_ctrl *ctrl) } ctrl->csts = NVME_CSTS_RDY; + + /* + * Controllers that are not yet enabled should not really enforce the + * keep alive timeout, but we still want to track a timeout and cleanup + * in case a host died before it enabled the controller. Hence, simply + * reset the keep alive timer when the controller is enabled. + */ + mod_delayed_work(system_wq, &ctrl->ka_work, ctrl->kato * HZ); } static void nvmet_clear_ctrl(struct nvmet_ctrl *ctrl) -- cgit v1.2.3 From 6d8b834989f56066499cb25fd7d60169ed5e188b Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 20 Jun 2018 15:01:53 +0300 Subject: r8169: Fix netpoll oops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass the correct thing to rtl8169_interrupt() from netpoll. Cc: Realtek linux nic maintainers Cc: netdev@vger.kernel.org Cc: Heiner Kallweit Cc: David S. Miller Fixes: ebcd5daa7ffd ("r8169: change interrupt handler argument type") Signed-off-by: Ville Syrjälä Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 75dfac0248f4..f4cae2be0fda 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -7148,7 +7148,7 @@ static void rtl8169_netpoll(struct net_device *dev) { struct rtl8169_private *tp = netdev_priv(dev); - rtl8169_interrupt(pci_irq_vector(tp->pci_dev, 0), dev); + rtl8169_interrupt(pci_irq_vector(tp->pci_dev, 0), tp); } #endif -- cgit v1.2.3 From 08ba91ee6e2c1c08d3f0648f978cbb5dbf3491d8 Mon Sep 17 00:00:00 2001 From: Doron Roberts-Kedes Date: Fri, 15 Jun 2018 14:05:32 -0700 Subject: nbd: Add the nbd NBD_DISCONNECT_ON_CLOSE config flag. If NBD_DISCONNECT_ON_CLOSE is set on a device, then the driver will issue a disconnect from nbd_release if the device has no remaining bdev->bd_openers. Fix ret val so reconfigure with only setting the flag succeeds. Reviewed-by: Josef Bacik Signed-off-by: Doron Roberts-Kedes Signed-off-by: Jens Axboe --- drivers/block/nbd.c | 42 ++++++++++++++++++++++++++++++++++-------- include/uapi/linux/nbd.h | 3 +++ 2 files changed, 37 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 3b7083b8ecbb..74a05561b620 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -76,6 +76,7 @@ struct link_dead_args { #define NBD_HAS_CONFIG_REF 4 #define NBD_BOUND 5 #define NBD_DESTROY_ON_DISCONNECT 6 +#define NBD_DISCONNECT_ON_CLOSE 7 struct nbd_config { u32 flags; @@ -138,6 +139,7 @@ static void nbd_config_put(struct nbd_device *nbd); static void nbd_connect_reply(struct genl_info *info, int index); static int nbd_genl_status(struct sk_buff *skb, struct genl_info *info); static void nbd_dead_link_work(struct work_struct *work); +static void nbd_disconnect_and_put(struct nbd_device *nbd); static inline struct device *nbd_to_dev(struct nbd_device *nbd) { @@ -1305,6 +1307,12 @@ out: static void nbd_release(struct gendisk *disk, fmode_t mode) { struct nbd_device *nbd = disk->private_data; + struct block_device *bdev = bdget_disk(disk, 0); + + if (test_bit(NBD_DISCONNECT_ON_CLOSE, &nbd->config->runtime_flags) && + bdev->bd_openers == 0) + nbd_disconnect_and_put(nbd); + nbd_config_put(nbd); nbd_put(nbd); } @@ -1705,6 +1713,10 @@ again: &config->runtime_flags); put_dev = true; } + if (flags & NBD_CFLAG_DISCONNECT_ON_CLOSE) { + set_bit(NBD_DISCONNECT_ON_CLOSE, + &config->runtime_flags); + } } if (info->attrs[NBD_ATTR_SOCKETS]) { @@ -1749,6 +1761,17 @@ out: return ret; } +static void nbd_disconnect_and_put(struct nbd_device *nbd) +{ + mutex_lock(&nbd->config_lock); + nbd_disconnect(nbd); + nbd_clear_sock(nbd); + mutex_unlock(&nbd->config_lock); + if (test_and_clear_bit(NBD_HAS_CONFIG_REF, + &nbd->config->runtime_flags)) + nbd_config_put(nbd); +} + static int nbd_genl_disconnect(struct sk_buff *skb, struct genl_info *info) { struct nbd_device *nbd; @@ -1781,13 +1804,7 @@ static int nbd_genl_disconnect(struct sk_buff *skb, struct genl_info *info) nbd_put(nbd); return 0; } - mutex_lock(&nbd->config_lock); - nbd_disconnect(nbd); - nbd_clear_sock(nbd); - mutex_unlock(&nbd->config_lock); - if (test_and_clear_bit(NBD_HAS_CONFIG_REF, - &nbd->config->runtime_flags)) - nbd_config_put(nbd); + nbd_disconnect_and_put(nbd); nbd_config_put(nbd); nbd_put(nbd); return 0; @@ -1798,7 +1815,7 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info) struct nbd_device *nbd = NULL; struct nbd_config *config; int index; - int ret = -EINVAL; + int ret = 0; bool put_dev = false; if (!netlink_capable(skb, CAP_SYS_ADMIN)) @@ -1838,6 +1855,7 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info) !nbd->task_recv) { dev_err(nbd_to_dev(nbd), "not configured, cannot reconfigure\n"); + ret = -EINVAL; goto out; } @@ -1862,6 +1880,14 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info) &config->runtime_flags)) refcount_inc(&nbd->refs); } + + if (flags & NBD_CFLAG_DISCONNECT_ON_CLOSE) { + set_bit(NBD_DISCONNECT_ON_CLOSE, + &config->runtime_flags); + } else { + clear_bit(NBD_DISCONNECT_ON_CLOSE, + &config->runtime_flags); + } } if (info->attrs[NBD_ATTR_SOCKETS]) { diff --git a/include/uapi/linux/nbd.h b/include/uapi/linux/nbd.h index 85a3fb65e40a..20d6cc91435d 100644 --- a/include/uapi/linux/nbd.h +++ b/include/uapi/linux/nbd.h @@ -53,6 +53,9 @@ enum { /* These are client behavior specific flags. */ #define NBD_CFLAG_DESTROY_ON_DISCONNECT (1 << 0) /* delete the nbd device on disconnect. */ +#define NBD_CFLAG_DISCONNECT_ON_CLOSE (1 << 1) /* disconnect the nbd device on + * close by last opener. + */ /* userspace doesn't need the nbd_device structure */ -- cgit v1.2.3 From ea0820bb771175c7d4192fc6f5b5c56b3c6d5239 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 20 Jun 2018 10:03:56 +0200 Subject: net: davinci_emac: match the mdio device against its compatible if possible Device tree based systems without of_dev_auxdata will have the mdio device named differently than "davinci_mdio(.0)". In this case use the device's parent's compatible string for matching Signed-off-by: Bartosz Golaszewski Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/davinci_emac.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index a1a6445b5a7e..f270beebb428 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1387,6 +1387,10 @@ static int emac_devioctl(struct net_device *ndev, struct ifreq *ifrq, int cmd) static int match_first_device(struct device *dev, void *data) { + if (dev->parent && dev->parent->of_node) + return of_device_is_compatible(dev->parent->of_node, + "ti,davinci_mdio"); + return !strncmp(dev_name(dev), "davinci_mdio", 12); } -- cgit v1.2.3 From 08d02364b12faa54d76dbfea2090321fd27996f2 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Wed, 20 Jun 2018 10:50:46 +0200 Subject: net: mscc: fix the injection header When injecting frames in the Ocelot switch driver an injection header (IFH) should be used to configure various parameters related to a given frame, such as the port onto which the frame should be departed or its vlan id. Other parameters in the switch configuration can led to an injected frame being sent without an IFH but this led to various issues as the per-frame parameters are then not used. This is especially true when using multiple ports for injection. The IFH was injected with the wrong endianness which led to the switch not taking it into account as the IFH_INJ_BYPASS bit was then unset. (The bit tells the switch to use the IFH over its internal configuration). This patch fixes it. In addition to the endianness fix, the IFH is also fixed. As it was (unwillingly) unused, some of its fields were not configured the right way. Fixes: a556c76adc05 ("net: mscc: Add initial Ocelot switch support") Signed-off-by: Antoine Tenart Reviewed-by: Alexandre Belloni Signed-off-by: David S. Miller --- drivers/net/ethernet/mscc/ocelot.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index fb2c8f8071e6..52c57e0ff617 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -344,10 +344,9 @@ static int ocelot_port_stop(struct net_device *dev) static int ocelot_gen_ifh(u32 *ifh, struct frame_info *info) { ifh[0] = IFH_INJ_BYPASS; - ifh[1] = (0xff00 & info->port) >> 8; + ifh[1] = (0xf00 & info->port) >> 8; ifh[2] = (0xff & info->port) << 24; - ifh[3] = IFH_INJ_POP_CNT_DISABLE | (info->cpuq << 20) | - (info->tag_type << 16) | info->vid; + ifh[3] = (info->tag_type << 16) | info->vid; return 0; } @@ -370,11 +369,12 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) QS_INJ_CTRL_SOF, QS_INJ_CTRL, grp); info.port = BIT(port->chip_port); - info.cpuq = 0xff; + info.tag_type = IFH_TAG_TYPE_C; + info.vid = skb_vlan_tag_get(skb); ocelot_gen_ifh(ifh, &info); for (i = 0; i < IFH_LEN; i++) - ocelot_write_rix(ocelot, ifh[i], QS_INJ_WR, grp); + ocelot_write_rix(ocelot, cpu_to_be32(ifh[i]), QS_INJ_WR, grp); count = (skb->len + 3) / 4; last = skb->len % 4; -- cgit v1.2.3 From 30877961b1cdd6fdca783c2e8c4f0f47e95dc58c Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 21 Jun 2018 12:56:04 +0800 Subject: ipvlan: fix IFLA_MTU ignored on NEWLINK Commit 296d48568042 ("ipvlan: inherit MTU from master device") adjusted the mtu from the master device when creating a ipvlan device, but it would also override the mtu value set in rtnl_create_link. It causes IFLA_MTU param not to take effect. So this patch is to not adjust the mtu if IFLA_MTU param is set when creating a ipvlan device. Fixes: 296d48568042 ("ipvlan: inherit MTU from master device") Reported-by: Jianlin Shi Signed-off-by: Xin Long Signed-off-by: David S. Miller --- drivers/net/ipvlan/ipvlan_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index d02f0a7c534e..23c1d6600241 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -594,7 +594,8 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, ipvlan->phy_dev = phy_dev; ipvlan->dev = dev; ipvlan->sfeatures = IPVLAN_FEATURES; - ipvlan_adjust_mtu(ipvlan, phy_dev); + if (!tb[IFLA_MTU]) + ipvlan_adjust_mtu(ipvlan, phy_dev); INIT_LIST_HEAD(&ipvlan->addrs); spin_lock_init(&ipvlan->addrs_lock); -- cgit v1.2.3 From 64d7839af8c8f67daaf9bf387135052c55d85f90 Mon Sep 17 00:00:00 2001 From: Harini Katakam Date: Wed, 20 Jun 2018 17:04:20 +0530 Subject: net: macb: Fix ptp time adjustment for large negative delta When delta passed to gem_ptp_adjtime is negative, the sign is maintained in the ns_to_timespec64 conversion. Hence timespec_add should be used directly. timespec_sub will just subtract the negative value thus increasing the time difference. Signed-off-by: Harini Katakam Acked-by: Nicolas Ferre Signed-off-by: David S. Miller --- drivers/net/ethernet/cadence/macb_ptp.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/cadence/macb_ptp.c b/drivers/net/ethernet/cadence/macb_ptp.c index 2220c771092b..678835136bf8 100644 --- a/drivers/net/ethernet/cadence/macb_ptp.c +++ b/drivers/net/ethernet/cadence/macb_ptp.c @@ -170,10 +170,7 @@ static int gem_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) if (delta > TSU_NSEC_MAX_VAL) { gem_tsu_get_time(&bp->ptp_clock_info, &now); - if (sign) - now = timespec64_sub(now, then); - else - now = timespec64_add(now, then); + now = timespec64_add(now, then); gem_tsu_set_time(&bp->ptp_clock_info, (const struct timespec64 *)&now); -- cgit v1.2.3 From 02d62a8bc48e92171c46540722e2d52ce77d87af Mon Sep 17 00:00:00 2001 From: James Smart Date: Wed, 20 Jun 2018 07:44:12 -0700 Subject: nvme-fc: release io queues to allow fast fail Rather than leaving io queues quiesced after tearing down an association, restart them. This allows ios to be replayed, with fastfail ios terminating and non-fastfail getting into loops of retry. This follows rdma's lead. Signed-off-by: James Smart Reviewed-by: Sagi Grimberg Signed-off-by: Christoph Hellwig --- drivers/nvme/host/fc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index b528a2f5826c..41d45a1b5c62 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -2790,6 +2790,9 @@ nvme_fc_delete_association(struct nvme_fc_ctrl *ctrl) /* re-enable the admin_q so anything new can fast fail */ blk_mq_unquiesce_queue(ctrl->ctrl.admin_q); + /* resume the io queues so that things will fast fail */ + nvme_start_queues(&ctrl->ctrl); + nvme_fc_ctlr_inactive_on_rport(ctrl); } @@ -2804,9 +2807,6 @@ nvme_fc_delete_ctrl(struct nvme_ctrl *nctrl) * waiting for io to terminate */ nvme_fc_delete_association(ctrl); - - /* resume the io queues so that things will fast fail */ - nvme_start_queues(nctrl); } static void -- cgit v1.2.3 From e8b92efa629dac0e70ea4145c5e70616de5f89c8 Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Tue, 23 Jan 2018 12:17:19 +0100 Subject: drm/bridge/sii8620: fix display of packed pixel modes in MHL2 Currently packed pixel modes in MHL2 can't be displayed. The device automatically recognizes output format, so setting format other than RGB causes failure. Fix it by writing proper values to registers. Tested on MHL1 and MHL2 using various vendors' dongles both in DVI and HDMI mode. Signed-off-by: Maciej Purski Signed-off-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/1516706239-9104-1-git-send-email-m.purski@samsung.com --- drivers/gpu/drm/bridge/sil-sii8620.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 4a3deeda065c..250effa0e6b8 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -1017,20 +1017,11 @@ static void sii8620_stop_video(struct sii8620 *ctx) static void sii8620_set_format(struct sii8620 *ctx) { - u8 out_fmt; - if (sii8620_is_mhl3(ctx)) { sii8620_setbits(ctx, REG_M3_P0CTRL, BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE_PACKED, ctx->use_packed_pixel ? ~0 : 0); } else { - if (ctx->use_packed_pixel) - sii8620_write_seq_static(ctx, - REG_VID_MODE, BIT_VID_MODE_M1080P, - REG_MHL_TOP_CTL, BIT_MHL_TOP_CTL_MHL_PP_SEL | 1, - REG_MHLTX_CTL6, 0x60 - ); - else sii8620_write_seq_static(ctx, REG_VID_MODE, 0, REG_MHL_TOP_CTL, 1, @@ -1038,15 +1029,9 @@ static void sii8620_set_format(struct sii8620 *ctx) ); } - if (ctx->use_packed_pixel) - out_fmt = VAL_TPI_FORMAT(YCBCR422, FULL) | - BIT_TPI_OUTPUT_CSCMODE709; - else - out_fmt = VAL_TPI_FORMAT(RGB, FULL); - sii8620_write_seq(ctx, REG_TPI_INPUT, VAL_TPI_FORMAT(RGB, FULL), - REG_TPI_OUTPUT, out_fmt, + REG_TPI_OUTPUT, VAL_TPI_FORMAT(RGB, FULL), ); } -- cgit v1.2.3 From 9f9cafc14016f23f982d3ce18f9057923bd3037a Mon Sep 17 00:00:00 2001 From: Jianchao Wang Date: Wed, 20 Jun 2018 13:42:22 +0800 Subject: nvme-pci: move nvme_kill_queues to nvme_remove_dead_ctrl There is race between nvme_remove and nvme_reset_work that can lead to io hang. nvme_remove nvme_reset_work -> nvme_remove_dead_ctrl -> nvme_dev_disable -> quiesce request_queue -> queue remove_work -> cancel_work_sync reset_work -> nvme_remove_namespaces -> splice ctrl->namespaces nvme_remove_dead_ctrl_work -> nvme_kill_queues -> nvme_ns_remove do nothing -> blk_cleanup_queue -> blk_freeze_queue Finally, the request_queue is quiesced state when wait freeze, we will get io hang here. To fix it, move the nvme_kill_queues from nvme_remove_dead_ctrl_work to nvme_remove_dead_ctrl. Suggested-by: Keith Busch Signed-off-by: Jianchao Wang Reviewed-by: Keith Busch Signed-off-by: Christoph Hellwig --- drivers/nvme/host/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index fc33804662e7..73a97fcea364 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2289,6 +2289,7 @@ static void nvme_remove_dead_ctrl(struct nvme_dev *dev, int status) nvme_get_ctrl(&dev->ctrl); nvme_dev_disable(dev, false); + nvme_kill_queues(&dev->ctrl); if (!queue_work(nvme_wq, &dev->remove_work)) nvme_put_ctrl(&dev->ctrl); } @@ -2405,7 +2406,6 @@ static void nvme_remove_dead_ctrl_work(struct work_struct *work) struct nvme_dev *dev = container_of(work, struct nvme_dev, remove_work); struct pci_dev *pdev = to_pci_dev(dev->dev); - nvme_kill_queues(&dev->ctrl); if (pci_get_drvdata(pdev)) device_release_driver(&pdev->dev); nvme_put_ctrl(&dev->ctrl); -- cgit v1.2.3 From 943e942e6266f22babee5efeb00f8f672fbff5bd Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 21 Jun 2018 09:49:37 -0600 Subject: nvme-pci: limit max IO size and segments to avoid high order allocations nvme requires an sg table allocation for each request. If the request is large, then the allocation can become quite large. For instance, with our default software settings of 1280KB IO size, we'll need 10248 bytes of sg table. That turns into a 2nd order allocation, which we can't always guarantee. If we fail the allocation, blk-mq will retry it later. But there's no guarantee that we'll EVER be able to allocate that much contigious memory. Limit the IO size such that we never need more than a single page of memory. That's a lot faster and more reliable. Then back that allocation with a mempool, so that we know we'll always be able to succeed the allocation at some point. Signed-off-by: Jens Axboe Acked-by: Keith Busch Signed-off-by: Christoph Hellwig --- drivers/nvme/host/core.c | 1 + drivers/nvme/host/nvme.h | 1 + drivers/nvme/host/pci.c | 42 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 39 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 21710a7460c8..46df030b2c3f 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -1808,6 +1808,7 @@ static void nvme_set_queue_limits(struct nvme_ctrl *ctrl, u32 max_segments = (ctrl->max_hw_sectors / (ctrl->page_size >> 9)) + 1; + max_segments = min_not_zero(max_segments, ctrl->max_segments); blk_queue_max_hw_sectors(q, ctrl->max_hw_sectors); blk_queue_max_segments(q, min_t(u32, max_segments, USHRT_MAX)); } diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 231807cbc849..0c4a33df3b2f 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -170,6 +170,7 @@ struct nvme_ctrl { u64 cap; u32 page_size; u32 max_hw_sectors; + u32 max_segments; u16 oncs; u16 oacs; u16 nssa; diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 73a97fcea364..ba943f211687 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -38,6 +38,13 @@ #define SGES_PER_PAGE (PAGE_SIZE / sizeof(struct nvme_sgl_desc)) +/* + * These can be higher, but we need to ensure that any command doesn't + * require an sg allocation that needs more than a page of data. + */ +#define NVME_MAX_KB_SZ 4096 +#define NVME_MAX_SEGS 127 + static int use_threaded_interrupts; module_param(use_threaded_interrupts, int, 0); @@ -100,6 +107,8 @@ struct nvme_dev { struct nvme_ctrl ctrl; struct completion ioq_wait; + mempool_t *iod_mempool; + /* shadow doorbell buffer support: */ u32 *dbbuf_dbs; dma_addr_t dbbuf_dbs_dma_addr; @@ -477,10 +486,7 @@ static blk_status_t nvme_init_iod(struct request *rq, struct nvme_dev *dev) iod->use_sgl = nvme_pci_use_sgls(dev, rq); if (nseg > NVME_INT_PAGES || size > NVME_INT_BYTES(dev)) { - size_t alloc_size = nvme_pci_iod_alloc_size(dev, size, nseg, - iod->use_sgl); - - iod->sg = kmalloc(alloc_size, GFP_ATOMIC); + iod->sg = mempool_alloc(dev->iod_mempool, GFP_ATOMIC); if (!iod->sg) return BLK_STS_RESOURCE; } else { @@ -526,7 +532,7 @@ static void nvme_free_iod(struct nvme_dev *dev, struct request *req) } if (iod->sg != iod->inline_sg) - kfree(iod->sg); + mempool_free(iod->sg, dev->iod_mempool); } #ifdef CONFIG_BLK_DEV_INTEGRITY @@ -2280,6 +2286,7 @@ static void nvme_pci_free_ctrl(struct nvme_ctrl *ctrl) blk_put_queue(dev->ctrl.admin_q); kfree(dev->queues); free_opal_dev(dev->ctrl.opal_dev); + mempool_destroy(dev->iod_mempool); kfree(dev); } @@ -2334,6 +2341,13 @@ static void nvme_reset_work(struct work_struct *work) if (result) goto out; + /* + * Limit the max command size to prevent iod->sg allocations going + * over a single page. + */ + dev->ctrl.max_hw_sectors = NVME_MAX_KB_SZ << 1; + dev->ctrl.max_segments = NVME_MAX_SEGS; + result = nvme_init_identify(&dev->ctrl); if (result) goto out; @@ -2509,6 +2523,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) int node, result = -ENOMEM; struct nvme_dev *dev; unsigned long quirks = id->driver_data; + size_t alloc_size; node = dev_to_node(&pdev->dev); if (node == NUMA_NO_NODE) @@ -2546,6 +2561,23 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (result) goto release_pools; + /* + * Double check that our mempool alloc size will cover the biggest + * command we support. + */ + alloc_size = nvme_pci_iod_alloc_size(dev, NVME_MAX_KB_SZ, + NVME_MAX_SEGS, true); + WARN_ON_ONCE(alloc_size > PAGE_SIZE); + + dev->iod_mempool = mempool_create_node(1, mempool_kmalloc, + mempool_kfree, + (void *) alloc_size, + GFP_KERNEL, node); + if (!dev->iod_mempool) { + result = -ENOMEM; + goto release_pools; + } + dev_info(dev->ctrl.device, "pci function %s\n", dev_name(&pdev->dev)); nvme_get_ctrl(&dev->ctrl); -- cgit v1.2.3 From e9893e6fa932f42c90c4ac5849fa9aa0f0f00a34 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Wed, 13 Jun 2018 14:32:36 +0530 Subject: mtd: rawnand: fix return value check for bad block status Positive return value from read_oob() is making false BAD blocks. For some of the NAND controllers, OOB bytes will be protected with ECC and read_oob() will return number of bitflips. If there is any bitflip in ECC protected OOB bytes for BAD block status page, then that block is getting treated as BAD. Fixes: c120e75e0e7d ("mtd: nand: use read_oob() instead of cmdfunc() for bad block check") Cc: Signed-off-by: Abhishek Sahu Reviewed-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/nand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 10c4f9919850..b01d15ec4c56 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -440,7 +440,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs) for (; page < page_end; page++) { res = chip->ecc.read_oob(mtd, chip, page); - if (res) + if (res < 0) return res; bad = chip->oob_poi[chip->badblockpos]; -- cgit v1.2.3 From fe3dd97dd66bb7fb23b8077a3803d2f951e60b00 Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Wed, 20 Jun 2018 11:46:30 +0200 Subject: mtd: rawnand: All AC chips have a broken GET_FEATURES(TIMINGS). Make sure we flag all broken chips as not supporting this feature. Also move this logic to a new function to keep things readable. Fixes: 34c5c01e0c8c ("mtd: rawnand: macronix: nack the support of changing timings for one chip") Cc: Signed-off-by: Mason Yang Signed-off-by: Boris Brezillon Reviewed-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_macronix.c | 48 +++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c index 7ed1f87e742a..49c546c97c6f 100644 --- a/drivers/mtd/nand/raw/nand_macronix.c +++ b/drivers/mtd/nand/raw/nand_macronix.c @@ -17,23 +17,47 @@ #include +/* + * Macronix AC series does not support using SET/GET_FEATURES to change + * the timings unlike what is declared in the parameter page. Unflag + * this feature to avoid unnecessary downturns. + */ +static void macronix_nand_fix_broken_get_timings(struct nand_chip *chip) +{ + unsigned int i; + static const char * const broken_get_timings[] = { + "MX30LF1G18AC", + "MX30LF1G28AC", + "MX30LF2G18AC", + "MX30LF2G28AC", + "MX30LF4G18AC", + "MX30LF4G28AC", + "MX60LF8G18AC", + }; + + if (!chip->parameters.supports_set_get_features) + return; + + for (i = 0; i < ARRAY_SIZE(broken_get_timings); i++) { + if (!strcmp(broken_get_timings[i], chip->parameters.model)) + break; + } + + if (i == ARRAY_SIZE(broken_get_timings)) + return; + + bitmap_clear(chip->parameters.get_feature_list, + ONFI_FEATURE_ADDR_TIMING_MODE, 1); + bitmap_clear(chip->parameters.set_feature_list, + ONFI_FEATURE_ADDR_TIMING_MODE, 1); +} + static int macronix_nand_init(struct nand_chip *chip) { if (nand_is_slc(chip)) chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; - /* - * MX30LF2G18AC chip does not support using SET/GET_FEATURES to change - * the timings unlike what is declared in the parameter page. Unflag - * this feature to avoid unnecessary downturns. - */ - if (chip->parameters.supports_set_get_features && - !strcmp("MX30LF2G18AC", chip->parameters.model)) { - bitmap_clear(chip->parameters.get_feature_list, - ONFI_FEATURE_ADDR_TIMING_MODE, 1); - bitmap_clear(chip->parameters.set_feature_list, - ONFI_FEATURE_ADDR_TIMING_MODE, 1); - } + macronix_nand_fix_broken_get_timings(chip); return 0; } -- cgit v1.2.3 From f93aa8c4de307069c270b2d81741961162bead6c Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Wed, 6 Jun 2018 12:13:27 +0200 Subject: mtd: cfi_cmdset_0002: Use right chip in do_ppb_xxlock() do_ppb_xxlock() fails to add chip->start when querying for lock status (and chip_ready test), which caused false status reports. Fix that by adding adr += chip->start and adjust call sites accordingly. Fixes: 1648eaaa1575 ("mtd: cfi_cmdset_0002: Support Persistent Protection Bits (PPB) locking") Cc: stable@vger.kernel.org Signed-off-by: Joakim Tjernlund Signed-off-by: Boris Brezillon --- drivers/mtd/chips/cfi_cmdset_0002.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index a0c655628d6d..aa09d5155429 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -2544,8 +2544,9 @@ static int __maybe_unused do_ppb_xxlock(struct map_info *map, unsigned long timeo; int ret; + adr += chip->start; mutex_lock(&chip->mutex); - ret = get_chip(map, chip, adr + chip->start, FL_LOCKING); + ret = get_chip(map, chip, adr, FL_LOCKING); if (ret) { mutex_unlock(&chip->mutex); return ret; @@ -2563,8 +2564,8 @@ static int __maybe_unused do_ppb_xxlock(struct map_info *map, if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) { chip->state = FL_LOCKING; - map_write(map, CMD(0xA0), chip->start + adr); - map_write(map, CMD(0x00), chip->start + adr); + map_write(map, CMD(0xA0), adr); + map_write(map, CMD(0x00), adr); } else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) { /* * Unlocking of one specific sector is not supported, so we @@ -2602,7 +2603,7 @@ static int __maybe_unused do_ppb_xxlock(struct map_info *map, map_write(map, CMD(0x00), chip->start); chip->state = FL_READY; - put_chip(map, chip, adr + chip->start); + put_chip(map, chip, adr); mutex_unlock(&chip->mutex); return ret; -- cgit v1.2.3 From 5fdfc3dbad099281bf027a353d5786c09408a8e5 Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Wed, 6 Jun 2018 12:13:28 +0200 Subject: mtd: cfi_cmdset_0002: fix SEGV unlocking multiple chips cfi_ppb_unlock() tries to relock all sectors that were locked before unlocking the whole chip. This locking used the chip start address + the FULL offset from the first flash chip, thereby forming an illegal address. Fix that by using the chip offset(adr). Fixes: 1648eaaa1575 ("mtd: cfi_cmdset_0002: Support Persistent Protection Bits (PPB) locking") Cc: stable@vger.kernel.org Signed-off-by: Joakim Tjernlund Signed-off-by: Boris Brezillon --- drivers/mtd/chips/cfi_cmdset_0002.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index aa09d5155429..87f9925d25f9 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -2526,7 +2526,7 @@ static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) struct ppb_lock { struct flchip *chip; - loff_t offset; + unsigned long adr; int locked; }; @@ -2662,7 +2662,7 @@ static int __maybe_unused cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, */ if ((adr < ofs) || (adr >= (ofs + len))) { sect[sectors].chip = &cfi->chips[chipnum]; - sect[sectors].offset = offset; + sect[sectors].adr = adr; sect[sectors].locked = do_ppb_xxlock( map, &cfi->chips[chipnum], adr, 0, DO_XXLOCK_ONEBLOCK_GETLOCK); @@ -2706,7 +2706,7 @@ static int __maybe_unused cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, */ for (i = 0; i < sectors; i++) { if (sect[i].locked) - do_ppb_xxlock(map, sect[i].chip, sect[i].offset, 0, + do_ppb_xxlock(map, sect[i].chip, sect[i].adr, 0, DO_XXLOCK_ONEBLOCK_LOCK); } -- cgit v1.2.3 From cb257783c2927b73614b20f915a91ff78aa6f3e8 Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Thu, 21 Jun 2018 14:00:20 +0100 Subject: xen-netfront: Fix mismatched rtnl_unlock Fixes: f599c64fdf7d ("xen-netfront: Fix race between device setup and open") Reported-by: Ben Hutchings Signed-off-by: Ross Lagerwall Reviewed-by: Juergen Gross Signed-off-by: David S. Miller --- drivers/net/xen-netfront.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 922ce0abf5cf..ee4cb6cfde1e 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1810,7 +1810,7 @@ static int talk_to_netback(struct xenbus_device *dev, err = xen_net_read_mac(dev, info->netdev->dev_addr); if (err) { xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename); - goto out; + goto out_unlocked; } rtnl_lock(); @@ -1925,6 +1925,7 @@ abort_transaction_no_dev_fatal: xennet_destroy_queues(info); out: rtnl_unlock(); +out_unlocked: device_unregister(&dev->dev); return err; } -- cgit v1.2.3 From 45c8184c1bed1ca8a7f02918552063a00b909bf5 Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Thu, 21 Jun 2018 14:00:21 +0100 Subject: xen-netfront: Update features after registering netdev Update the features after calling register_netdev() otherwise the device features are not set up correctly and it not possible to change the MTU of the device. After this change, the features reported by ethtool match the device's features before the commit which introduced the issue and it is possible to change the device's MTU. Fixes: f599c64fdf7d ("xen-netfront: Fix race between device setup and open") Reported-by: Liam Shepherd Signed-off-by: Ross Lagerwall Reviewed-by: Juergen Gross Signed-off-by: David S. Miller --- drivers/net/xen-netfront.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index ee4cb6cfde1e..a57daecf1d57 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1951,10 +1951,6 @@ static int xennet_connect(struct net_device *dev) /* talk_to_netback() sets the correct number of queues */ num_queues = dev->real_num_tx_queues; - rtnl_lock(); - netdev_update_features(dev); - rtnl_unlock(); - if (dev->reg_state == NETREG_UNINITIALIZED) { err = register_netdev(dev); if (err) { @@ -1964,6 +1960,10 @@ static int xennet_connect(struct net_device *dev) } } + rtnl_lock(); + netdev_update_features(dev); + rtnl_unlock(); + /* * All public and private state should now be sane. Get * ready to start sending and receiving packets and give the driver -- cgit v1.2.3 From 0cd8116f172eed018907303dbff5c112690eeb91 Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Wed, 6 Jun 2018 12:13:29 +0200 Subject: mtd: cfi_cmdset_0002: Fix unlocking requests crossing a chip boudary The "sector is in requested range" test used to determine whether sectors should be re-locked or not is done on a variable that is reset everytime we cross a chip boundary, which can lead to some blocks being re-locked while the caller expect them to be unlocked. Fix the check to make sure this cannot happen. Fixes: 1648eaaa1575 ("mtd: cfi_cmdset_0002: Support Persistent Protection Bits (PPB) locking") Cc: stable@vger.kernel.org Signed-off-by: Joakim Tjernlund Signed-off-by: Boris Brezillon --- drivers/mtd/chips/cfi_cmdset_0002.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 87f9925d25f9..9cfc2645dd93 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -2660,7 +2660,7 @@ static int __maybe_unused cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, * sectors shall be unlocked, so lets keep their locking * status at "unlocked" (locked=0) for the final re-locking. */ - if ((adr < ofs) || (adr >= (ofs + len))) { + if ((offset < ofs) || (offset >= (ofs + len))) { sect[sectors].chip = &cfi->chips[chipnum]; sect[sectors].adr = adr; sect[sectors].locked = do_ppb_xxlock( -- cgit v1.2.3 From 24bb555e6e46d96e2a954aa0295029a81cc9bbaa Mon Sep 17 00:00:00 2001 From: Aaron Ma Date: Thu, 21 Jun 2018 17:14:01 -0700 Subject: Input: elantech - enable middle button of touchpads on ThinkPad P52 PNPID is better way to identify the type of touchpads. Enable middle button support on 2 types of touchpads on Lenovo P52. Cc: stable@vger.kernel.org Signed-off-by: Aaron Ma Reviewed-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elantech.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index fb4d902c4403..4c52e43c9273 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1175,6 +1175,12 @@ static const struct dmi_system_id elantech_dmi_has_middle_button[] = { { } }; +static const char * const middle_button_pnp_ids[] = { + "LEN2131", /* ThinkPad P52 w/ NFC */ + "LEN2132", /* ThinkPad P52 */ + NULL +}; + /* * Set the appropriate event bits for the input subsystem */ @@ -1194,7 +1200,8 @@ static int elantech_set_input_params(struct psmouse *psmouse) __clear_bit(EV_REL, dev->evbit); __set_bit(BTN_LEFT, dev->keybit); - if (dmi_check_system(elantech_dmi_has_middle_button)) + if (dmi_check_system(elantech_dmi_has_middle_button) || + psmouse_matches_pnp_id(psmouse, middle_button_pnp_ids)) __set_bit(BTN_MIDDLE, dev->keybit); __set_bit(BTN_RIGHT, dev->keybit); -- cgit v1.2.3 From e0ae2519ca004a628fa55aeef969c37edce522d3 Mon Sep 17 00:00:00 2001 From: ??? Date: Thu, 21 Jun 2018 17:15:32 -0700 Subject: Input: elantech - fix V4 report decoding for module with middle key Some touchpad has middle key and it will be indicated in bit 2 of packet[0]. We need to fix V4 formation's byte mask to prevent error decoding. Signed-off-by: KT Liao Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elantech.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 4c52e43c9273..dd85b16dc6f8 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -799,7 +799,7 @@ static int elantech_packet_check_v4(struct psmouse *psmouse) else if (ic_version == 7 && etd->info.samples[1] == 0x2A) sanity_check = ((packet[3] & 0x1c) == 0x10); else - sanity_check = ((packet[0] & 0x0c) == 0x04 && + sanity_check = ((packet[0] & 0x08) == 0x00 && (packet[3] & 0x1c) == 0x10); if (!sanity_check) -- cgit v1.2.3 From 8938fc7b8fe9ccfa11751ead502a8d385b607967 Mon Sep 17 00:00:00 2001 From: Alexandr Savca Date: Thu, 21 Jun 2018 17:12:54 -0700 Subject: Input: elan_i2c - add ELAN0618 (Lenovo v330 15IKB) ACPI ID Add ELAN0618 to the list of supported touchpads; this ID is used in Lenovo v330 15IKB devices. Signed-off-by: Alexandr Savca Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elan_i2c_core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 8ff75114e762..f69d21610166 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -1345,6 +1345,7 @@ static const struct acpi_device_id elan_acpi_id[] = { { "ELAN060C", 0 }, { "ELAN0611", 0 }, { "ELAN0612", 0 }, + { "ELAN0618", 0 }, { "ELAN1000", 0 }, { } }; -- cgit v1.2.3 From 50fc7b61959af4b95fafce7fe5dd565199e0b61a Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 19 Jun 2018 11:17:32 -0700 Subject: Input: elan_i2c_smbus - fix more potential stack buffer overflows Commit 40f7090bb1b4 ("Input: elan_i2c_smbus - fix corrupted stack") fixed most of the functions using i2c_smbus_read_block_data() to allocate a buffer with the maximum block size. However three functions were left unchanged: * In elan_smbus_initialize(), increase the buffer size in the same way. * In elan_smbus_calibrate_result(), the buffer is provided by the caller (calibrate_store()), so introduce a bounce buffer. Also name the result buffer size. * In elan_smbus_get_report(), the buffer is provided by the caller but happens to be the right length. Add a compile-time assertion to ensure this remains the case. Cc: # 3.19+ Signed-off-by: Ben Hutchings Reviewed-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elan_i2c.h | 2 ++ drivers/input/mouse/elan_i2c_core.c | 2 +- drivers/input/mouse/elan_i2c_smbus.c | 10 ++++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h index 599544c1a91c..243e0fa6e3e3 100644 --- a/drivers/input/mouse/elan_i2c.h +++ b/drivers/input/mouse/elan_i2c.h @@ -27,6 +27,8 @@ #define ETP_DISABLE_POWER 0x0001 #define ETP_PRESSURE_OFFSET 25 +#define ETP_CALIBRATE_MAX_LEN 3 + /* IAP Firmware handling */ #define ETP_PRODUCT_ID_FORMAT_STRING "%d.0" #define ETP_FW_NAME "elan_i2c_" ETP_PRODUCT_ID_FORMAT_STRING ".bin" diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index f69d21610166..1f9cd7d8b7ad 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -613,7 +613,7 @@ static ssize_t calibrate_store(struct device *dev, int tries = 20; int retval; int error; - u8 val[3]; + u8 val[ETP_CALIBRATE_MAX_LEN]; retval = mutex_lock_interruptible(&data->sysfs_mutex); if (retval) diff --git a/drivers/input/mouse/elan_i2c_smbus.c b/drivers/input/mouse/elan_i2c_smbus.c index cfcb32559925..c060d270bc4d 100644 --- a/drivers/input/mouse/elan_i2c_smbus.c +++ b/drivers/input/mouse/elan_i2c_smbus.c @@ -56,7 +56,7 @@ static int elan_smbus_initialize(struct i2c_client *client) { u8 check[ETP_SMBUS_HELLOPACKET_LEN] = { 0x55, 0x55, 0x55, 0x55, 0x55 }; - u8 values[ETP_SMBUS_HELLOPACKET_LEN] = { 0, 0, 0, 0, 0 }; + u8 values[I2C_SMBUS_BLOCK_MAX] = {0}; int len, error; /* Get hello packet */ @@ -117,12 +117,16 @@ static int elan_smbus_calibrate(struct i2c_client *client) static int elan_smbus_calibrate_result(struct i2c_client *client, u8 *val) { int error; + u8 buf[I2C_SMBUS_BLOCK_MAX] = {0}; + + BUILD_BUG_ON(ETP_CALIBRATE_MAX_LEN > sizeof(buf)); error = i2c_smbus_read_block_data(client, - ETP_SMBUS_CALIBRATE_QUERY, val); + ETP_SMBUS_CALIBRATE_QUERY, buf); if (error < 0) return error; + memcpy(val, buf, ETP_CALIBRATE_MAX_LEN); return 0; } @@ -472,6 +476,8 @@ static int elan_smbus_get_report(struct i2c_client *client, u8 *report) { int len; + BUILD_BUG_ON(I2C_SMBUS_BLOCK_MAX > ETP_SMBUS_REPORT_LEN); + len = i2c_smbus_read_block_data(client, ETP_SMBUS_PACKET_QUERY, &report[ETP_SMBUS_REPORT_OFFSET]); -- cgit v1.2.3 From dd6bee81c942c0ea01030da9356026afb88f9d18 Mon Sep 17 00:00:00 2001 From: Enno Boland Date: Tue, 19 Jun 2018 11:55:33 -0700 Subject: Input: xpad - fix GPD Win 2 controller name This fixes using the controller with SDL2. SDL2 has a naive algorithm to apply the correct settings to a controller. For X-Box compatible controllers it expects that the controller name contains a variation of a 'XBOX'-string. This patch changes the identifier to contain "X-Box" as substring. Tested with Steam and C-Dogs-SDL which both detect the controller properly after adding this patch. Fixes: c1ba08390a8b ("Input: xpad - add GPD Win 2 Controller USB IDs") Cc: stable@vger.kernel.org Signed-off-by: Enno Boland Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 48e36acbeb49..cd620e009bad 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -125,7 +125,7 @@ static const struct xpad_device { u8 mapping; u8 xtype; } xpad_device[] = { - { 0x0079, 0x18d4, "GPD Win 2 Controller", 0, XTYPE_XBOX360 }, + { 0x0079, 0x18d4, "GPD Win 2 X-Box Controller", 0, XTYPE_XBOX360 }, { 0x044f, 0x0f00, "Thrustmaster Wheel", 0, XTYPE_XBOX }, { 0x044f, 0x0f03, "Thrustmaster Wheel", 0, XTYPE_XBOX }, { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX }, -- cgit v1.2.3 From 40141bb480a2c7805e8f4faec14ade90121ecb06 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 21 Jun 2018 18:16:45 +0100 Subject: net: ethernet: ti: davinci_cpdma: make function cpdma_desc_pool_create static The function cpdma_desc_pool_create is local to the source and does not need to be in global scope, so make it static. Cleans up sparse warning: warning: symbol 'cpdma_desc_pool_create' was not declared. Should it be static? Signed-off-by: Colin Ian King Reviewed-by: Grygorii Strashko Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/davinci_cpdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index cdbddf16dd29..4f1267477aa4 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -205,7 +205,7 @@ static void cpdma_desc_pool_destroy(struct cpdma_ctlr *ctlr) * devices (e.g. cpsw switches) use plain old memory. Descriptor pools * abstract out these details */ -int cpdma_desc_pool_create(struct cpdma_ctlr *ctlr) +static int cpdma_desc_pool_create(struct cpdma_ctlr *ctlr) { struct cpdma_params *cpdma_params = &ctlr->params; struct cpdma_desc_pool *pool; -- cgit v1.2.3 From c51b3c639e01f20559531eef3c5919feae23c55a Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Mon, 18 Jun 2018 09:36:39 +0200 Subject: xen: add new hypercall buffer mapping device For passing arbitrary data from user land to the Xen hypervisor the Xen tools today are using mlock()ed buffers. Unfortunately the kernel might change access rights of such buffers for brief periods of time e.g. for page migration or compaction, leading to access faults in the hypervisor, as the hypervisor can't use the locks of the kernel. In order to solve this problem add a new device node to the Xen privcmd driver to easily allocate hypercall buffers via mmap(). The memory is allocated in the kernel and just mapped into user space. Marked as VM_IO the user mapping will not be subject to page migration et al. Signed-off-by: Juergen Gross Reviewed-by: Boris Ostrovsky Signed-off-by: Juergen Gross --- drivers/xen/Makefile | 2 +- drivers/xen/privcmd-buf.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/xen/privcmd.c | 9 ++ drivers/xen/privcmd.h | 3 + 4 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 drivers/xen/privcmd-buf.c (limited to 'drivers') diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 451e833f5931..48b154276179 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -41,4 +41,4 @@ obj-$(CONFIG_XEN_PVCALLS_FRONTEND) += pvcalls-front.o xen-evtchn-y := evtchn.o xen-gntdev-y := gntdev.o xen-gntalloc-y := gntalloc.o -xen-privcmd-y := privcmd.o +xen-privcmd-y := privcmd.o privcmd-buf.o diff --git a/drivers/xen/privcmd-buf.c b/drivers/xen/privcmd-buf.c new file mode 100644 index 000000000000..df1ed37c3269 --- /dev/null +++ b/drivers/xen/privcmd-buf.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +/****************************************************************************** + * privcmd-buf.c + * + * Mmap of hypercall buffers. + * + * Copyright (c) 2018 Juergen Gross + */ + +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include "privcmd.h" + +MODULE_LICENSE("GPL"); + +static unsigned int limit = 64; +module_param(limit, uint, 0644); +MODULE_PARM_DESC(limit, "Maximum number of pages that may be allocated by " + "the privcmd-buf device per open file"); + +struct privcmd_buf_private { + struct mutex lock; + struct list_head list; + unsigned int allocated; +}; + +struct privcmd_buf_vma_private { + struct privcmd_buf_private *file_priv; + struct list_head list; + unsigned int users; + unsigned int n_pages; + struct page *pages[]; +}; + +static int privcmd_buf_open(struct inode *ino, struct file *file) +{ + struct privcmd_buf_private *file_priv; + + file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); + if (!file_priv) + return -ENOMEM; + + mutex_init(&file_priv->lock); + INIT_LIST_HEAD(&file_priv->list); + + file->private_data = file_priv; + + return 0; +} + +static void privcmd_buf_vmapriv_free(struct privcmd_buf_vma_private *vma_priv) +{ + unsigned int i; + + vma_priv->file_priv->allocated -= vma_priv->n_pages; + + list_del(&vma_priv->list); + + for (i = 0; i < vma_priv->n_pages; i++) + if (vma_priv->pages[i]) + __free_page(vma_priv->pages[i]); + + kfree(vma_priv); +} + +static int privcmd_buf_release(struct inode *ino, struct file *file) +{ + struct privcmd_buf_private *file_priv = file->private_data; + struct privcmd_buf_vma_private *vma_priv; + + mutex_lock(&file_priv->lock); + + while (!list_empty(&file_priv->list)) { + vma_priv = list_first_entry(&file_priv->list, + struct privcmd_buf_vma_private, + list); + privcmd_buf_vmapriv_free(vma_priv); + } + + mutex_unlock(&file_priv->lock); + + kfree(file_priv); + + return 0; +} + +static void privcmd_buf_vma_open(struct vm_area_struct *vma) +{ + struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data; + + if (!vma_priv) + return; + + mutex_lock(&vma_priv->file_priv->lock); + vma_priv->users++; + mutex_unlock(&vma_priv->file_priv->lock); +} + +static void privcmd_buf_vma_close(struct vm_area_struct *vma) +{ + struct privcmd_buf_vma_private *vma_priv = vma->vm_private_data; + struct privcmd_buf_private *file_priv; + + if (!vma_priv) + return; + + file_priv = vma_priv->file_priv; + + mutex_lock(&file_priv->lock); + + vma_priv->users--; + if (!vma_priv->users) + privcmd_buf_vmapriv_free(vma_priv); + + mutex_unlock(&file_priv->lock); +} + +static vm_fault_t privcmd_buf_vma_fault(struct vm_fault *vmf) +{ + pr_debug("fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n", + vmf->vma, vmf->vma->vm_start, vmf->vma->vm_end, + vmf->pgoff, (void *)vmf->address); + + return VM_FAULT_SIGBUS; +} + +static const struct vm_operations_struct privcmd_buf_vm_ops = { + .open = privcmd_buf_vma_open, + .close = privcmd_buf_vma_close, + .fault = privcmd_buf_vma_fault, +}; + +static int privcmd_buf_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct privcmd_buf_private *file_priv = file->private_data; + struct privcmd_buf_vma_private *vma_priv; + unsigned long count = vma_pages(vma); + unsigned int i; + int ret = 0; + + if (!(vma->vm_flags & VM_SHARED) || count > limit || + file_priv->allocated + count > limit) + return -EINVAL; + + vma_priv = kzalloc(sizeof(*vma_priv) + count * sizeof(void *), + GFP_KERNEL); + if (!vma_priv) + return -ENOMEM; + + vma_priv->n_pages = count; + count = 0; + for (i = 0; i < vma_priv->n_pages; i++) { + vma_priv->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!vma_priv->pages[i]) + break; + count++; + } + + mutex_lock(&file_priv->lock); + + file_priv->allocated += count; + + vma_priv->file_priv = file_priv; + vma_priv->users = 1; + + vma->vm_flags |= VM_IO | VM_DONTEXPAND; + vma->vm_ops = &privcmd_buf_vm_ops; + vma->vm_private_data = vma_priv; + + list_add(&vma_priv->list, &file_priv->list); + + if (vma_priv->n_pages != count) + ret = -ENOMEM; + else + for (i = 0; i < vma_priv->n_pages; i++) { + ret = vm_insert_page(vma, vma->vm_start + i * PAGE_SIZE, + vma_priv->pages[i]); + if (ret) + break; + } + + if (ret) + privcmd_buf_vmapriv_free(vma_priv); + + mutex_unlock(&file_priv->lock); + + return ret; +} + +const struct file_operations xen_privcmdbuf_fops = { + .owner = THIS_MODULE, + .open = privcmd_buf_open, + .release = privcmd_buf_release, + .mmap = privcmd_buf_mmap, +}; +EXPORT_SYMBOL_GPL(xen_privcmdbuf_fops); + +struct miscdevice xen_privcmdbuf_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "xen/hypercall", + .fops = &xen_privcmdbuf_fops, +}; diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index 8ae0349d9f0a..7e6e682104dc 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -1007,12 +1007,21 @@ static int __init privcmd_init(void) pr_err("Could not register Xen privcmd device\n"); return err; } + + err = misc_register(&xen_privcmdbuf_dev); + if (err != 0) { + pr_err("Could not register Xen hypercall-buf device\n"); + misc_deregister(&privcmd_dev); + return err; + } + return 0; } static void __exit privcmd_exit(void) { misc_deregister(&privcmd_dev); + misc_deregister(&xen_privcmdbuf_dev); } module_init(privcmd_init); diff --git a/drivers/xen/privcmd.h b/drivers/xen/privcmd.h index 14facaeed36f..0dd9f8f67ee3 100644 --- a/drivers/xen/privcmd.h +++ b/drivers/xen/privcmd.h @@ -1,3 +1,6 @@ #include extern const struct file_operations xen_privcmd_fops; +extern const struct file_operations xen_privcmdbuf_fops; + +extern struct miscdevice xen_privcmdbuf_dev; -- cgit v1.2.3 From eef04c7b3786ff0c9cb1019278b6c6c2ea0ad4ff Mon Sep 17 00:00:00 2001 From: Boris Ostrovsky Date: Thu, 21 Jun 2018 13:29:44 -0400 Subject: xen: Remove unnecessary BUG_ON from __unbind_from_irq() Commit 910f8befdf5b ("xen/pirq: fix error path cleanup when binding MSIs") fixed a couple of errors in error cleanup path of xen_bind_pirq_msi_to_irq(). This cleanup allowed a call to __unbind_from_irq() with an unbound irq, which would result in triggering the BUG_ON there. Since there is really no reason for the BUG_ON (xen_free_irq() can operate on unbound irqs) we can remove it. Reported-by: Ben Hutchings Signed-off-by: Boris Ostrovsky Cc: stable@vger.kernel.org Reviewed-by: Juergen Gross Signed-off-by: Juergen Gross --- drivers/xen/events/events_base.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c index 762378f1811c..08e4af04d6f2 100644 --- a/drivers/xen/events/events_base.c +++ b/drivers/xen/events/events_base.c @@ -628,8 +628,6 @@ static void __unbind_from_irq(unsigned int irq) xen_irq_info_cleanup(info); } - BUG_ON(info_for_irq(irq)->type == IRQT_UNBOUND); - xen_free_irq(irq); } -- cgit v1.2.3 From f1ce87f6080b1dda7e7b1eda3da332add19d87b9 Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Wed, 6 Jun 2018 12:13:30 +0200 Subject: mtd: cfi_cmdset_0002: Avoid walking all chips when unlocking. cfi_ppb_unlock() walks all flash chips when unlocking sectors, avoid walking chips unaffected by the unlock operation. Fixes: 1648eaaa1575 ("mtd: cfi_cmdset_0002: Support Persistent Protection Bits (PPB) locking") Cc: stable@vger.kernel.org Signed-off-by: Joakim Tjernlund Signed-off-by: Boris Brezillon --- drivers/mtd/chips/cfi_cmdset_0002.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 9cfc2645dd93..1b64ac8c5bc8 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -2676,6 +2676,8 @@ static int __maybe_unused cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, i++; if (adr >> cfi->chipshift) { + if (offset >= (ofs + len)) + break; adr = 0; chipnum++; -- cgit v1.2.3 From cbdceb9b3e1928554fffd0d889adf2d0d8edee4d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 22 Jun 2018 09:04:25 +0200 Subject: mtd: dataflash: Use ULL suffix for 64-bit constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With gcc 4.1.2 when compiling for 32-bit: drivers/mtd/devices/mtd_dataflash.c:736: warning: integer constant is too large for ‘long’ type drivers/mtd/devices/mtd_dataflash.c:737: warning: integer constant is too large for ‘long’ type Add the missing "ULL" suffixes to fix this. Fixes: 67e4145ebf2c161d ("mtd: dataflash: Add flash_info for AT45DB641E") Signed-off-by: Geert Uytterhoeven Acked-by: Andrey Smirnov Signed-off-by: Boris Brezillon --- drivers/mtd/devices/mtd_dataflash.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 3a6f450d1093..53febe8a68c3 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -733,8 +733,8 @@ static struct flash_info dataflash_data[] = { { "AT45DB642x", 0x1f2800, 8192, 1056, 11, SUP_POW2PS}, { "at45db642d", 0x1f2800, 8192, 1024, 10, SUP_POW2PS | IS_POW2PS}, - { "AT45DB641E", 0x1f28000100, 32768, 264, 9, SUP_EXTID | SUP_POW2PS}, - { "at45db641e", 0x1f28000100, 32768, 256, 8, SUP_EXTID | SUP_POW2PS | IS_POW2PS}, + { "AT45DB641E", 0x1f28000100ULL, 32768, 264, 9, SUP_EXTID | SUP_POW2PS}, + { "at45db641e", 0x1f28000100ULL, 32768, 256, 8, SUP_EXTID | SUP_POW2PS | IS_POW2PS}, }; static struct flash_info *jedec_lookup(struct spi_device *spi, -- cgit v1.2.3 From 52e1cf2d19c2e62e6a81b8de3f7320d033917dd5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 22 Jun 2018 08:42:22 +0200 Subject: efi/libstub/tpm: Initialize efi_physical_addr_t vars to zero for mixed mode Commit: 79832f0b5f71 ("efi/libstub/tpm: Initialize pointer variables to zero for mixed mode") fixes a problem with the tpm code on mixed mode (64-bit kernel on 32-bit UEFI), where 64-bit pointer variables are not fully initialized by the 32-bit EFI code. A similar problem applies to the efi_physical_addr_t variables which are written by the ->get_event_log() EFI call. Even though efi_physical_addr_t is 64-bit everywhere, it seems that some 32-bit UEFI implementations only fill in the lower 32 bits when passed a pointer to an efi_physical_addr_t to fill. This commit initializes these to 0 to, to ensure the upper 32 bits are 0 in mixed mode. This fixes recent kernels sometimes hanging during early boot on mixed mode UEFI systems. Signed-off-by: Hans de Goede Signed-off-by: Ard Biesheuvel Cc: # v4.16+ Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: linux-efi@vger.kernel.org Link: http://lkml.kernel.org/r/20180622064222.11633-2-ard.biesheuvel@linaro.org Signed-off-by: Ingo Molnar --- drivers/firmware/efi/libstub/tpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/tpm.c b/drivers/firmware/efi/libstub/tpm.c index caa37a6dd9d4..a90b0b8fc69a 100644 --- a/drivers/firmware/efi/libstub/tpm.c +++ b/drivers/firmware/efi/libstub/tpm.c @@ -64,7 +64,7 @@ static void efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t *sys_table_arg) efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID; efi_guid_t linux_eventlog_guid = LINUX_EFI_TPM_EVENT_LOG_GUID; efi_status_t status; - efi_physical_addr_t log_location, log_last_entry; + efi_physical_addr_t log_location = 0, log_last_entry = 0; struct linux_efi_tpm_eventlog *log_tbl = NULL; unsigned long first_entry_addr, last_entry_addr; size_t log_size, last_entry_size; -- cgit v1.2.3 From 893fbfff976cd069f2e60c3b186dbe3f85504db2 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 22 Jun 2018 10:52:49 +0100 Subject: irqchip/ls-scfg-msi: Fix MSI affinity handling The ls-scfs-msi driver is not dealing with the effective affinity as it should. Let's fix that, and make it clear that the effective affinity is restricted to a single CPU. Also prevent the driver from messing with the internals of the affinity setting infrastructure. Reported-by: Alexandre Belloni Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Tested-by: Alexandre Belloni Cc: Jason Cooper Cc: Yang Yingliang Cc: Sumit Garg Link: https://lkml.kernel.org/r/20180622095254.5906-3-marc.zyngier@arm.com --- drivers/irqchip/irq-ls-scfg-msi.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-ls-scfg-msi.c b/drivers/irqchip/irq-ls-scfg-msi.c index 1ec3bfe56693..c671b3212010 100644 --- a/drivers/irqchip/irq-ls-scfg-msi.c +++ b/drivers/irqchip/irq-ls-scfg-msi.c @@ -93,8 +93,12 @@ static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) msg->address_lo = lower_32_bits(msi_data->msiir_addr); msg->data = data->hwirq; - if (msi_affinity_flag) - msg->data |= cpumask_first(data->common->affinity); + if (msi_affinity_flag) { + const struct cpumask *mask; + + mask = irq_data_get_effective_affinity_mask(data); + msg->data |= cpumask_first(mask); + } iommu_dma_map_msi_msg(data->irq, msg); } @@ -121,7 +125,7 @@ static int ls_scfg_msi_set_affinity(struct irq_data *irq_data, return -EINVAL; } - cpumask_copy(irq_data->common->affinity, mask); + irq_data_update_effective_affinity(irq_data, cpumask_of(cpu)); return IRQ_SET_MASK_OK; } -- cgit v1.2.3 From cbaf45a6be497c272e80500e4fd9bccdf20d5050 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 22 Jun 2018 10:52:50 +0100 Subject: irqchip/gic-v2m: Fix SPI release on error path On failing to allocate the required SPIs, the actual number of interrupts should be freed and not its log2 value. Fixes: de337ee30142 ("irqchip/gic-v2m: Add PCI Multi-MSI support") Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Cc: Jason Cooper Cc: Alexandre Belloni Cc: Yang Yingliang Cc: Sumit Garg Link: https://lkml.kernel.org/r/20180622095254.5906-4-marc.zyngier@arm.com --- drivers/irqchip/irq-gic-v2m.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index 0f52d44b3f69..f5fe0100f9ff 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -199,7 +199,7 @@ static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, fail: irq_domain_free_irqs_parent(domain, virq, nr_irqs); - gicv2m_unalloc_msi(v2m, hwirq, get_count_order(nr_irqs)); + gicv2m_unalloc_msi(v2m, hwirq, nr_irqs); return err; } -- cgit v1.2.3 From c1797b11a09c8323c92b074fd48b89a936c991d0 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Fri, 22 Jun 2018 10:52:51 +0100 Subject: irqchip/gic-v3-its: Don't bind LPI to unavailable NUMA node On a NUMA system, if an ITS is local to an offline node, the ITS driver may pick an offline CPU to bind the LPI. In this case, pick an online CPU (and the first one will do). But on some systems, binding an LPI to non-local node CPU may cause deadlock (see Cavium erratum 23144). In this case, just fail the activate and return an error code. Signed-off-by: Yang Yingliang Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Cc: Jason Cooper Cc: Alexandre Belloni Cc: Sumit Garg Cc: stable@vger.kernel.org Link: https://lkml.kernel.org/r/20180622095254.5906-5-marc.zyngier@arm.com --- drivers/irqchip/irq-gic-v3-its.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 5377d7e2afba..cae53937feeb 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -2310,7 +2310,14 @@ static int its_irq_domain_activate(struct irq_domain *domain, cpu_mask = cpumask_of_node(its_dev->its->numa_node); /* Bind the LPI to the first possible CPU */ - cpu = cpumask_first(cpu_mask); + cpu = cpumask_first_and(cpu_mask, cpu_online_mask); + if (cpu >= nr_cpu_ids) { + if (its_dev->its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144) + return -EINVAL; + + cpu = cpumask_first(cpu_online_mask); + } + its_dev->event_map.col_map[event] = cpu; irq_data_update_effective_affinity(d, cpumask_of(cpu)); -- cgit v1.2.3 From 83559b47cdc4d396fc1187a13b527d01b55e0fe6 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 22 Jun 2018 10:52:52 +0100 Subject: irqchip/gic-v3-its: Only emit SYNC if targetting a valid collection It is possible, under obscure circumstances, to convince the ITS driver to emit a SYNC operation that targets a collection that is not bound to any redistributor (and the target_address field is zero) because the corresponding CPU has not been seen yet (the system has been booted with max_cpus="something small"). If the ITS is using the linear CPU number as the target, this is not a big deal, as we just end-up issuing a SYNC to CPU0. But if the ITS requires the physical address of the redistributor (with GITS_TYPER.PTA==1), we end-up asking the ITS to write to the physical address zero, which is not exactly a good idea (there has been report of the ITS locking up). This should of course never happen, but hey, this is SW... In order to avoid the above disaster, let's track which collections have been actually initialized, and let's not generate a SYNC if the collection hasn't been properly bound to a redistributor. Take this opportunity to spit our a warning, in the hope that someone may report the issue if it arrises again. Reported-by: Yang Yingliang Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Cc: Jason Cooper Cc: Alexandre Belloni Cc: Sumit Garg Link: https://lkml.kernel.org/r/20180622095254.5906-6-marc.zyngier@arm.com --- drivers/irqchip/irq-gic-v3-its.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index cae53937feeb..fcfc96f8e0de 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -182,6 +182,14 @@ static struct its_collection *dev_event_to_col(struct its_device *its_dev, return its->collections + its_dev->event_map.col_map[event]; } +static struct its_collection *valid_col(struct its_collection *col) +{ + if (WARN_ON_ONCE(col->target_address & GENMASK_ULL(0, 15))) + return NULL; + + return col; +} + /* * ITS command descriptors - parameters to be encoded in a command * block. @@ -439,7 +447,7 @@ static struct its_collection *its_build_mapti_cmd(struct its_node *its, its_fixup_cmd(cmd); - return col; + return valid_col(col); } static struct its_collection *its_build_movi_cmd(struct its_node *its, @@ -458,7 +466,7 @@ static struct its_collection *its_build_movi_cmd(struct its_node *its, its_fixup_cmd(cmd); - return col; + return valid_col(col); } static struct its_collection *its_build_discard_cmd(struct its_node *its, @@ -476,7 +484,7 @@ static struct its_collection *its_build_discard_cmd(struct its_node *its, its_fixup_cmd(cmd); - return col; + return valid_col(col); } static struct its_collection *its_build_inv_cmd(struct its_node *its, @@ -494,7 +502,7 @@ static struct its_collection *its_build_inv_cmd(struct its_node *its, its_fixup_cmd(cmd); - return col; + return valid_col(col); } static struct its_collection *its_build_int_cmd(struct its_node *its, @@ -512,7 +520,7 @@ static struct its_collection *its_build_int_cmd(struct its_node *its, its_fixup_cmd(cmd); - return col; + return valid_col(col); } static struct its_collection *its_build_clear_cmd(struct its_node *its, @@ -530,7 +538,7 @@ static struct its_collection *its_build_clear_cmd(struct its_node *its, its_fixup_cmd(cmd); - return col; + return valid_col(col); } static struct its_collection *its_build_invall_cmd(struct its_node *its, @@ -1824,11 +1832,16 @@ static int its_alloc_tables(struct its_node *its) static int its_alloc_collections(struct its_node *its) { + int i; + its->collections = kcalloc(nr_cpu_ids, sizeof(*its->collections), GFP_KERNEL); if (!its->collections) return -ENOMEM; + for (i = 0; i < nr_cpu_ids; i++) + its->collections[i].target_address = ~0ULL; + return 0; } -- cgit v1.2.3 From 205e065d91d72e6afad112ea84f0ca60b30bf5ab Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 22 Jun 2018 10:52:53 +0100 Subject: irqchip/gic-v3-its: Only emit VSYNC if targetting a valid collection Similarily to the SYNC operation, it must be verified that the VPE targetted by a VLPI is backed by a valid collection in the GIC driver data structures. Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Cc: Jason Cooper Cc: Alexandre Belloni Cc: Yang Yingliang Cc: Sumit Garg Link: https://lkml.kernel.org/r/20180622095254.5906-7-marc.zyngier@arm.com --- drivers/irqchip/irq-gic-v3-its.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index fcfc96f8e0de..0269ffb93f6e 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -190,6 +190,14 @@ static struct its_collection *valid_col(struct its_collection *col) return col; } +static struct its_vpe *valid_vpe(struct its_node *its, struct its_vpe *vpe) +{ + if (valid_col(its->collections + vpe->col_idx)) + return vpe; + + return NULL; +} + /* * ITS command descriptors - parameters to be encoded in a command * block. @@ -562,7 +570,7 @@ static struct its_vpe *its_build_vinvall_cmd(struct its_node *its, its_fixup_cmd(cmd); - return desc->its_vinvall_cmd.vpe; + return valid_vpe(its, desc->its_vinvall_cmd.vpe); } static struct its_vpe *its_build_vmapp_cmd(struct its_node *its, @@ -584,7 +592,7 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its, its_fixup_cmd(cmd); - return desc->its_vmapp_cmd.vpe; + return valid_vpe(its, desc->its_vmapp_cmd.vpe); } static struct its_vpe *its_build_vmapti_cmd(struct its_node *its, @@ -607,7 +615,7 @@ static struct its_vpe *its_build_vmapti_cmd(struct its_node *its, its_fixup_cmd(cmd); - return desc->its_vmapti_cmd.vpe; + return valid_vpe(its, desc->its_vmapti_cmd.vpe); } static struct its_vpe *its_build_vmovi_cmd(struct its_node *its, @@ -630,7 +638,7 @@ static struct its_vpe *its_build_vmovi_cmd(struct its_node *its, its_fixup_cmd(cmd); - return desc->its_vmovi_cmd.vpe; + return valid_vpe(its, desc->its_vmovi_cmd.vpe); } static struct its_vpe *its_build_vmovp_cmd(struct its_node *its, @@ -648,7 +656,7 @@ static struct its_vpe *its_build_vmovp_cmd(struct its_node *its, its_fixup_cmd(cmd); - return desc->its_vmovp_cmd.vpe; + return valid_vpe(its, desc->its_vmovp_cmd.vpe); } static u64 its_cmd_ptr_to_offset(struct its_node *its, -- cgit v1.2.3 From 82f499c8811149069ec958b72a86643a7a289b25 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 22 Jun 2018 10:52:54 +0100 Subject: irqchip/gic-v3-its: Fix reprogramming of redistributors on CPU hotplug Enabling LPIs was made a lot stricter recently, by checking that they are disabled before enabling them. By doing so, the CPU hotplug case was missed altogether, which leaves LPIs enabled on hotplug off (expecting the CPU to eventually come back), and won't write a different value anyway on hotplug on. So skip that check if that particular case is detected Fixes: 6eb486b66a30 ("irqchip/gic-v3: Ensure GICR_CTLR.EnableLPI=0 is observed before enabling") Reported-by: Sumit Garg Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Tested-by: Sumit Garg Cc: Jason Cooper Cc: Alexandre Belloni Cc: Yang Yingliang Link: https://lkml.kernel.org/r/20180622095254.5906-8-marc.zyngier@arm.com --- drivers/irqchip/irq-gic-v3-its.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 0269ffb93f6e..d7842d312d3e 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -3427,6 +3427,16 @@ static int redist_disable_lpis(void) u64 timeout = USEC_PER_SEC; u64 val; + /* + * If coming via a CPU hotplug event, we don't need to disable + * LPIs before trying to re-enable them. They are already + * configured and all is well in the world. Detect this case + * by checking the allocation of the pending table for the + * current CPU. + */ + if (gic_data_rdist()->pend_page) + return 0; + if (!gic_rdists_supports_plpis()) { pr_info("CPU%d: LPIs not supported\n", smp_processor_id()); return -ENXIO; -- cgit v1.2.3 From 3f6e6986045d47f87bd982910821b7ab9758487e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 23 Jun 2018 01:06:34 +0900 Subject: mtd: rawnand: denali_dt: set clk_x_rate to 200 MHz unconditionally Since commit 1bb88666775e ("mtd: nand: denali: handle timing parameters by setup_data_interface()"), denali_dt.c gets the clock rate from the clock driver. The driver expects the frequency of the bus interface clock, whereas the clock driver of SOCFPGA provides the core clock. Thus, the setup_data_interface() hook calculates timing parameters based on a wrong frequency. To make it work without relying on the clock driver, hard-code the clock frequency, 200MHz. This is fine for existing DT of UniPhier, and also fixes the issue of SOCFPGA because both platforms use 200 MHz for the bus interface clock. Fixes: 1bb88666775e ("mtd: nand: denali: handle timing parameters by setup_data_interface()") Cc: linux-stable #4.14+ Reported-by: Philipp Rosenberger Suggested-by: Boris Brezillon Signed-off-by: Masahiro Yamada Tested-by: Richard Weinberger Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/denali_dt.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c index cfd33e6ca77f..5869e90cc14b 100644 --- a/drivers/mtd/nand/raw/denali_dt.c +++ b/drivers/mtd/nand/raw/denali_dt.c @@ -123,7 +123,11 @@ static int denali_dt_probe(struct platform_device *pdev) if (ret) return ret; - denali->clk_x_rate = clk_get_rate(dt->clk); + /* + * Hardcode the clock rate for the backward compatibility. + * This works for both SOCFPGA and UniPhier. + */ + denali->clk_x_rate = 200000000; ret = denali_init(denali); if (ret) -- cgit v1.2.3 From 0ae52ddf5bd7f685bb43d7687290f6c2eeacfb31 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 22 Jun 2018 13:05:35 +0200 Subject: lightnvm: Remove depends on HAS_DMA in case of platform dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove dependencies on HAS_DMA where a Kconfig symbol depends on another symbol that implies HAS_DMA, and, optionally, on "|| COMPILE_TEST". In most cases this other symbol is an architecture or platform specific symbol, or PCI. Generic symbols and drivers without platform dependencies keep their dependencies on HAS_DMA, to prevent compiling subsystems or drivers that cannot work anyway. This simplifies the dependencies, and allows to improve compile-testing. Signed-off-by: Geert Uytterhoeven Reviewed-by: Mark Brown Acked-by: Robin Murphy Reviewed-by: Matias Bjørling Signed-off-by: Jens Axboe --- drivers/lightnvm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/lightnvm/Kconfig b/drivers/lightnvm/Kconfig index 10c08982185a..9c03f35d9df1 100644 --- a/drivers/lightnvm/Kconfig +++ b/drivers/lightnvm/Kconfig @@ -4,7 +4,7 @@ menuconfig NVM bool "Open-Channel SSD target support" - depends on BLOCK && HAS_DMA && PCI + depends on BLOCK && PCI select BLK_DEV_NVME help Say Y here to get to enable Open-channel SSDs. -- cgit v1.2.3 From f21c601a2bb319ec19eb4562eadc7797d90fd90e Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Fri, 15 Jun 2018 09:35:33 -0400 Subject: dm: use bio_split() when splitting out the already processed bio Use of bio_clone_bioset() is inefficient if there is no need to clone the original bio's bio_vec array. Best to use the bio_clone_fast() variant. Also, just using bio_advance() is only part of what is needed to properly setup the clone -- it doesn't account for the various bio_integrity() related work that also needs to be performed (see bio_split). Address both of these issues by switching from bio_clone_bioset() to bio_split(). Fixes: 18a25da8 ("dm: ensure bio submission follows a depth-first tree walk") Cc: stable@vger.kernel.org # 4.15+, requires removal of '&' before md->queue->bio_split Reported-by: Christoph Hellwig Reviewed-by: NeilBrown Signed-off-by: Mike Snitzer --- drivers/md/dm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/md/dm.c b/drivers/md/dm.c index e65429a29c06..a3b103e8e3ce 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1606,10 +1606,9 @@ static blk_qc_t __split_and_process_bio(struct mapped_device *md, * the usage of io->orig_bio in dm_remap_zone_report() * won't be affected by this reassignment. */ - struct bio *b = bio_clone_bioset(bio, GFP_NOIO, - &md->queue->bio_split); + struct bio *b = bio_split(bio, bio_sectors(bio) - ci.sector_count, + GFP_NOIO, &md->queue->bio_split); ci.io->orig_bio = b; - bio_advance(bio, (bio_sectors(bio) - ci.sector_count) << 9); bio_chain(b, bio); ret = generic_make_request(bio); break; -- cgit v1.2.3 From 7ccdbf85d3b2237cdbdafe6a47673dbdfab4b7a2 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Mon, 18 Jun 2018 11:57:51 -0400 Subject: dm thin metadata: remove needless work from __commit_transaction Commit 5a32083d03fb5 ("dm: take care to copy the space map roots before locking the superblock") properly removed the calls to dm_sm_root_size() from __write_initial_superblock(). But the dm_sm_root_size() calls were left dangling in __commit_transaction(). Fixes: 5a32083d03fb5 ("dm: take care to copy the space map roots before locking the superblock") Signed-off-by: Mike Snitzer --- drivers/md/dm-thin-metadata.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers') diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index 36ef284ad086..72142021b5c9 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -776,7 +776,6 @@ static int __write_changed_details(struct dm_pool_metadata *pmd) static int __commit_transaction(struct dm_pool_metadata *pmd) { int r; - size_t metadata_len, data_len; struct thin_disk_superblock *disk_super; struct dm_block *sblock; @@ -797,14 +796,6 @@ static int __commit_transaction(struct dm_pool_metadata *pmd) if (r < 0) return r; - r = dm_sm_root_size(pmd->metadata_sm, &metadata_len); - if (r < 0) - return r; - - r = dm_sm_root_size(pmd->data_sm, &data_len); - if (r < 0) - return r; - r = save_sm_roots(pmd); if (r < 0) return r; -- cgit v1.2.3 From 50a7d3ba7c9ac5e0b7e03fc7f420180989361dbf Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 18 Jun 2018 10:50:33 -0700 Subject: dm writecache: use 2-factor allocator arguments This adjusts the allocator calls to use the 2-factor argument style, as already done treewide for better defense against allocator overflows. Signed-off-by: Kees Cook [snitzer: tweaked code to leave assignment in a test alone] Signed-off-by: Mike Snitzer --- drivers/md/dm-writecache.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c index 5961c7794ef3..07ea6a48aac6 100644 --- a/drivers/md/dm-writecache.c +++ b/drivers/md/dm-writecache.c @@ -259,7 +259,7 @@ static int persistent_memory_claim(struct dm_writecache *wc) if (da != p) { long i; wc->memory_map = NULL; - pages = kvmalloc(p * sizeof(struct page *), GFP_KERNEL); + pages = kvmalloc_array(p, sizeof(struct page *), GFP_KERNEL); if (!pages) { r = -ENOMEM; goto err2; @@ -859,7 +859,7 @@ static int writecache_alloc_entries(struct dm_writecache *wc) if (wc->entries) return 0; - wc->entries = vmalloc(sizeof(struct wc_entry) * wc->n_blocks); + wc->entries = vmalloc(array_size(sizeof(struct wc_entry), wc->n_blocks)); if (!wc->entries) return -ENOMEM; for (b = 0; b < wc->n_blocks; b++) { @@ -1481,9 +1481,9 @@ static void __writecache_writeback_pmem(struct dm_writecache *wc, struct writeba wb->bio.bi_iter.bi_sector = read_original_sector(wc, e); wb->page_offset = PAGE_SIZE; if (max_pages <= WB_LIST_INLINE || - unlikely(!(wb->wc_list = kmalloc(max_pages * sizeof(struct wc_entry *), - GFP_NOIO | __GFP_NORETRY | - __GFP_NOMEMALLOC | __GFP_NOWARN)))) { + unlikely(!(wb->wc_list = kmalloc_array(max_pages, sizeof(struct wc_entry *), + GFP_NOIO | __GFP_NORETRY | + __GFP_NOMEMALLOC | __GFP_NOWARN)))) { wb->wc_list = wb->wc_list_inline; max_pages = WB_LIST_INLINE; } -- cgit v1.2.3 From 2d0b2d64d325e22939d9db3ba784f1236459ed98 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 22 Jun 2018 08:09:11 -0700 Subject: dm zoned: avoid triggering reclaim from inside dmz_map() This patch avoids that lockdep reports the following: ====================================================== WARNING: possible circular locking dependency detected 4.18.0-rc1 #62 Not tainted ------------------------------------------------------ kswapd0/84 is trying to acquire lock: 00000000c313516d (&xfs_nondir_ilock_class){++++}, at: xfs_free_eofblocks+0xa2/0x1e0 but task is already holding lock: 00000000591c83ae (fs_reclaim){+.+.}, at: __fs_reclaim_acquire+0x5/0x30 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #2 (fs_reclaim){+.+.}: kmem_cache_alloc+0x2c/0x2b0 radix_tree_node_alloc.constprop.19+0x3d/0xc0 __radix_tree_create+0x161/0x1c0 __radix_tree_insert+0x45/0x210 dmz_map+0x245/0x2d0 [dm_zoned] __map_bio+0x40/0x260 __split_and_process_non_flush+0x116/0x220 __split_and_process_bio+0x81/0x180 __dm_make_request.isra.32+0x5a/0x100 generic_make_request+0x36e/0x690 submit_bio+0x6c/0x140 mpage_readpages+0x19e/0x1f0 read_pages+0x6d/0x1b0 __do_page_cache_readahead+0x21b/0x2d0 force_page_cache_readahead+0xc4/0x100 generic_file_read_iter+0x7c6/0xd20 __vfs_read+0x102/0x180 vfs_read+0x9b/0x140 ksys_read+0x55/0xc0 do_syscall_64+0x5a/0x1f0 entry_SYSCALL_64_after_hwframe+0x49/0xbe -> #1 (&dmz->chunk_lock){+.+.}: dmz_map+0x133/0x2d0 [dm_zoned] __map_bio+0x40/0x260 __split_and_process_non_flush+0x116/0x220 __split_and_process_bio+0x81/0x180 __dm_make_request.isra.32+0x5a/0x100 generic_make_request+0x36e/0x690 submit_bio+0x6c/0x140 _xfs_buf_ioapply+0x31c/0x590 xfs_buf_submit_wait+0x73/0x520 xfs_buf_read_map+0x134/0x2f0 xfs_trans_read_buf_map+0xc3/0x580 xfs_read_agf+0xa5/0x1e0 xfs_alloc_read_agf+0x59/0x2b0 xfs_alloc_pagf_init+0x27/0x60 xfs_bmap_longest_free_extent+0x43/0xb0 xfs_bmap_btalloc_nullfb+0x7f/0xf0 xfs_bmap_btalloc+0x428/0x7c0 xfs_bmapi_write+0x598/0xcc0 xfs_iomap_write_allocate+0x15a/0x330 xfs_map_blocks+0x1cf/0x3f0 xfs_do_writepage+0x15f/0x7b0 write_cache_pages+0x1ca/0x540 xfs_vm_writepages+0x65/0xa0 do_writepages+0x48/0xf0 __writeback_single_inode+0x58/0x730 writeback_sb_inodes+0x249/0x5c0 wb_writeback+0x11e/0x550 wb_workfn+0xa3/0x670 process_one_work+0x228/0x670 worker_thread+0x3c/0x390 kthread+0x11c/0x140 ret_from_fork+0x3a/0x50 -> #0 (&xfs_nondir_ilock_class){++++}: down_read_nested+0x43/0x70 xfs_free_eofblocks+0xa2/0x1e0 xfs_fs_destroy_inode+0xac/0x270 dispose_list+0x51/0x80 prune_icache_sb+0x52/0x70 super_cache_scan+0x127/0x1a0 shrink_slab.part.47+0x1bd/0x590 shrink_node+0x3b5/0x470 balance_pgdat+0x158/0x3b0 kswapd+0x1ba/0x600 kthread+0x11c/0x140 ret_from_fork+0x3a/0x50 other info that might help us debug this: Chain exists of: &xfs_nondir_ilock_class --> &dmz->chunk_lock --> fs_reclaim Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(fs_reclaim); lock(&dmz->chunk_lock); lock(fs_reclaim); lock(&xfs_nondir_ilock_class); *** DEADLOCK *** 3 locks held by kswapd0/84: #0: 00000000591c83ae (fs_reclaim){+.+.}, at: __fs_reclaim_acquire+0x5/0x30 #1: 000000000f8208f5 (shrinker_rwsem){++++}, at: shrink_slab.part.47+0x3f/0x590 #2: 00000000cacefa54 (&type->s_umount_key#43){.+.+}, at: trylock_super+0x16/0x50 stack backtrace: CPU: 7 PID: 84 Comm: kswapd0 Not tainted 4.18.0-rc1 #62 Hardware name: Supermicro Super Server/X10SRL-F, BIOS 2.0 12/17/2015 Call Trace: dump_stack+0x85/0xcb print_circular_bug.isra.36+0x1ce/0x1db __lock_acquire+0x124e/0x1310 lock_acquire+0x9f/0x1f0 down_read_nested+0x43/0x70 xfs_free_eofblocks+0xa2/0x1e0 xfs_fs_destroy_inode+0xac/0x270 dispose_list+0x51/0x80 prune_icache_sb+0x52/0x70 super_cache_scan+0x127/0x1a0 shrink_slab.part.47+0x1bd/0x590 shrink_node+0x3b5/0x470 balance_pgdat+0x158/0x3b0 kswapd+0x1ba/0x600 kthread+0x11c/0x140 ret_from_fork+0x3a/0x50 Reported-by: Masato Suzuki Fixes: 4218a9554653 ("dm zoned: use GFP_NOIO in I/O path") Cc: Signed-off-by: Bart Van Assche Signed-off-by: Mike Snitzer --- drivers/md/dm-zoned-target.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/md/dm-zoned-target.c b/drivers/md/dm-zoned-target.c index 3c0e45f4dcf5..a44183ff4be0 100644 --- a/drivers/md/dm-zoned-target.c +++ b/drivers/md/dm-zoned-target.c @@ -787,7 +787,7 @@ static int dmz_ctr(struct dm_target *ti, unsigned int argc, char **argv) /* Chunk BIO work */ mutex_init(&dmz->chunk_lock); - INIT_RADIX_TREE(&dmz->chunk_rxtree, GFP_KERNEL); + INIT_RADIX_TREE(&dmz->chunk_rxtree, GFP_NOIO); dmz->chunk_wq = alloc_workqueue("dmz_cwq_%s", WQ_MEM_RECLAIM | WQ_UNBOUND, 0, dev->name); if (!dmz->chunk_wq) { -- cgit v1.2.3 From f2ccaa5904666f84d4e6958d6db9d1e65ead5085 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 22 Jun 2018 10:01:19 +0200 Subject: dm raid: don't use 'const' in function return A newly introduced function has 'const int' as the return type, but as "make W=1" reports, that has no meaning: drivers/md/dm-raid.c:510:18: error: type qualifiers ignored on function return type [-Werror=ignored-qualifiers] This changes the return type to plain 'int'. Signed-off-by: Arnd Bergmann Fixes: 33e53f06850f ("dm raid: introduce extended superblock and new raid types to support takeover/reshaping") Signed-off-by: Mike Snitzer Fixes: 552aa679f2657431 ("dm raid: use rs_is_raid*()") Signed-off-by: Geert Uytterhoeven Signed-off-by: Mike Snitzer --- drivers/md/dm-raid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index ab13fcec3fca..75df4c9d8b54 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -588,7 +588,7 @@ static const char *raid10_md_layout_to_format(int layout) } /* Return md raid10 algorithm for @name */ -static const int raid10_name_to_format(const char *name) +static int raid10_name_to_format(const char *name) { if (!strcasecmp(name, "near")) return ALGORITHM_RAID10_NEAR; -- cgit v1.2.3 From fe2a19652918a5247418aed48a247414a5e45fe2 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 21 Jun 2018 16:48:26 -0400 Subject: drm/amdgpu: Count disabled CRTCs in commit tail earlier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a regression I accidentally reduced that was picked up by kasan, where we were checking the CRTC atomic states after DRM's helpers had already freed them. Example: ================================================================== BUG: KASAN: use-after-free in amdgpu_dm_atomic_commit_tail.cold.50+0x13d/0x15a [amdgpu] Read of size 1 at addr ffff8803a697b071 by task kworker/u16:0/7 CPU: 7 PID: 7 Comm: kworker/u16:0 Tainted: G O 4.18.0-rc1Lyude-Upstream+ #1 Hardware name: HP HP ZBook 15 G4/8275, BIOS P70 Ver. 01.21 05/02/2018 Workqueue: events_unbound commit_work [drm_kms_helper] Call Trace: dump_stack+0xc1/0x169 ? dump_stack_print_info.cold.1+0x42/0x42 ? kmsg_dump_rewind_nolock+0xd9/0xd9 ? printk+0x9f/0xc5 ? amdgpu_dm_atomic_commit_tail.cold.50+0x13d/0x15a [amdgpu] print_address_description+0x6c/0x23c ? amdgpu_dm_atomic_commit_tail.cold.50+0x13d/0x15a [amdgpu] kasan_report.cold.6+0x241/0x2fd amdgpu_dm_atomic_commit_tail.cold.50+0x13d/0x15a [amdgpu] ? commit_planes_to_stream.constprop.45+0x13b0/0x13b0 [amdgpu] ? cpu_load_update_active+0x290/0x290 ? finish_task_switch+0x2bd/0x840 ? __switch_to_asm+0x34/0x70 ? read_word_at_a_time+0xe/0x20 ? strscpy+0x14b/0x460 ? drm_atomic_helper_wait_for_dependencies+0x47d/0x7e0 [drm_kms_helper] commit_tail+0x96/0xe0 [drm_kms_helper] process_one_work+0x88a/0x1360 ? create_worker+0x540/0x540 ? __sched_text_start+0x8/0x8 ? move_queued_task+0x760/0x760 ? call_rcu_sched+0x20/0x20 ? vsnprintf+0xcda/0x1350 ? wait_woken+0x1c0/0x1c0 ? mutex_unlock+0x1d/0x40 ? init_timer_key+0x190/0x230 ? schedule+0xea/0x390 ? __schedule+0x1ea0/0x1ea0 ? need_to_create_worker+0xe4/0x210 ? init_worker_pool+0x700/0x700 ? try_to_del_timer_sync+0xbf/0x110 ? del_timer+0x120/0x120 ? __mutex_lock_slowpath+0x10/0x10 worker_thread+0x196/0x11f0 ? flush_rcu_work+0x50/0x50 ? __switch_to_asm+0x34/0x70 ? __switch_to_asm+0x34/0x70 ? __switch_to_asm+0x40/0x70 ? __switch_to_asm+0x34/0x70 ? __switch_to_asm+0x40/0x70 ? __switch_to_asm+0x34/0x70 ? __switch_to_asm+0x40/0x70 ? __schedule+0x7d6/0x1ea0 ? migrate_swap_stop+0x850/0x880 ? __sched_text_start+0x8/0x8 ? save_stack+0x8c/0xb0 ? kasan_kmalloc+0xbf/0xe0 ? kmem_cache_alloc_trace+0xe4/0x190 ? kthread+0x98/0x390 ? ret_from_fork+0x35/0x40 ? ret_from_fork+0x35/0x40 ? deactivate_slab.isra.67+0x3c4/0x5c0 ? kthread+0x98/0x390 ? kthread+0x98/0x390 ? set_track+0x76/0x120 ? schedule+0xea/0x390 ? __schedule+0x1ea0/0x1ea0 ? wait_woken+0x1c0/0x1c0 ? kasan_unpoison_shadow+0x30/0x40 ? parse_args.cold.15+0x17a/0x17a ? flush_rcu_work+0x50/0x50 kthread+0x2d4/0x390 ? kthread_create_worker_on_cpu+0xc0/0xc0 ret_from_fork+0x35/0x40 Allocated by task 1124: kasan_kmalloc+0xbf/0xe0 kmem_cache_alloc_trace+0xe4/0x190 dm_crtc_duplicate_state+0x78/0x130 [amdgpu] drm_atomic_get_crtc_state+0x147/0x410 [drm] page_flip_common+0x57/0x230 [drm_kms_helper] drm_atomic_helper_page_flip+0xa6/0x110 [drm_kms_helper] drm_mode_page_flip_ioctl+0xc4b/0x10a0 [drm] drm_ioctl_kernel+0x1d4/0x260 [drm] drm_ioctl+0x433/0x920 [drm] amdgpu_drm_ioctl+0x11d/0x290 [amdgpu] do_vfs_ioctl+0x1a1/0x13d0 ksys_ioctl+0x60/0x90 __x64_sys_ioctl+0x6f/0xb0 do_syscall_64+0x147/0x440 entry_SYSCALL_64_after_hwframe+0x44/0xa9 Freed by task 1124: __kasan_slab_free+0x12e/0x180 kfree+0x92/0x1a0 drm_atomic_state_default_clear+0x315/0xc40 [drm] __drm_atomic_state_free+0x35/0xd0 [drm] drm_atomic_helper_update_plane+0xac/0x350 [drm_kms_helper] __setplane_internal+0x2d6/0x840 [drm] drm_mode_cursor_universal+0x41e/0xbe0 [drm] drm_mode_cursor_common+0x49f/0x880 [drm] drm_mode_cursor_ioctl+0xd8/0x130 [drm] drm_ioctl_kernel+0x1d4/0x260 [drm] drm_ioctl+0x433/0x920 [drm] amdgpu_drm_ioctl+0x11d/0x290 [amdgpu] do_vfs_ioctl+0x1a1/0x13d0 ksys_ioctl+0x60/0x90 __x64_sys_ioctl+0x6f/0xb0 do_syscall_64+0x147/0x440 entry_SYSCALL_64_after_hwframe+0x44/0xa9 The buggy address belongs to the object at ffff8803a697b068 which belongs to the cache kmalloc-1024 of size 1024 The buggy address is located 9 bytes inside of 1024-byte region [ffff8803a697b068, ffff8803a697b468) The buggy address belongs to the page: page:ffffea000e9a5e00 count:1 mapcount:0 mapping:ffff88041e00efc0 index:0x0 compound_mapcount: 0 flags: 0x8000000000008100(slab|head) raw: 8000000000008100 ffffea000ecbc208 ffff88041e000c70 ffff88041e00efc0 raw: 0000000000000000 0000000000170017 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff8803a697af00: fb fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff8803a697af80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >ffff8803a697b000: fc fc fc fc fc fc fc fc fc fc fc fc fc fb fb fb ^ ffff8803a697b080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff8803a697b100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ================================================================== So, we fix this by counting the number of CRTCs this atomic commit disabled early on in the function before their atomic states have been freed, then use that count later to do the appropriate number of RPM puts at the end of the function. Acked-by: Michel Dänzer Reviewed-by: Harry Wentland Cc: stable@vger.kernel.org Fixes: 97028037a38ae ("drm/amdgpu: Grab/put runtime PM references in atomic_commit_tail()") Signed-off-by: Lyude Paul Cc: Michel Dänzer Reported-by: Michel Dänzer Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index f9add85157e7..689dbdf44bbf 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -4206,6 +4206,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) struct drm_connector *connector; struct drm_connector_state *old_con_state, *new_con_state; struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state; + int crtc_disable_count = 0; drm_atomic_helper_update_legacy_modeset_state(dev, state); @@ -4410,6 +4411,9 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); bool modeset_needed; + if (old_crtc_state->active && !new_crtc_state->active) + crtc_disable_count++; + dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); dm_old_crtc_state = to_dm_crtc_state(old_crtc_state); modeset_needed = modeset_required( @@ -4463,11 +4467,9 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) * so we can put the GPU into runtime suspend if we're not driving any * displays anymore */ + for (i = 0; i < crtc_disable_count; i++) + pm_runtime_put_autosuspend(dev->dev); pm_runtime_mark_last_busy(dev->dev); - for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { - if (old_crtc_state->active && !new_crtc_state->active) - pm_runtime_put_autosuspend(dev->dev); - } } -- cgit v1.2.3 From 38e624a18f9a05b8c894409be6b14709a7206c7c Mon Sep 17 00:00:00 2001 From: Michel Dänzer Date: Thu, 21 Jun 2018 11:27:46 +0200 Subject: drm/amdgpu: GPU vs CPU page size fixes in amdgpu_vm_bo_split_mapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit start / last / max_entries are numbers of GPU pages, pfn / count are numbers of CPU pages. Convert between them accordingly. Fixes badness on systems with > 4K page size. Cc: stable@vger.kernel.org Bugzilla: https://bugs.freedesktop.org/106258 Reported-by: Matt Corallo Tested-by: foxbat@ruin.net Reviewed-by: Alex Deucher Reviewed-by: Christian König Signed-off-by: Michel Dänzer Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index b0eb2f537392..edf16b2b957a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -1463,7 +1463,9 @@ static int amdgpu_vm_bo_split_mapping(struct amdgpu_device *adev, uint64_t count; max_entries = min(max_entries, 16ull * 1024ull); - for (count = 1; count < max_entries; ++count) { + for (count = 1; + count < max_entries / (PAGE_SIZE / AMDGPU_GPU_PAGE_SIZE); + ++count) { uint64_t idx = pfn + count; if (pages_addr[idx] != @@ -1476,7 +1478,7 @@ static int amdgpu_vm_bo_split_mapping(struct amdgpu_device *adev, dma_addr = pages_addr; } else { addr = pages_addr[pfn]; - max_entries = count; + max_entries = count * (PAGE_SIZE / AMDGPU_GPU_PAGE_SIZE); } } else if (flags & AMDGPU_PTE_VALID) { @@ -1491,7 +1493,7 @@ static int amdgpu_vm_bo_split_mapping(struct amdgpu_device *adev, if (r) return r; - pfn += last - start + 1; + pfn += (last - start + 1) / (PAGE_SIZE / AMDGPU_GPU_PAGE_SIZE); if (nodes && nodes->size == pfn) { pfn = 0; ++nodes; -- cgit v1.2.3 From b8f1f65882f07913157c44673af7ec0b308d03eb Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Thu, 21 Jun 2018 13:11:31 +0800 Subject: vhost_net: validate sock before trying to put its fd Sock will be NULL if we pass -1 to vhost_net_set_backend(), but when we meet errors during ubuf allocation, the code does not check for NULL before calling sockfd_put(), this will lead NULL dereferencing. Fixing by checking sock pointer before. Fixes: bab632d69ee4 ("vhost: vhost TX zero-copy support") Reported-by: Dan Carpenter Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/vhost/net.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 686dc670fd29..29756d88799b 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -1226,7 +1226,8 @@ err_used: if (ubufs) vhost_net_ubuf_put_wait_and_free(ubufs); err_ubufs: - sockfd_put(sock); + if (sock) + sockfd_put(sock); err_vq: mutex_unlock(&vq->mutex); err: -- cgit v1.2.3 From 271f7ff5aa5a73488b7a9d8b84b5205fb5b2f7cc Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Fri, 22 Jun 2018 10:15:39 +0200 Subject: net: mvneta: fix the Rx desc DMA address in the Rx path When using s/w buffer management, buffers are allocated and DMA mapped. When doing so on an arm64 platform, an offset correction is applied on the DMA address, before storing it in an Rx descriptor. The issue is this DMA address is then used later in the Rx path without removing the offset correction. Thus the DMA address is wrong, which can led to various issues. This patch fixes this by removing the offset correction from the DMA address retrieved from the Rx descriptor before using it in the Rx path. Fixes: 8d5047cf9ca2 ("net: mvneta: Convert to be 64 bits compatible") Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 17a904cc6a5e..0ad2f3f7da85 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -1932,7 +1932,7 @@ static int mvneta_rx_swbm(struct mvneta_port *pp, int rx_todo, rx_bytes = rx_desc->data_size - (ETH_FCS_LEN + MVNETA_MH_SIZE); index = rx_desc - rxq->descs; data = rxq->buf_virt_addr[index]; - phys_addr = rx_desc->buf_phys_addr; + phys_addr = rx_desc->buf_phys_addr - pp->rx_offset_correction; if (!mvneta_rxq_desc_is_first_last(rx_status) || (rx_status & MVNETA_RXD_ERR_SUMMARY)) { -- cgit v1.2.3 From c2cd650b5b2bd6b57f2f62be65b1a8a576d4de6d Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Fri, 22 Jun 2018 11:50:52 +0200 Subject: net: mscc: make sparse happy This patch fixes a sparse warning about using an incorrect type in argument 2 of ocelot_write_rix(), as an u32 was expected but a __be32 was given. The conversion to u32 is forced, which is safe as the value will be written as-is in the hardware without any modification. Fixes: 08d02364b12f ("net: mscc: fix the injection header") Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/mscc/ocelot.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 52c57e0ff617..776a8a9be8e3 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -374,7 +374,8 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) ocelot_gen_ifh(ifh, &info); for (i = 0; i < IFH_LEN; i++) - ocelot_write_rix(ocelot, cpu_to_be32(ifh[i]), QS_INJ_WR, grp); + ocelot_write_rix(ocelot, (__force u32)cpu_to_be32(ifh[i]), + QS_INJ_WR, grp); count = (skb->len + 3) / 4; last = skb->len % 4; -- cgit v1.2.3 From e020797b7def11c8feeb3ee5c1f48c12cb959def Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 22 Jun 2018 13:08:43 +0200 Subject: net: Remove depends on HAS_DMA in case of platform dependency Remove dependencies on HAS_DMA where a Kconfig symbol depends on another symbol that implies HAS_DMA, and, optionally, on "|| COMPILE_TEST". In most cases this other symbol is an architecture or platform specific symbol, or PCI. Generic symbols and drivers without platform dependencies keep their dependencies on HAS_DMA, to prevent compiling subsystems or drivers that cannot work anyway. This simplifies the dependencies, and allows to improve compile-testing. Signed-off-by: Geert Uytterhoeven Reviewed-by: Mark Brown Acked-by: Robin Murphy Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/Kconfig | 2 +- drivers/net/ethernet/apm/xgene-v2/Kconfig | 1 - drivers/net/ethernet/apm/xgene/Kconfig | 1 - drivers/net/ethernet/arc/Kconfig | 6 ++++-- drivers/net/ethernet/broadcom/Kconfig | 2 -- drivers/net/ethernet/calxeda/Kconfig | 2 +- drivers/net/ethernet/hisilicon/Kconfig | 2 +- drivers/net/ethernet/marvell/Kconfig | 8 +++----- drivers/net/ethernet/mellanox/mlxsw/Kconfig | 2 +- drivers/net/ethernet/renesas/Kconfig | 2 -- drivers/net/wireless/broadcom/brcm80211/Kconfig | 1 - drivers/net/wireless/quantenna/qtnfmac/Kconfig | 2 +- 12 files changed, 12 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig index d5c15e8bb3de..f273af136fc7 100644 --- a/drivers/net/ethernet/amd/Kconfig +++ b/drivers/net/ethernet/amd/Kconfig @@ -173,7 +173,7 @@ config SUNLANCE config AMD_XGBE tristate "AMD 10GbE Ethernet driver" - depends on ((OF_NET && OF_ADDRESS) || ACPI || PCI) && HAS_IOMEM && HAS_DMA + depends on ((OF_NET && OF_ADDRESS) || ACPI || PCI) && HAS_IOMEM depends on X86 || ARM64 || COMPILE_TEST select BITREVERSE select CRC32 diff --git a/drivers/net/ethernet/apm/xgene-v2/Kconfig b/drivers/net/ethernet/apm/xgene-v2/Kconfig index 1205861b6318..eedd3f3dd22e 100644 --- a/drivers/net/ethernet/apm/xgene-v2/Kconfig +++ b/drivers/net/ethernet/apm/xgene-v2/Kconfig @@ -1,6 +1,5 @@ config NET_XGENE_V2 tristate "APM X-Gene SoC Ethernet-v2 Driver" - depends on HAS_DMA depends on ARCH_XGENE || COMPILE_TEST help This is the Ethernet driver for the on-chip ethernet interface diff --git a/drivers/net/ethernet/apm/xgene/Kconfig b/drivers/net/ethernet/apm/xgene/Kconfig index afccb033177b..e4e33c900b57 100644 --- a/drivers/net/ethernet/apm/xgene/Kconfig +++ b/drivers/net/ethernet/apm/xgene/Kconfig @@ -1,6 +1,5 @@ config NET_XGENE tristate "APM X-Gene SoC Ethernet Driver" - depends on HAS_DMA depends on ARCH_XGENE || COMPILE_TEST select PHYLIB select MDIO_XGENE diff --git a/drivers/net/ethernet/arc/Kconfig b/drivers/net/ethernet/arc/Kconfig index e743ddf46343..5d0ab8e74b68 100644 --- a/drivers/net/ethernet/arc/Kconfig +++ b/drivers/net/ethernet/arc/Kconfig @@ -24,7 +24,8 @@ config ARC_EMAC_CORE config ARC_EMAC tristate "ARC EMAC support" select ARC_EMAC_CORE - depends on OF_IRQ && OF_NET && HAS_DMA && (ARC || COMPILE_TEST) + depends on OF_IRQ && OF_NET + depends on ARC || COMPILE_TEST ---help--- On some legacy ARC (Synopsys) FPGA boards such as ARCAngel4/ML50x non-standard on-chip ethernet device ARC EMAC 10/100 is used. @@ -33,7 +34,8 @@ config ARC_EMAC config EMAC_ROCKCHIP tristate "Rockchip EMAC support" select ARC_EMAC_CORE - depends on OF_IRQ && OF_NET && REGULATOR && HAS_DMA && (ARCH_ROCKCHIP || COMPILE_TEST) + depends on OF_IRQ && OF_NET && REGULATOR + depends on ARCH_ROCKCHIP || COMPILE_TEST ---help--- Support for Rockchip RK3036/RK3066/RK3188 EMAC ethernet controllers. This selects Rockchip SoC glue layer support for the diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index af75156919ed..4c3bfde6e8de 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -157,7 +157,6 @@ config BGMAC config BGMAC_BCMA tristate "Broadcom iProc GBit BCMA support" depends on BCMA && BCMA_HOST_SOC - depends on HAS_DMA depends on BCM47XX || ARCH_BCM_5301X || COMPILE_TEST select BGMAC select PHYLIB @@ -170,7 +169,6 @@ config BGMAC_BCMA config BGMAC_PLATFORM tristate "Broadcom iProc GBit platform support" - depends on HAS_DMA depends on ARCH_BCM_IPROC || COMPILE_TEST depends on OF select BGMAC diff --git a/drivers/net/ethernet/calxeda/Kconfig b/drivers/net/ethernet/calxeda/Kconfig index 07d2201530d2..9fdd496b90ff 100644 --- a/drivers/net/ethernet/calxeda/Kconfig +++ b/drivers/net/ethernet/calxeda/Kconfig @@ -1,6 +1,6 @@ config NET_CALXEDA_XGMAC tristate "Calxeda 1G/10G XGMAC Ethernet driver" - depends on HAS_IOMEM && HAS_DMA + depends on HAS_IOMEM depends on ARCH_HIGHBANK || COMPILE_TEST select CRC32 help diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig index 8bcf470ff5f3..fb1a7251f45d 100644 --- a/drivers/net/ethernet/hisilicon/Kconfig +++ b/drivers/net/ethernet/hisilicon/Kconfig @@ -5,7 +5,7 @@ config NET_VENDOR_HISILICON bool "Hisilicon devices" default y - depends on (OF || ACPI) && HAS_DMA + depends on OF || ACPI depends on ARM || ARM64 || COMPILE_TEST ---help--- If you have a network (Ethernet) card belonging to this class, say Y. diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index cc2f7701e71e..f33fd22b351c 100644 --- a/drivers/net/ethernet/marvell/Kconfig +++ b/drivers/net/ethernet/marvell/Kconfig @@ -18,8 +18,8 @@ if NET_VENDOR_MARVELL config MV643XX_ETH tristate "Marvell Discovery (643XX) and Orion ethernet support" - depends on (MV64X60 || PPC32 || PLAT_ORION || COMPILE_TEST) && INET - depends on HAS_DMA + depends on MV64X60 || PPC32 || PLAT_ORION || COMPILE_TEST + depends on INET select PHYLIB select MVMDIO ---help--- @@ -58,7 +58,6 @@ config MVNETA_BM_ENABLE config MVNETA tristate "Marvell Armada 370/38x/XP/37xx network interface support" depends on ARCH_MVEBU || COMPILE_TEST - depends on HAS_DMA select MVMDIO select PHYLINK ---help--- @@ -84,7 +83,6 @@ config MVNETA_BM config MVPP2 tristate "Marvell Armada 375/7K/8K network interface support" depends on ARCH_MVEBU || COMPILE_TEST - depends on HAS_DMA select MVMDIO select PHYLINK ---help--- @@ -93,7 +91,7 @@ config MVPP2 config PXA168_ETH tristate "Marvell pxa168 ethernet support" - depends on HAS_IOMEM && HAS_DMA + depends on HAS_IOMEM depends on CPU_PXA168 || ARCH_BERLIN || COMPILE_TEST select PHYLIB ---help--- diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig index f4d9c9975ac3..82827a8d3d67 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig +++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig @@ -30,7 +30,7 @@ config MLXSW_CORE_THERMAL config MLXSW_PCI tristate "PCI bus implementation for Mellanox Technologies Switch ASICs" - depends on PCI && HAS_DMA && HAS_IOMEM && MLXSW_CORE + depends on PCI && HAS_IOMEM && MLXSW_CORE default m ---help--- This is PCI bus implementation for Mellanox Technologies Switch ASICs. diff --git a/drivers/net/ethernet/renesas/Kconfig b/drivers/net/ethernet/renesas/Kconfig index 27be51f0a421..f3f7477043ce 100644 --- a/drivers/net/ethernet/renesas/Kconfig +++ b/drivers/net/ethernet/renesas/Kconfig @@ -17,7 +17,6 @@ if NET_VENDOR_RENESAS config SH_ETH tristate "Renesas SuperH Ethernet support" - depends on HAS_DMA depends on ARCH_RENESAS || SUPERH || COMPILE_TEST select CRC32 select MII @@ -31,7 +30,6 @@ config SH_ETH config RAVB tristate "Renesas Ethernet AVB support" - depends on HAS_DMA depends on ARCH_RENESAS || COMPILE_TEST select CRC32 select MII diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig index 9d99eb42d917..6acba67bca07 100644 --- a/drivers/net/wireless/broadcom/brcm80211/Kconfig +++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig @@ -60,7 +60,6 @@ config BRCMFMAC_PCIE bool "PCIE bus interface support for FullMAC driver" depends on BRCMFMAC depends on PCI - depends on HAS_DMA select BRCMFMAC_PROTO_MSGBUF select FW_LOADER ---help--- diff --git a/drivers/net/wireless/quantenna/qtnfmac/Kconfig b/drivers/net/wireless/quantenna/qtnfmac/Kconfig index 025fa6018550..8d1492a90bd1 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/Kconfig +++ b/drivers/net/wireless/quantenna/qtnfmac/Kconfig @@ -7,7 +7,7 @@ config QTNFMAC config QTNFMAC_PEARL_PCIE tristate "Quantenna QSR10g PCIe support" default n - depends on HAS_DMA && PCI && CFG80211 + depends on PCI && CFG80211 select QTNFMAC select FW_LOADER select CRC32 -- cgit v1.2.3 From 5ce36338a30f9814fc4824f9fe6c20cd83d872c7 Mon Sep 17 00:00:00 2001 From: Ganesh Goudar Date: Sat, 23 Jun 2018 20:28:26 +0530 Subject: cxgb4: when disabling dcb set txq dcb priority to 0 When we are disabling DCB, store "0" in txq->dcb_prio since that's used for future TX Work Request "OVLAN_IDX" values. Setting non zero priority upon disabling DCB would halt the traffic. Reported-by: AMG Zollner Robert CC: David Ahern Signed-off-by: Casey Leedom Signed-off-by: Ganesh Goudar Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index dd04a2f89ce6..bc03c175a3cd 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -263,7 +263,7 @@ static void dcb_tx_queue_prio_enable(struct net_device *dev, int enable) "Can't %s DCB Priority on port %d, TX Queue %d: err=%d\n", enable ? "set" : "unset", pi->port_id, i, -err); else - txq->dcb_prio = value; + txq->dcb_prio = enable ? value : 0; } } -- cgit v1.2.3 From e7e197edd09c25774b4f12cab19f9d5462f240f4 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Sat, 23 Jun 2018 23:22:52 +0200 Subject: qmi_wwan: add support for the Dell Wireless 5821e module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This module exposes two USB configurations: a QMI+AT capable setup on USB config #1 and a MBIM capable setup on USB config #2. By default the kernel will choose the MBIM capable configuration as long as the cdc_mbim driver is available. This patch adds support for the QMI port in the secondary configuration. Signed-off-by: Aleksander Morgado Acked-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 8e8b51f171f4..8fac8e132c5b 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1246,6 +1246,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x413c, 0x81b3, 8)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */ {QMI_FIXED_INTF(0x413c, 0x81b6, 8)}, /* Dell Wireless 5811e */ {QMI_FIXED_INTF(0x413c, 0x81b6, 10)}, /* Dell Wireless 5811e */ + {QMI_FIXED_INTF(0x413c, 0x81d7, 1)}, /* Dell Wireless 5821e */ {QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */ {QMI_FIXED_INTF(0x03f0, 0x9d1d, 1)}, /* HP lt4120 Snapdragon X5 LTE */ {QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */ -- cgit v1.2.3 From 13399ff25f179811ce9c1df1523eb39f9e4a4772 Mon Sep 17 00:00:00 2001 From: Tomasz Duszynski Date: Mon, 28 May 2018 17:38:59 +0200 Subject: iio: pressure: bmp280: fix relative humidity unit According to IIO ABI relative humidity reading should be returned in milli percent. This patch addresses that by applying proper scaling and returning integer instead of fractional format type specifier. Note that the fixes tag is before the driver was heavily refactored to introduce spi support, so the patch won't apply that far back. Signed-off-by: Tomasz Duszynski Fixes: 14beaa8f5ab1 ("iio: pressure: bmp280: add humidity support") Acked-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 5ec3e41b65f2..fe87d27779d9 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -415,10 +415,9 @@ static int bmp280_read_humid(struct bmp280_data *data, int *val, int *val2) } comp_humidity = bmp280_compensate_humidity(data, adc_humidity); - *val = comp_humidity; - *val2 = 1024; + *val = comp_humidity * 1000 / 1024; - return IIO_VAL_FRACTIONAL; + return IIO_VAL_INT; } static int bmp280_read_raw(struct iio_dev *indio_dev, -- cgit v1.2.3 From 288320f6e9a254bb595b4783c9bdfcc7c0e73153 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 30 May 2018 19:19:36 +0100 Subject: iio: tsl2x7x/tsl2772: avoid potential division by zero It may be possible for tsl2772_get_lux to return a zero lux value and hence a division by zero can occur when lux_val is zero. Check for this case and return -ERANGE to avoid the division by zero. Detected by CoverityScan, CID#1469484 ("Division or modulo by zero") Signed-off-by: Colin Ian King Acked-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2772.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index 34d42a2504c9..df5b2a0da96c 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -582,6 +582,8 @@ static int tsl2772_als_calibrate(struct iio_dev *indio_dev) "%s: failed to get lux\n", __func__); return lux_val; } + if (lux_val == 0) + return -ERANGE; ret = (chip->settings.als_cal_target * chip->settings.als_gain_trim) / lux_val; -- cgit v1.2.3 From b02ec67a8e38875cdc5f9214be885022f11c0017 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Thu, 7 Jun 2018 21:52:50 +0300 Subject: iio: mma8452: Fix ignoring MMA8452_INT_DRDY Interrupts are ignored if no event bit is set in the status status register and this breaks the buffer interface. No data is shown when running "iio_generic_buffer -n mma8451 -a" and interrupt counts go crazy. Fix by not returning IRQ_NONE if DRDY is set. Fixes: 605f72de137a ("iio: accel: mma8452: improvements to handle multiple events") Signed-off-by: Leonard Crestez cc: Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 7e3d82cff3d5..c149c9c360fc 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -1053,7 +1053,7 @@ static irqreturn_t mma8452_interrupt(int irq, void *p) if (src < 0) return IRQ_NONE; - if (!(src & data->chip_info->enabled_events)) + if (!(src & (data->chip_info->enabled_events | MMA8452_INT_DRDY))) return IRQ_NONE; if (src & MMA8452_INT_DRDY) { -- cgit v1.2.3 From 2a221c04228bbf683fc8d3cb5d177aebb38b393c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 18 Jun 2018 18:09:36 +0200 Subject: iio: imu: inv_mpu6050: Fix probe() failure on older ACPI based machines Commit 5ec6486daa98 ("iio:imu: inv_mpu6050: support more interrupt types") causes inv_mpu_core_probe() to fail if the IRQ does not have a trigger-type setup. This happens on machines where the mpu6050 is enumerated through ACPI and an older Interrupt type ACPI resource is used for the interrupt, rather then a GpioInt type type, causing the mpu6050 driver to no longer work there. This happens on e.g. the Asus T100TA. This commits makes the mpu6050 fallback to the old IRQF_TRIGGER_RISING default if the irq-type is not setup, fixing this. Signed-off-by: Hans de Goede Fixes: 5ec6486daa98 ("iio:imu: inv_mpu6050: support more interrupt types") Reviewed-by: Martin Kelly Reviewed-by: Jean-Baptiste Maneyrol Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index f9c0624505a2..42618fe4f83e 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -959,6 +959,8 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, } irq_type = irqd_get_trigger_type(desc); + if (!irq_type) + irq_type = IRQF_TRIGGER_RISING; if (irq_type == IRQF_TRIGGER_RISING) st->irq_mask = INV_MPU6050_ACTIVE_HIGH; else if (irq_type == IRQF_TRIGGER_FALLING) -- cgit v1.2.3 From 829eb05365ff06e8adc23f2541597d0cc3c18348 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 24 Jun 2018 11:57:31 +0100 Subject: sfc: make function efx_rps_hash_bucket static The function efx_rps_hash_bucket is local to the source and does not need to be in global scope, so make it static. Cleans up sparse warning: symbol 'efx_rps_hash_bucket' was not declared. Should it be static? Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/efx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index ad4a354ce570..570ec72266f3 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -3180,6 +3180,7 @@ bool efx_rps_check_rule(struct efx_arfs_rule *rule, unsigned int filter_idx, return true; } +static struct hlist_head *efx_rps_hash_bucket(struct efx_nic *efx, const struct efx_filter_spec *spec) { -- cgit v1.2.3 From 1dd2058f902eb2d8bd5bedab0b190a21d5245e71 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 21 Jun 2018 11:06:41 +0300 Subject: cpufreq: qcom-kryo: Fix error handling in probe() We should return if get_cpu_device() fails or it leads to a NULL dereference. Also dev_pm_opp_of_get_opp_desc_node() returns NULL on error, it never returns error pointers. Fixes: 46e2856b8e18 (cpufreq: Add Kryo CPU scaling driver) Signed-off-by: Dan Carpenter Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/qcom-cpufreq-kryo.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/qcom-cpufreq-kryo.c b/drivers/cpufreq/qcom-cpufreq-kryo.c index 01bddacf5c3b..29389accf3e9 100644 --- a/drivers/cpufreq/qcom-cpufreq-kryo.c +++ b/drivers/cpufreq/qcom-cpufreq-kryo.c @@ -87,8 +87,8 @@ static int qcom_cpufreq_kryo_probe(struct platform_device *pdev) int ret; cpu_dev = get_cpu_device(0); - if (NULL == cpu_dev) - ret = -ENODEV; + if (!cpu_dev) + return -ENODEV; msm8996_version = qcom_cpufreq_kryo_get_msm_id(); if (NUM_OF_MSM8996_VERSIONS == msm8996_version) { @@ -97,8 +97,8 @@ static int qcom_cpufreq_kryo_probe(struct platform_device *pdev) } np = dev_pm_opp_of_get_opp_desc_node(cpu_dev); - if (IS_ERR(np)) - return PTR_ERR(np); + if (!np) + return -ENOENT; ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu"); if (!ret) { -- cgit v1.2.3 From ad6384ba3ac98ad524194e37de379c1fe503870b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 20 Jun 2018 11:45:37 +0530 Subject: PM / Domains: Rename opp_node to np The DT node passed here isn't necessarily an OPP node, as this routine can also be used for cases where the "required-opps" property is present directly in the device's node. Rename it. This also removes a stale comment. Acked-by: Ulf Hansson Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 7 +++---- include/linux/pm_domain.h | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 4925af5c4cf0..c298de8a8308 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2487,10 +2487,9 @@ EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states); * power domain corresponding to a DT node's "required-opps" property. * * @dev: Device for which the performance-state needs to be found. - * @opp_node: DT node where the "required-opps" property is present. This can be + * @np: DT node where the "required-opps" property is present. This can be * the device node itself (if it doesn't have an OPP table) or a node * within the OPP table of a device (if device has an OPP table). - * @state: Pointer to return performance state. * * Returns performance state corresponding to the "required-opps" property of * a DT node. This calls platform specific genpd->opp_to_performance_state() @@ -2499,7 +2498,7 @@ EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states); * Returns performance state on success and 0 on failure. */ unsigned int of_genpd_opp_to_performance_state(struct device *dev, - struct device_node *opp_node) + struct device_node *np) { struct generic_pm_domain *genpd; struct dev_pm_opp *opp; @@ -2514,7 +2513,7 @@ unsigned int of_genpd_opp_to_performance_state(struct device *dev, genpd_lock(genpd); - opp = of_dev_pm_opp_find_required_opp(&genpd->dev, opp_node); + opp = of_dev_pm_opp_find_required_opp(&genpd->dev, np); if (IS_ERR(opp)) { dev_err(dev, "Failed to find required OPP: %ld\n", PTR_ERR(opp)); diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 139f79c8477a..cb8d84090cfb 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -234,7 +234,7 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np); int of_genpd_parse_idle_states(struct device_node *dn, struct genpd_power_state **states, int *n); unsigned int of_genpd_opp_to_performance_state(struct device *dev, - struct device_node *opp_node); + struct device_node *np); int genpd_dev_pm_attach(struct device *dev); struct device *genpd_dev_pm_attach_by_id(struct device *dev, @@ -274,7 +274,7 @@ static inline int of_genpd_parse_idle_states(struct device_node *dn, static inline unsigned int of_genpd_opp_to_performance_state(struct device *dev, - struct device_node *opp_node) + struct device_node *np) { return 0; } -- cgit v1.2.3 From 229bc19fd7aca4f37964af06e3583c1c8f36b5d6 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 21 Jun 2018 16:19:41 +0300 Subject: xhci: Fix perceived dead host due to runtime suspend race with event handler Don't rely on event interrupt (EINT) bit alone to detect pending port change in resume. If no change event is detected the host may be suspended again, oterwise roothubs are resumed. There is a lag in xHC setting EINT. If we don't notice the pending change in resume, and the controller is runtime suspeded again, it causes the event handler to assume host is dead as it will fail to read xHC registers once PCI puts the controller to D3 state. [ 268.520969] xhci_hcd: xhci_resume: starting port polling. [ 268.520985] xhci_hcd: xhci_hub_status_data: stopping port polling. [ 268.521030] xhci_hcd: xhci_suspend: stopping port polling. [ 268.521040] xhci_hcd: // Setting command ring address to 0x349bd001 [ 268.521139] xhci_hcd: Port Status Change Event for port 3 [ 268.521149] xhci_hcd: resume root hub [ 268.521163] xhci_hcd: port resume event for port 3 [ 268.521168] xhci_hcd: xHC is not running. [ 268.521174] xhci_hcd: handle_port_status: starting port polling. [ 268.596322] xhci_hcd: xhci_hc_died: xHCI host controller not responding, assume dead The EINT lag is described in a additional note in xhci specs 4.19.2: "Due to internal xHC scheduling and system delays, there will be a lag between a change bit being set and the Port Status Change Event that it generated being written to the Event Ring. If SW reads the PORTSC and sees a change bit set, there is no guarantee that the corresponding Port Status Change Event has already been written into the Event Ring." Cc: Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 40 +++++++++++++++++++++++++++++++++++++--- drivers/usb/host/xhci.h | 4 ++++ 2 files changed, 41 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 8c8da2d657fa..f11ec61bcc7d 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -908,6 +908,41 @@ static void xhci_disable_port_wake_on_bits(struct xhci_hcd *xhci) spin_unlock_irqrestore(&xhci->lock, flags); } +static bool xhci_pending_portevent(struct xhci_hcd *xhci) +{ + struct xhci_port **ports; + int port_index; + u32 status; + u32 portsc; + + status = readl(&xhci->op_regs->status); + if (status & STS_EINT) + return true; + /* + * Checking STS_EINT is not enough as there is a lag between a change + * bit being set and the Port Status Change Event that it generated + * being written to the Event Ring. See note in xhci 1.1 section 4.19.2. + */ + + port_index = xhci->usb2_rhub.num_ports; + ports = xhci->usb2_rhub.ports; + while (port_index--) { + portsc = readl(ports[port_index]->addr); + if (portsc & PORT_CHANGE_MASK || + (portsc & PORT_PLS_MASK) == XDEV_RESUME) + return true; + } + port_index = xhci->usb3_rhub.num_ports; + ports = xhci->usb3_rhub.ports; + while (port_index--) { + portsc = readl(ports[port_index]->addr); + if (portsc & PORT_CHANGE_MASK || + (portsc & PORT_PLS_MASK) == XDEV_RESUME) + return true; + } + return false; +} + /* * Stop HC (not bus-specific) * @@ -1009,7 +1044,7 @@ EXPORT_SYMBOL_GPL(xhci_suspend); */ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) { - u32 command, temp = 0, status; + u32 command, temp = 0; struct usb_hcd *hcd = xhci_to_hcd(xhci); struct usb_hcd *secondary_hcd; int retval = 0; @@ -1134,8 +1169,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) done: if (retval == 0) { /* Resume root hubs only when have pending events. */ - status = readl(&xhci->op_regs->status); - if (status & STS_EINT) { + if (xhci_pending_portevent(xhci)) { usb_hcd_resume_root_hub(xhci->shared_hcd); usb_hcd_resume_root_hub(hcd); } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 939e2f86b595..841e89ffe2e9 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -382,6 +382,10 @@ struct xhci_op_regs { #define PORT_PLC (1 << 22) /* port configure error change - port failed to configure its link partner */ #define PORT_CEC (1 << 23) +#define PORT_CHANGE_MASK (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \ + PORT_RC | PORT_PLC | PORT_CEC) + + /* Cold Attach Status - xHC can set this bit to report device attached during * Sx state. Warm port reset should be perfomed to clear this bit and move port * to connected state. -- cgit v1.2.3 From d850c1658328e757635a46763783c6fd56390dcb Mon Sep 17 00:00:00 2001 From: Zhengjun Xing Date: Thu, 21 Jun 2018 16:19:42 +0300 Subject: xhci: Fix kernel oops in trace_xhci_free_virt_device commit 44a182b9d177 ("xhci: Fix use-after-free in xhci_free_virt_device") set dev->udev pointer to NULL in xhci_free_dev(), it will cause kernel panic in trace_xhci_free_virt_device. This patch reimplement the trace function trace_xhci_free_virt_device, remove dev->udev dereference and added more useful parameters to show in the trace function,it also makes sure dev->udev is not NULL before calling trace_xhci_free_virt_device. This issue happened when xhci-hcd trace is enabled and USB devices hot plug test. Original use-after-free patch went to stable so this needs so be applied there as well. [ 1092.022457] usb 2-4: USB disconnect, device number 6 [ 1092.092772] BUG: unable to handle kernel NULL pointer dereference at 0000000000000000 [ 1092.101694] PGD 0 P4D 0 [ 1092.104601] Oops: 0000 [#1] SMP [ 1092.207734] Workqueue: usb_hub_wq hub_event [ 1092.212507] RIP: 0010:trace_event_raw_event_xhci_log_virt_dev+0x6c/0xf0 [ 1092.220050] RSP: 0018:ffff8c252e883d28 EFLAGS: 00010086 [ 1092.226024] RAX: ffff8c24af86fa84 RBX: 0000000000000003 RCX: ffff8c25255c2a01 [ 1092.234130] RDX: 0000000000000000 RSI: 00000000aef55009 RDI: ffff8c252e883d28 [ 1092.242242] RBP: ffff8c252550e2c0 R08: ffff8c24af86fa84 R09: 0000000000000a70 [ 1092.250364] R10: 0000000000000a70 R11: 0000000000000000 R12: ffff8c251f21a000 [ 1092.258468] R13: 000000000000000c R14: ffff8c251f21a000 R15: ffff8c251f432f60 [ 1092.266572] FS: 0000000000000000(0000) GS:ffff8c252e880000(0000) knlGS:0000000000000000 [ 1092.275757] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 1092.282281] CR2: 0000000000000000 CR3: 0000000154209001 CR4: 00000000003606e0 [ 1092.290384] Call Trace: [ 1092.293156] [ 1092.295439] xhci_free_virt_device.part.34+0x182/0x1a0 [ 1092.301288] handle_cmd_completion+0x7ac/0xfa0 [ 1092.306336] ? trace_event_raw_event_xhci_log_trb+0x6e/0xa0 [ 1092.312661] xhci_irq+0x3e8/0x1f60 [ 1092.316524] __handle_irq_event_percpu+0x75/0x180 [ 1092.321876] handle_irq_event_percpu+0x20/0x50 [ 1092.326922] handle_irq_event+0x36/0x60 [ 1092.331273] handle_edge_irq+0x6d/0x180 [ 1092.335644] handle_irq+0x16/0x20 [ 1092.339417] do_IRQ+0x41/0xc0 [ 1092.342782] common_interrupt+0xf/0xf [ 1092.346955] Fixes: 44a182b9d177 ("xhci: Fix use-after-free in xhci_free_virt_device") Cc: Signed-off-by: Zhengjun Xing Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 4 ++-- drivers/usb/host/xhci-trace.h | 36 +++++++++++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index acbd3d7b8828..8a62eee9eee1 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -886,12 +886,12 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) dev = xhci->devs[slot_id]; - trace_xhci_free_virt_device(dev); - xhci->dcbaa->dev_context_ptrs[slot_id] = 0; if (!dev) return; + trace_xhci_free_virt_device(dev); + if (dev->tt_info) old_active_eps = dev->tt_info->active_eps; diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 410544ffe78f..88b427434bd8 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -171,6 +171,37 @@ DEFINE_EVENT(xhci_log_trb, xhci_dbc_gadget_ep_queue, TP_ARGS(ring, trb) ); +DECLARE_EVENT_CLASS(xhci_log_free_virt_dev, + TP_PROTO(struct xhci_virt_device *vdev), + TP_ARGS(vdev), + TP_STRUCT__entry( + __field(void *, vdev) + __field(unsigned long long, out_ctx) + __field(unsigned long long, in_ctx) + __field(u8, fake_port) + __field(u8, real_port) + __field(u16, current_mel) + + ), + TP_fast_assign( + __entry->vdev = vdev; + __entry->in_ctx = (unsigned long long) vdev->in_ctx->dma; + __entry->out_ctx = (unsigned long long) vdev->out_ctx->dma; + __entry->fake_port = (u8) vdev->fake_port; + __entry->real_port = (u8) vdev->real_port; + __entry->current_mel = (u16) vdev->current_mel; + ), + TP_printk("vdev %p ctx %llx | %llx fake_port %d real_port %d current_mel %d", + __entry->vdev, __entry->in_ctx, __entry->out_ctx, + __entry->fake_port, __entry->real_port, __entry->current_mel + ) +); + +DEFINE_EVENT(xhci_log_free_virt_dev, xhci_free_virt_device, + TP_PROTO(struct xhci_virt_device *vdev), + TP_ARGS(vdev) +); + DECLARE_EVENT_CLASS(xhci_log_virt_dev, TP_PROTO(struct xhci_virt_device *vdev), TP_ARGS(vdev), @@ -208,11 +239,6 @@ DEFINE_EVENT(xhci_log_virt_dev, xhci_alloc_virt_device, TP_ARGS(vdev) ); -DEFINE_EVENT(xhci_log_virt_dev, xhci_free_virt_device, - TP_PROTO(struct xhci_virt_device *vdev), - TP_ARGS(vdev) -); - DEFINE_EVENT(xhci_log_virt_dev, xhci_setup_device, TP_PROTO(struct xhci_virt_device *vdev), TP_ARGS(vdev) -- cgit v1.2.3 From 36eb93509c45d0bdbd8d09a01ab9d857972f5963 Mon Sep 17 00:00:00 2001 From: Dongjiu Geng Date: Thu, 21 Jun 2018 16:19:43 +0300 Subject: usb: xhci: remove the code build warning Initialize the 'err' variate to remove the build warning, the warning is shown as below: drivers/usb/host/xhci-tegra.c: In function 'tegra_xusb_mbox_thread': drivers/usb/host/xhci-tegra.c:552:6: warning: 'err' may be used uninitialized in this function [-Wuninitialized] drivers/usb/host/xhci-tegra.c:482:6: note: 'err' was declared here Fixes: e84fce0f8837 ("usb: xhci: Add NVIDIA Tegra XUSB controller driver") Signed-off-by: Dongjiu Geng Acked-by: Thierry Reding Acked-by: Jon Hunter Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index a8c1d073cba0..d50549fedec8 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -481,7 +481,7 @@ static void tegra_xusb_mbox_handle(struct tegra_xusb *tegra, unsigned long mask; unsigned int port; bool idle, enable; - int err; + int err = 0; memset(&rsp, 0, sizeof(rsp)); -- cgit v1.2.3 From 3431a150fd89bc74cd2f2aaf6977cc0e278fb445 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 21 Jun 2018 16:19:44 +0300 Subject: usb: xhci: tegra: fix runtime PM error handling The address-of operator will always evaluate to true. However, power should be explicitly disabled if no power domain is used. Remove the address-of operator. Fixes: 58c38116c6cc ("usb: xhci: tegra: Add support for managing powergates") Signed-off-by: Stefan Agner Acked-by: Jon Hunter Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index d50549fedec8..4b463e5202a4 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1223,10 +1223,10 @@ disable_rpm: pm_runtime_disable(&pdev->dev); usb_put_hcd(tegra->hcd); disable_xusbc: - if (!&pdev->dev.pm_domain) + if (!pdev->dev.pm_domain) tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC); disable_xusba: - if (!&pdev->dev.pm_domain) + if (!pdev->dev.pm_domain) tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA); put_padctl: tegra_xusb_padctl_put(tegra->padctl); -- cgit v1.2.3 From 305886ca87be480ae159908c2affd135c04215cf Mon Sep 17 00:00:00 2001 From: Ajay Gupta Date: Thu, 21 Jun 2018 16:19:45 +0300 Subject: usb: xhci: increase CRS timeout value Some controllers take almost 55ms to complete controller restore state (CRS). There is no timeout limit mentioned in xhci specification so fixing the issue by increasing the timeout limit to 100ms [reformat code comment -Mathias] Signed-off-by: Ajay Gupta Signed-off-by: Nagaraj Annaiah Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index f11ec61bcc7d..2f4850f25e82 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1078,8 +1078,13 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) command = readl(&xhci->op_regs->command); command |= CMD_CRS; writel(command, &xhci->op_regs->command); + /* + * Some controllers take up to 55+ ms to complete the controller + * restore so setting the timeout to 100ms. Xhci specification + * doesn't mention any timeout value. + */ if (xhci_handshake(&xhci->op_regs->status, - STS_RESTORE, 0, 10 * 1000)) { + STS_RESTORE, 0, 100 * 1000)) { xhci_warn(xhci, "WARN: xHC restore state timeout\n"); spin_unlock_irq(&xhci->lock); return -ETIMEDOUT; -- cgit v1.2.3 From d2d2e3c46be5d6dd8001d0eebdf7cafb9bc7006b Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 21 Jun 2018 16:43:17 +0300 Subject: acpi: Add helper for deactivating memory region Sometimes memory resource may be overlapping with SystemMemory Operation Region by design, for example if the memory region is used as a mailbox for communication with a firmware in the system. One occasion of such mailboxes is USB Type-C Connector System Software Interface (UCSI). With regions like that, it is important that the driver is able to map the memory with the requirements it has. For example, the driver should be allowed to map the memory as non-cached memory. However, if the operation region has been accessed before the driver has mapped the memory, the memory has been marked as write-back by the time the driver is loaded. That means the driver will fail to map the memory if it expects non-cached memory. To work around the problem, introducing helper that the drivers can use to temporarily deactivate (unmap) SystemMemory Operation Regions that overlap with their IO memory. Fixes: 8243edf44152 ("usb: typec: ucsi: Add ACPI driver") Cc: stable@vger.kernel.org Reviewed-by: Rafael J. Wysocki Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/osl.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 3 +++ 2 files changed, 75 insertions(+) (limited to 'drivers') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 7ca41bf023c9..8df9abfa947b 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -45,6 +45,8 @@ #include #include +#include "acpica/accommon.h" +#include "acpica/acnamesp.h" #include "internal.h" #define _COMPONENT ACPI_OS_SERVICES @@ -1490,6 +1492,76 @@ int acpi_check_region(resource_size_t start, resource_size_t n, } EXPORT_SYMBOL(acpi_check_region); +static acpi_status acpi_deactivate_mem_region(acpi_handle handle, u32 level, + void *_res, void **return_value) +{ + struct acpi_mem_space_context **mem_ctx; + union acpi_operand_object *handler_obj; + union acpi_operand_object *region_obj2; + union acpi_operand_object *region_obj; + struct resource *res = _res; + acpi_status status; + + region_obj = acpi_ns_get_attached_object(handle); + if (!region_obj) + return AE_OK; + + handler_obj = region_obj->region.handler; + if (!handler_obj) + return AE_OK; + + if (region_obj->region.space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) + return AE_OK; + + if (!(region_obj->region.flags & AOPOBJ_SETUP_COMPLETE)) + return AE_OK; + + region_obj2 = acpi_ns_get_secondary_object(region_obj); + if (!region_obj2) + return AE_OK; + + mem_ctx = (void *)®ion_obj2->extra.region_context; + + if (!(mem_ctx[0]->address >= res->start && + mem_ctx[0]->address < res->end)) + return AE_OK; + + status = handler_obj->address_space.setup(region_obj, + ACPI_REGION_DEACTIVATE, + NULL, (void **)mem_ctx); + if (ACPI_SUCCESS(status)) + region_obj->region.flags &= ~(AOPOBJ_SETUP_COMPLETE); + + return status; +} + +/** + * acpi_release_memory - Release any mappings done to a memory region + * @handle: Handle to namespace node + * @res: Memory resource + * @level: A level that terminates the search + * + * Walks through @handle and unmaps all SystemMemory Operation Regions that + * overlap with @res and that have already been activated (mapped). + * + * This is a helper that allows drivers to place special requirements on memory + * region that may overlap with operation regions, primarily allowing them to + * safely map the region as non-cached memory. + * + * The unmapped Operation Regions will be automatically remapped next time they + * are called, so the drivers do not need to do anything else. + */ +acpi_status acpi_release_memory(acpi_handle handle, struct resource *res, + u32 level) +{ + if (!(res->flags & IORESOURCE_MEM)) + return AE_TYPE; + + return acpi_walk_namespace(ACPI_TYPE_REGION, handle, level, + acpi_deactivate_mem_region, NULL, res, NULL); +} +EXPORT_SYMBOL_GPL(acpi_release_memory); + /* * Let drivers know whether the resource checks are effective */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 4b35a66383f9..e54f40974eb0 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -443,6 +443,9 @@ int acpi_check_resource_conflict(const struct resource *res); int acpi_check_region(resource_size_t start, resource_size_t n, const char *name); +acpi_status acpi_release_memory(acpi_handle handle, struct resource *res, + u32 level); + int acpi_resources_are_enforced(void); #ifdef CONFIG_HIBERNATION -- cgit v1.2.3 From 1f9f9d168ce619608572b01771c47a41b15429e6 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 21 Jun 2018 16:43:18 +0300 Subject: usb: typec: ucsi: acpi: Workaround for cache mode issue This fixes an issue where the driver fails with an error: ioremap error for 0x3f799000-0x3f79a000, requested 0x2, got 0x0 On some platforms the UCSI ACPI mailbox SystemMemory Operation Region may be setup before the driver has been loaded. That will lead into the driver failing to map the mailbox region, as it has been already marked as write-back memory. acpi_os_ioremap() for x86 uses ioremap_cache() unconditionally. When the issue happens, the embedded controller has a pending query event for the UCSI notification right after boot-up which causes the operation region to be setup before UCSI driver has been loaded. The fix is to notify acpi core that the driver is about to access memory region which potentially overlaps with an operation region right before mapping it. acpi_release_memory() will check if the memory has already been setup (mapped) by acpi core, and deactivate it (unmap) if it has. The driver is then able to map the memory with ioremap_nocache() and set the memtype to uncached for the region. Reported-by: Paul Menzel Fixes: 8243edf44152 ("usb: typec: ucsi: Add ACPI driver") Cc: stable@vger.kernel.org Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_acpi.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 44eb4e1ea817..a18112a83fae 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -79,6 +79,11 @@ static int ucsi_acpi_probe(struct platform_device *pdev) return -ENODEV; } + /* This will make sure we can use ioremap_nocache() */ + status = acpi_release_memory(ACPI_HANDLE(&pdev->dev), res, 1); + if (ACPI_FAILURE(status)) + return -ENOMEM; + /* * NOTE: The memory region for the data structures is used also in an * operation region, which means ACPI has already reserved it. Therefore -- cgit v1.2.3 From 68816e16b4789f2d05e77b6dcb77564cf5d6a8d8 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 21 Jun 2018 16:43:19 +0300 Subject: usb: typec: ucsi: Fix for incorrect status data issue According to UCSI Specification, Connector Change Event only means a change in the Connector Status and Operation Mode fields of the STATUS data structure. So any other change should create another event. Unfortunately on some platforms the firmware acting as PPM (platform policy manager - usually embedded controller firmware) still does not report any other status changes if there is a connector change event. So if the connector power or data role was changed when a device was plugged to the connector, the driver does not get any indication about that. The port will show wrong roles if that happens. To fix the issue, always checking the data and power role together with a connector change event. Fixes: c1b0bc2dabfa ("usb: typec: Add support for UCSI interface") Signed-off-by: Heikki Krogerus Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index bd5cca5632b3..8d0a6fe748bd 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -350,6 +350,19 @@ static void ucsi_connector_change(struct work_struct *work) } if (con->status.change & UCSI_CONSTAT_CONNECT_CHANGE) { + typec_set_pwr_role(con->port, con->status.pwr_dir); + + switch (con->status.partner_type) { + case UCSI_CONSTAT_PARTNER_TYPE_UFP: + typec_set_data_role(con->port, TYPEC_HOST); + break; + case UCSI_CONSTAT_PARTNER_TYPE_DFP: + typec_set_data_role(con->port, TYPEC_DEVICE); + break; + default: + break; + } + if (con->status.connected) ucsi_register_partner(con); else -- cgit v1.2.3 From e16711c32bca59a29d8ef449eda5b8427dc6eb6d Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 20 Jun 2018 17:05:42 -0700 Subject: staging/typec: fix tcpci_rt1711h build errors Fix Kconfig warning and build errors in staging/typec/rt1711h.c. The driver uses I2C interfaces so it should depend on I2C. WARNING: unmet direct dependencies detected for TYPEC_TCPCI Depends on [m]: STAGING [=y] && TYPEC_TCPM [=y] && I2C [=m] Selected by [y]: - TYPEC_RT1711H [=y] && STAGING [=y] && TYPEC_TCPM [=y] and then: drivers/staging/typec/tcpci.o: In function `tcpci_probe': ../drivers/staging/typec/tcpci.c:536: undefined reference to `__devm_regmap_init_i2c' drivers/staging/typec/tcpci.o: In function `tcpci_i2c_driver_init': ../drivers/staging/typec/tcpci.c:593: undefined reference to `i2c_register_driver' drivers/staging/typec/tcpci.o: In function `tcpci_i2c_driver_exit': ../drivers/staging/typec/tcpci.c:593: undefined reference to `i2c_del_driver' drivers/staging/typec/tcpci_rt1711h.o: In function `rt1711h_check_revision': ../drivers/staging/typec/tcpci_rt1711h.c:218: undefined reference to `i2c_smbus_read_word_data' ../drivers/staging/typec/tcpci_rt1711h.c:225: undefined reference to `i2c_smbus_read_word_data' drivers/staging/typec/tcpci_rt1711h.o: In function `rt1711h_probe': ../drivers/staging/typec/tcpci_rt1711h.c:251: undefined reference to `__devm_regmap_init_i2c' drivers/staging/typec/tcpci_rt1711h.o: In function `rt1711h_i2c_driver_init': ../drivers/staging/typec/tcpci_rt1711h.c:308: undefined reference to `i2c_register_driver' drivers/staging/typec/tcpci_rt1711h.o: In function `rt1711h_i2c_driver_exit': ../drivers/staging/typec/tcpci_rt1711h.c:308: undefined reference to `i2c_del_driver' Fixes: ce08eaeb6388 ("staging: typec: rt1711h typec chip driver") Reported-by: kbuild test robot Signed-off-by: Randy Dunlap Cc: ShuFan Lee Cc: kbuild-all@01.org Reviewed-by: Guenter Roeck Revieved-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/staging/typec/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/staging/typec/Kconfig b/drivers/staging/typec/Kconfig index 3aa981fbc8f5..e45ed08a5166 100644 --- a/drivers/staging/typec/Kconfig +++ b/drivers/staging/typec/Kconfig @@ -11,6 +11,7 @@ config TYPEC_TCPCI config TYPEC_RT1711H tristate "Richtek RT1711H Type-C chip driver" + depends on I2C select TYPEC_TCPCI help Richtek RT1711H Type-C chip driver that works with -- cgit v1.2.3 From 4a762569a2722b8a48066c7bacf0e1dc67d17fa1 Mon Sep 17 00:00:00 2001 From: Houston Yaroschoff Date: Mon, 11 Jun 2018 12:39:09 +0200 Subject: usb: cdc_acm: Add quirk for Uniden UBC125 scanner Uniden UBC125 radio scanner has USB interface which fails to work with cdc_acm driver: usb 1-1.5: new full-speed USB device number 4 using xhci_hcd cdc_acm 1-1.5:1.0: Zero length descriptor references cdc_acm: probe of 1-1.5:1.0 failed with error -22 Adding the NO_UNION_NORMAL quirk for the device fixes the issue: usb 1-4: new full-speed USB device number 15 using xhci_hcd usb 1-4: New USB device found, idVendor=1965, idProduct=0018 usb 1-4: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-4: Product: UBC125XLT usb 1-4: Manufacturer: Uniden Corp. usb 1-4: SerialNumber: 0001 cdc_acm 1-4:1.0: ttyACM0: USB ACM device `lsusb -v` of the device: Bus 001 Device 015: ID 1965:0018 Uniden Corporation Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 2 Communications bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x1965 Uniden Corporation idProduct 0x0018 bcdDevice 0.01 iManufacturer 1 Uniden Corp. iProduct 2 UBC125XLT iSerial 3 0001 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 48 bNumInterfaces 2 bConfigurationValue 1 iConfiguration 0 bmAttributes 0x80 (Bus Powered) MaxPower 500mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 2 Communications bInterfaceSubClass 2 Abstract (modem) bInterfaceProtocol 0 None iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x87 EP 7 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0008 1x 8 bytes bInterval 10 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 10 CDC Data bInterfaceSubClass 0 Unused bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Device Status: 0x0000 (Bus Powered) Signed-off-by: Houston Yaroschoff Cc: stable Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 7b366a6c0b49..998b32d0167e 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1758,6 +1758,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x11ca, 0x0201), /* VeriFone Mx870 Gadget Serial */ .driver_info = SINGLE_RX_URB, }, + { USB_DEVICE(0x1965, 0x0018), /* Uniden UBC125XLT */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, -- cgit v1.2.3 From ecc443c03fb14abfb8a6af5e3b2d43b5257e60f2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 7 Jun 2018 15:54:48 +0200 Subject: NFC: pn533: Fix wrong GFP flag usage pn533_recv_response() is an urb completion handler, so it must use GFP_ATOMIC. pn533_usb_send_frame() OTOH runs from a regular sleeping context, so the pn533_submit_urb_for_response() there (and only there) can use the regular GFP_KERNEL flags. BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1514134 Fixes: 9815c7cf22da ("NFC: pn533: Separate physical layer from ...") Cc: Michael Thalmeier Signed-off-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- drivers/nfc/pn533/usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/pn533/usb.c b/drivers/nfc/pn533/usb.c index d5553c47014f..5d823e965883 100644 --- a/drivers/nfc/pn533/usb.c +++ b/drivers/nfc/pn533/usb.c @@ -74,7 +74,7 @@ static void pn533_recv_response(struct urb *urb) struct sk_buff *skb = NULL; if (!urb->status) { - skb = alloc_skb(urb->actual_length, GFP_KERNEL); + skb = alloc_skb(urb->actual_length, GFP_ATOMIC); if (!skb) { nfc_err(&phy->udev->dev, "failed to alloc memory\n"); } else { @@ -186,7 +186,7 @@ static int pn533_usb_send_frame(struct pn533 *dev, if (dev->protocol_type == PN533_PROTO_REQ_RESP) { /* request for response for sent packet directly */ - rc = pn533_submit_urb_for_response(phy, GFP_ATOMIC); + rc = pn533_submit_urb_for_response(phy, GFP_KERNEL); if (rc) goto error; } else if (dev->protocol_type == PN533_PROTO_REQ_ACK_RESP) { -- cgit v1.2.3 From 9578bcd0bb487b8ecef4b7eee799aafb678aa441 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 7 Jun 2018 16:17:14 +0300 Subject: typec: tcpm: Fix a msecs vs jiffies bug The tcpm_set_state() function take msecs not jiffies. Fixes: f0690a25a140 ("staging: typec: USB Type-C Port Manager (tcpm)") Signed-off-by: Dan Carpenter Acked-by: Heikki Krogerus Reviewed-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c index 8a201dd53d36..0dfd755020f4 100644 --- a/drivers/usb/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -3043,7 +3043,8 @@ static void run_state_machine(struct tcpm_port *port) tcpm_port_is_sink(port) && time_is_after_jiffies(port->delayed_runtime)) { tcpm_set_state(port, SNK_DISCOVERY, - port->delayed_runtime - jiffies); + jiffies_to_msecs(port->delayed_runtime - + jiffies)); break; } tcpm_set_state(port, unattached_state(port), 0); -- cgit v1.2.3 From d5a4f93511b7000183c0d528739b824752139f79 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 12 Jun 2018 09:53:01 +0800 Subject: usb: typec: tcpm: fix logbuffer index is wrong if _tcpm_log is re-entered The port->logbuffer_head may be wrong if the two processes enters _tcpm_log at the mostly same time. The 2nd process enters _tcpm_log before the 1st process update the index, then the 2nd process will not allocate logbuffer, when the 2nd process tries to use log buffer, the index has already updated by the 1st process, so it will get NULL pointer for updated logbuffer, the error message like below: tcpci 0-0050: Log buffer index 6 is NULL Cc: Heikki Krogerus Cc: Guenter Roeck Cc: Jun Li Signed-off-by: Peter Chen Reviewed-by: Heikki Krogerus Cc: stable Reviewed-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c index 0dfd755020f4..d961f1ec0e08 100644 --- a/drivers/usb/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -418,17 +418,18 @@ static void _tcpm_log(struct tcpm_port *port, const char *fmt, va_list args) u64 ts_nsec = local_clock(); unsigned long rem_nsec; + mutex_lock(&port->logbuffer_lock); if (!port->logbuffer[port->logbuffer_head]) { port->logbuffer[port->logbuffer_head] = kzalloc(LOG_BUFFER_ENTRY_SIZE, GFP_KERNEL); - if (!port->logbuffer[port->logbuffer_head]) + if (!port->logbuffer[port->logbuffer_head]) { + mutex_unlock(&port->logbuffer_lock); return; + } } vsnprintf(tmpbuffer, sizeof(tmpbuffer), fmt, args); - mutex_lock(&port->logbuffer_lock); - if (tcpm_log_full(port)) { port->logbuffer_head = max(port->logbuffer_head - 1, 0); strcpy(tmpbuffer, "overflow"); -- cgit v1.2.3 From 1376b0a2160319125c3a2822e8c09bd283cd8141 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 5 Jun 2018 12:36:30 +0300 Subject: staging: comedi: quatech_daqp_cs: fix no-op loop daqp_ao_insn_write() There is a '>' vs '<' typo so this loop is a no-op. Fixes: d35dcc89fc93 ("staging: comedi: quatech_daqp_cs: fix daqp_ao_insn_write()") Signed-off-by: Dan Carpenter Reviewed-by: Ian Abbott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/quatech_daqp_cs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/comedi/drivers/quatech_daqp_cs.c b/drivers/staging/comedi/drivers/quatech_daqp_cs.c index ea194aa01a64..257b0daff01f 100644 --- a/drivers/staging/comedi/drivers/quatech_daqp_cs.c +++ b/drivers/staging/comedi/drivers/quatech_daqp_cs.c @@ -642,7 +642,7 @@ static int daqp_ao_insn_write(struct comedi_device *dev, /* Make sure D/A update mode is direct update */ outb(0, dev->iobase + DAQP_AUX_REG); - for (i = 0; i > insn->n; i++) { + for (i = 0; i < insn->n; i++) { unsigned int val = data[i]; int ret; -- cgit v1.2.3 From 0a2bc00341dcfcc793c0dbf4f8d43adf60458b05 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Mon, 11 Jun 2018 11:06:53 -0700 Subject: staging: android: ion: Return an ERR_PTR in ion_map_kernel The expected return value from ion_map_kernel is an ERR_PTR. The error path for a vmalloc failure currently just returns NULL, triggering a warning in ion_buffer_kmap_get. Encode the vmalloc failure as an ERR_PTR. Reported-by: syzbot+55b1d9f811650de944c6@syzkaller.appspotmail.com Signed-off-by: Laura Abbott Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/ion/ion_heap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/android/ion/ion_heap.c b/drivers/staging/android/ion/ion_heap.c index e8c440329708..31db510018a9 100644 --- a/drivers/staging/android/ion/ion_heap.c +++ b/drivers/staging/android/ion/ion_heap.c @@ -30,7 +30,7 @@ void *ion_heap_map_kernel(struct ion_heap *heap, struct page **tmp = pages; if (!pages) - return NULL; + return ERR_PTR(-ENOMEM); if (buffer->flags & ION_FLAG_CACHED) pgprot = PAGE_KERNEL; -- cgit v1.2.3 From 90f26cc6bb90b35040f4da0347f480ea9df6e2fc Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 6 Jun 2018 09:43:27 +0800 Subject: usb: chipidea: host: fix disconnection detect issue The commit 4e88d4c08301 ("usb: add a flag to skip PHY initialization to struct usb_hcd") delete the assignment for hcd->usb_phy, it causes usb_phy_notify_connect{disconnect) are not called, the USB PHY driver is not notified of hot plug event, then the disconnection will not be detected by hardware. Fixes: 4e88d4c08301 ("usb: add a flag to skip PHY initialization to struct usb_hcd") Acked-by: Martin Blumenstingl Reported-by: Mats Karrman Tested-by: Mats Karrman Signed-off-by: Peter Chen --- drivers/usb/chipidea/host.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index af45aa3222b5..4638d9b066be 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -124,8 +124,11 @@ static int host_start(struct ci_hdrc *ci) hcd->power_budget = ci->platdata->power_budget; hcd->tpl_support = ci->platdata->tpl_support; - if (ci->phy || ci->usb_phy) + if (ci->phy || ci->usb_phy) { hcd->skip_phy_initialization = 1; + if (ci->usb_phy) + hcd->usb_phy = ci->usb_phy; + } ehci = hcd_to_ehci(hcd); ehci->caps = ci->hw_bank.cap; -- cgit v1.2.3 From 0ea3fa15b12739c5455cd5850ada4e9948171213 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 25 May 2018 18:08:24 +0200 Subject: qcom: cmd-db: enforce CONFIG_OF_RESERVED_MEM dependency Without CONFIG_OF_RESERVED_MEM, gcc sees that the global cmd_db_header variable is never initialized, and through code optimization concludes that a lot of other code cannot possibly work after that: drivers/soc/qcom/cmd-db.c: In function 'cmd_db_read_addr': drivers/soc/qcom/cmd-db.c:197:21: error: 'ent.addr' may be used uninitialized in this function [-Werror=maybe-uninitialized] return ret < 0 ? 0 : le32_to_cpu(ent.addr); drivers/soc/qcom/cmd-db.c: In function 'cmd_db_read_aux_data': drivers/soc/qcom/cmd-db.c:224:10: error: 'ent.len' may be used uninitialized in this function [-Werror=maybe-uninitialized] ent_len = le16_to_cpu(ent.len); drivers/soc/qcom/cmd-db.c:115:6: error: 'rsc_hdr.data_offset' may be used uninitialized in this function [-Werror=maybe-uninitialized] u16 offset = le16_to_cpu(hdr->data_offset); ^~~~~~ drivers/soc/qcom/cmd-db.c:116:6: error: 'ent.offset' may be used uninitialized in this function [-Werror=maybe-uninitialized] u16 loffset = le16_to_cpu(ent->offset); ^~~~~~~ drivers/soc/qcom/cmd-db.c: In function 'cmd_db_read_aux_data_len': drivers/soc/qcom/cmd-db.c:250:38: error: 'ent.len' may be used uninitialized in this function [-Werror=maybe-uninitialized] return ret < 0 ? 0 : le16_to_cpu(ent.len); ^ drivers/soc/qcom/cmd-db.c: In function 'cmd_db_read_slave_id': drivers/soc/qcom/cmd-db.c:272:7: error: 'ent.addr' may be used uninitialized in this function [-Werror=maybe-uninitialized] Using a hard CONFIG_OF_RESERVED_MEM dependency avoids this warning, and we can remove the CONFIG_OF dependency. Signed-off-by: Arnd Bergmann Reviewed-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/soc/qcom/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 9dc02f390ba3..5856e792d09c 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -5,7 +5,8 @@ menu "Qualcomm SoC drivers" config QCOM_COMMAND_DB bool "Qualcomm Command DB" - depends on (ARCH_QCOM && OF) || COMPILE_TEST + depends on ARCH_QCOM || COMPILE_TEST + depends on OF_RESERVED_MEM help Command DB queries shared memory by key string for shared system resources. Platform drivers that require to set state of a shared -- cgit v1.2.3 From 01a9e9493fb3f65c07077d59cf8ba8b6d2e0463e Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 11 Jun 2018 18:53:35 +0200 Subject: drm/meson: Fix an un-handled error path in 'meson_drv_bind_master()' If 'platform_get_resource_byname()' fails, we should release some resources before leaving, as already done in the other error handling path of the function. Fixes: acaa3f13b8dd ("drm/meson: Fix potential NULL dereference in meson_drv_bind_master()") Signed-off-by: Christophe JAILLET Acked-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20180611165335.24542-1-christophe.jaillet@wanadoo.fr --- drivers/gpu/drm/meson/meson_drv.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index 32b1a6cdecfc..d3443125e661 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -197,8 +197,10 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) priv->io_base = regs; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi"); - if (!res) - return -EINVAL; + if (!res) { + ret = -EINVAL; + goto free_drm; + } /* Simply ioremap since it may be a shared register zone */ regs = devm_ioremap(dev, res->start, resource_size(res)); if (!regs) { @@ -215,8 +217,10 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc"); - if (!res) - return -EINVAL; + if (!res) { + ret = -EINVAL; + goto free_drm; + } /* Simply ioremap since it may be a shared register zone */ regs = devm_ioremap(dev, res->start, resource_size(res)); if (!regs) { -- cgit v1.2.3 From 6f6f42466d902c92f21b46a45e6af22d1d663607 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 25 Jun 2018 08:17:33 -0500 Subject: PCI: shpchp: Manage SHPC unconditionally on non-ACPI systems An SHPC can be operated either by platform firmware or by the OS. The OS uses a host bridge ACPI _OSC method to negotiate for control of SHPC. If firmware wants to prevent an OS from operating an SHPC, it must supply an _OSC method that declines to grant SHPC ownership to the OS. If acpi_pci_find_root() returns NULL, it means there's no ACPI host bridge device (PNP0A03 or PNP0A08) and hence no _OSC method, so the OS is always allowed to manage the SHPC. Fix a NULL pointer dereference when CONFIG_ACPI=y but the current hardware/firmware platform doesn't support ACPI. In that case, acpi_get_hp_hw_control_from_firmware() is implemented but acpi_pci_find_root() returns NULL. Fixes: 90cc0c3cc709 ("PCI: shpchp: Add shpchp_is_native()") Link: https://lkml.kernel.org/r/20180621164715.28160-1-marc.zyngier@arm.com Reported-by: Marc Zyngier Tested-by: Marc Zyngier Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg --- drivers/pci/hotplug/acpi_pcihp.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index 3979f89b250a..5bd6c1573295 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -7,7 +7,6 @@ * All rights reserved. * * Send feedback to - * */ #include @@ -87,8 +86,17 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev) return 0; /* If _OSC exists, we should not evaluate OSHP */ + + /* + * If there's no ACPI host bridge (i.e., ACPI support is compiled + * into the kernel but the hardware platform doesn't support ACPI), + * there's nothing to do here. + */ host = pci_find_host_bridge(pdev->bus); root = acpi_pci_find_root(ACPI_HANDLE(&host->dev)); + if (!root) + return 0; + if (root->osc_support_set) goto no_control; -- cgit v1.2.3 From 68bb22e9d561ad7c20274eee92058048ac689b98 Mon Sep 17 00:00:00 2001 From: Alan Douglas Date: Fri, 22 Jun 2018 17:17:17 +0100 Subject: PCI: Initialize endpoint library before controllers The endpoint library must be initialized before its users, which are in drivers/pci/controllers. The endpoint initialization currently depends on link order. This corrects a kernel crash when loading the Cadence EP driver, since it calls devm_pci_epc_create() and this is only valid once the endpoint library has been initialized. Fixes: 6e0832fa432e ("PCI: Collect all native drivers under drivers/pci/controller/") Signed-off-by: Alan Douglas Signed-off-by: Bjorn Helgaas --- drivers/pci/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 535201984b8b..1b2cfe51e8d7 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -28,10 +28,10 @@ obj-$(CONFIG_PCI_PF_STUB) += pci-pf-stub.o obj-$(CONFIG_PCI_ECAM) += ecam.o obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o -obj-y += controller/ -obj-y += switch/ - # Endpoint library must be initialized before its users obj-$(CONFIG_PCI_ENDPOINT) += endpoint/ +obj-y += controller/ +obj-y += switch/ + ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG -- cgit v1.2.3 From 925d31668d5b6c9849c50167f31f9b5cf9eec892 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Tue, 19 Jun 2018 12:21:05 +0100 Subject: PCI: controller: Move PCI_DOMAINS selection to arch Kconfig Commit 51bc085d6454 ("PCI: Improve host drivers compile test coverage") added configuration options to allow PCI host controller drivers to be compile tested on all architectures. Some host controller drivers (eg PCIE_ALTERA) config entries select the PCI_DOMAINS config option to enable PCI domains management in the kernel. Now that host controller drivers can be compiled on all architectures, this triggers build regressions on arches that do not implement the PCI_DOMAINS required API (ie pci_domain_nr()): drivers/ata/pata_ali.c: In function 'ali_init_chipset': drivers/ata/pata_ali.c:469:38: error: implicit declaration of function 'pci_domain_nr'; did you mean 'pci_iomap_wc'? Furthemore, some software configurations (ie Jailhouse) require a PCI_DOMAINS enabled kernel to configure multiple host controllers without having an explicit dependency on the ARM platform on which they run. Make PCI_DOMAINS a visible configuration option on ARM so that software configurations that need it can manually select it and move the PCI_DOMAINS selection from PCI controllers configuration file to ARM sub-arch config entries that currently require it, fixing the issue. Fixes: 51bc085d6454 ("PCI: Improve host drivers compile test coverage") Link: https://lkml.kernel.org/r/20180612170229.GA10141@roeck-us.net Reported-by: Guenter Roeck Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Acked-by: Jan Kiszka Acked-by: Ley Foon Tan Acked-by: Rob Herring Cc: Scott Branden Cc: Will Deacon Cc: Russell King Cc: Guenter Roeck --- arch/arm/Kconfig | 8 +++++++- arch/arm/mach-bcm/Kconfig | 1 + arch/arm/mach-socfpga/Kconfig | 1 + drivers/pci/controller/Kconfig | 3 --- 4 files changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 54eeb8d00bc6..843edfd000be 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1245,8 +1245,14 @@ config PCI VESA. If you have PCI, say Y, otherwise N. config PCI_DOMAINS - bool + bool "Support for multiple PCI domains" depends on PCI + help + Enable PCI domains kernel management. Say Y if your machine + has a PCI bus hierarchy that requires more than one PCI + domain (aka segment) to be correctly managed. Say N otherwise. + + If you don't know what to do here, say N. config PCI_DOMAINS_GENERIC def_bool PCI_DOMAINS diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig index c46a728df44e..25aac6ee2ab1 100644 --- a/arch/arm/mach-bcm/Kconfig +++ b/arch/arm/mach-bcm/Kconfig @@ -20,6 +20,7 @@ config ARCH_BCM_IPROC select GPIOLIB select ARM_AMBA select PINCTRL + select PCI_DOMAINS if PCI help This enables support for systems based on Broadcom IPROC architected SoCs. The IPROC complex contains one or more ARM CPUs along with common diff --git a/arch/arm/mach-socfpga/Kconfig b/arch/arm/mach-socfpga/Kconfig index d0f62eacf59d..4adb901dd5eb 100644 --- a/arch/arm/mach-socfpga/Kconfig +++ b/arch/arm/mach-socfpga/Kconfig @@ -10,6 +10,7 @@ menuconfig ARCH_SOCFPGA select HAVE_ARM_SCU select HAVE_ARM_TWD if SMP select MFD_SYSCON + select PCI_DOMAINS if PCI if ARCH_SOCFPGA config SOCFPGA_SUSPEND diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 18fa09b3ac8f..cc9fa02d32a0 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -96,7 +96,6 @@ config PCI_HOST_GENERIC depends on OF select PCI_HOST_COMMON select IRQ_DOMAIN - select PCI_DOMAINS help Say Y here if you want to support a simple generic PCI host controller, such as the one emulated by kvmtool. @@ -138,7 +137,6 @@ config PCI_VERSATILE config PCIE_IPROC tristate - select PCI_DOMAINS help This enables the iProc PCIe core controller support for Broadcom's iProc family of SoCs. An appropriate bus interface driver needs @@ -176,7 +174,6 @@ config PCIE_IPROC_MSI config PCIE_ALTERA bool "Altera PCIe controller" depends on ARM || NIOS2 || COMPILE_TEST - select PCI_DOMAINS help Say Y here if you want to enable PCIe controller support on Altera FPGA. -- cgit v1.2.3 From 03ae3a9caf4a59edd32b65c89c375a98ce3ea1ef Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 25 Jun 2018 12:02:40 -0700 Subject: Input: psmouse - fix button reporting for basic protocols The commit ba667650c568 ("Input: psmouse - clean up code") was pretty brain-dead and broke extra buttons reporting for variety of PS/2 mice: Genius, Thinkmouse and Intellimouse Explorer. We need to actually inspect the data coming from the device when reporting events. Fixes: ba667650c568 ("Input: psmouse - clean up code") Reported-by: Jiri Slaby Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 5ff5b1952be0..d3ff1fc09af7 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -192,8 +192,8 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) else input_report_rel(dev, REL_WHEEL, -wheel); - input_report_key(dev, BTN_SIDE, BIT(4)); - input_report_key(dev, BTN_EXTRA, BIT(5)); + input_report_key(dev, BTN_SIDE, packet[3] & BIT(4)); + input_report_key(dev, BTN_EXTRA, packet[3] & BIT(5)); break; } break; @@ -203,13 +203,13 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) input_report_rel(dev, REL_WHEEL, -(s8) packet[3]); /* Extra buttons on Genius NewNet 3D */ - input_report_key(dev, BTN_SIDE, BIT(6)); - input_report_key(dev, BTN_EXTRA, BIT(7)); + input_report_key(dev, BTN_SIDE, packet[0] & BIT(6)); + input_report_key(dev, BTN_EXTRA, packet[0] & BIT(7)); break; case PSMOUSE_THINKPS: /* Extra button on ThinkingMouse */ - input_report_key(dev, BTN_EXTRA, BIT(3)); + input_report_key(dev, BTN_EXTRA, packet[0] & BIT(3)); /* * Without this bit of weirdness moving up gives wildly @@ -223,7 +223,7 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) * Cortron PS2 Trackball reports SIDE button in the * 4th bit of the first byte. */ - input_report_key(dev, BTN_SIDE, BIT(3)); + input_report_key(dev, BTN_SIDE, packet[0] & BIT(3)); packet[0] |= BIT(3); break; -- cgit v1.2.3 From a685557fbbc3122ed11e8ad3fa63a11ebc5de8c3 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Tue, 26 Jun 2018 12:04:23 -0400 Subject: dm thin: handle running out of data space vs concurrent discard Discards issued to a DM thin device can complete to userspace (via fstrim) _before_ the metadata changes associated with the discards is reflected in the thinp superblock (e.g. free blocks). As such, if a user constructs a test that loops repeatedly over these steps, block allocation can fail due to discards not having completed yet: 1) fill thin device via filesystem file 2) remove file 3) fstrim From initial report, here: https://www.redhat.com/archives/dm-devel/2018-April/msg00022.html "The root cause of this issue is that dm-thin will first remove mapping and increase corresponding blocks' reference count to prevent them from being reused before DISCARD bios get processed by the underlying layers. However. increasing blocks' reference count could also increase the nr_allocated_this_transaction in struct sm_disk which makes smd->old_ll.nr_allocated + smd->nr_allocated_this_transaction bigger than smd->old_ll.nr_blocks. In this case, alloc_data_block() will never commit metadata to reset the begin pointer of struct sm_disk, because sm_disk_get_nr_free() always return an underflow value." While there is room for improvement to the space-map accounting that thinp is making use of: the reality is this test is inherently racey and will result in the previous iteration's fstrim's discard(s) completing vs concurrent block allocation, via dd, in the next iteration of the loop. No amount of space map accounting improvements will be able to allow user's to use a block before a discard of that block has completed. So the best we can really do is allow DM thinp to gracefully handle such aggressive use of all the pool's data by degrading the pool into out-of-data-space (OODS) mode. We _should_ get that behaviour already (if space map accounting didn't falsely cause alloc_data_block() to believe free space was available).. but short of that we handle the current reality that dm_pool_alloc_data_block() can return -ENOSPC. Reported-by: Dennis Yang Cc: stable@vger.kernel.org Signed-off-by: Mike Snitzer --- drivers/md/dm-thin.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 7945238df1c0..b900723bbd0f 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -1386,6 +1386,8 @@ static void schedule_external_copy(struct thin_c *tc, dm_block_t virt_block, static void set_pool_mode(struct pool *pool, enum pool_mode new_mode); +static void requeue_bios(struct pool *pool); + static void check_for_space(struct pool *pool) { int r; @@ -1398,8 +1400,10 @@ static void check_for_space(struct pool *pool) if (r) return; - if (nr_free) + if (nr_free) { set_pool_mode(pool, PM_WRITE); + requeue_bios(pool); + } } /* @@ -1476,7 +1480,10 @@ static int alloc_data_block(struct thin_c *tc, dm_block_t *result) r = dm_pool_alloc_data_block(pool->pmd, result); if (r) { - metadata_operation_failed(pool, "dm_pool_alloc_data_block", r); + if (r == -ENOSPC) + set_pool_mode(pool, PM_OUT_OF_DATA_SPACE); + else + metadata_operation_failed(pool, "dm_pool_alloc_data_block", r); return r; } -- cgit v1.2.3 From d9e98ee248b04b2462e7411b204d420d04f1ba27 Mon Sep 17 00:00:00 2001 From: Leo Liu Date: Mon, 25 Jun 2018 14:56:06 -0400 Subject: drm/amdgpu: fix UBSAN: Undefined behaviour for amdgpu_fence.c Here is the UBSAN dump: [ 3.866656] index 2 is out of range for type 'amdgpu_uvd_inst [2]' [ 3.866693] Workqueue: events work_for_cpu_fn [ 3.866702] Call Trace: [ 3.866710] dump_stack+0x85/0xc5 [ 3.866719] ubsan_epilogue+0x9/0x40 [ 3.866727] __ubsan_handle_out_of_bounds+0x89/0x90 [ 3.866737] ? rcu_read_lock_sched_held+0x58/0x60 [ 3.866746] ? __kmalloc+0x26c/0x2d0 [ 3.866846] amdgpu_fence_driver_start_ring+0x259/0x280 [amdgpu] [ 3.866896] amdgpu_ring_init+0x12c/0x710 [amdgpu] [ 3.866906] ? sprintf+0x42/0x50 [ 3.866956] amdgpu_gfx_kiq_init_ring+0x1bc/0x3a0 [amdgpu] [ 3.867009] gfx_v8_0_sw_init+0x1ad3/0x2360 [amdgpu] [ 3.867062] ? smu7_init+0xec/0x160 [amdgpu] [ 3.867109] amdgpu_device_init+0x112c/0x1dc0 [amdgpu] 'ring->me' might be set as 2 with 'amdgpu_gfx_kiq_init_ring', that would cause out of range for 'amdgpu_uvd_inst[2]'. v2: simplified with ring type Signed-off-by: Leo Liu Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c index 39ec6b8890a1..e74d620d9699 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c @@ -376,7 +376,7 @@ int amdgpu_fence_driver_start_ring(struct amdgpu_ring *ring, struct amdgpu_device *adev = ring->adev; uint64_t index; - if (ring != &adev->uvd.inst[ring->me].ring) { + if (ring->funcs->type != AMDGPU_RING_TYPE_UVD) { ring->fence_drv.cpu_addr = &adev->wb.wb[ring->fence_offs]; ring->fence_drv.gpu_addr = adev->wb.gpu_addr + (ring->fence_offs * 4); } else { -- cgit v1.2.3 From 62d5b8e33b774128c93baac35a4347a6cfb64434 Mon Sep 17 00:00:00 2001 From: James Zhu Date: Tue, 19 Jun 2018 13:44:04 -0400 Subject: drm/amdgpu:Support new VCN FW version naming convention Support new VCN FW version naming convention: [31, 28] for VEP interface major version if applicable [27, 24] for decode interface major version [23, 20] for encode interface major version [19, 12] for encode interface minor version [11, 0] for firmware revision Bit 20-23, it is encode major and non-zero for new naming convention. This field is part of version minor and DRM_DISABLED_FLAG in old naming convention. Since the latest version minor is 0x5B and DRM_DISABLED_FLAG is zero in old naming convention, this field is always zero so far. These four bits are used to tell which naming convention is present. Signed-off-by: James Zhu Reviewed-by: Fang, Peter Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c index 127e87b470ff..1b4ad9b2a755 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c @@ -52,7 +52,7 @@ int amdgpu_vcn_sw_init(struct amdgpu_device *adev) unsigned long bo_size; const char *fw_name; const struct common_firmware_header *hdr; - unsigned version_major, version_minor, family_id; + unsigned char fw_check; int r; INIT_DELAYED_WORK(&adev->vcn.idle_work, amdgpu_vcn_idle_work_handler); @@ -83,12 +83,33 @@ int amdgpu_vcn_sw_init(struct amdgpu_device *adev) hdr = (const struct common_firmware_header *)adev->vcn.fw->data; adev->vcn.fw_version = le32_to_cpu(hdr->ucode_version); - family_id = le32_to_cpu(hdr->ucode_version) & 0xff; - version_major = (le32_to_cpu(hdr->ucode_version) >> 24) & 0xff; - version_minor = (le32_to_cpu(hdr->ucode_version) >> 8) & 0xff; - DRM_INFO("Found VCN firmware Version: %hu.%hu Family ID: %hu\n", - version_major, version_minor, family_id); + /* Bit 20-23, it is encode major and non-zero for new naming convention. + * This field is part of version minor and DRM_DISABLED_FLAG in old naming + * convention. Since the l:wq!atest version minor is 0x5B and DRM_DISABLED_FLAG + * is zero in old naming convention, this field is always zero so far. + * These four bits are used to tell which naming convention is present. + */ + fw_check = (le32_to_cpu(hdr->ucode_version) >> 20) & 0xf; + if (fw_check) { + unsigned int dec_ver, enc_major, enc_minor, vep, fw_rev; + + fw_rev = le32_to_cpu(hdr->ucode_version) & 0xfff; + enc_minor = (le32_to_cpu(hdr->ucode_version) >> 12) & 0xff; + enc_major = fw_check; + dec_ver = (le32_to_cpu(hdr->ucode_version) >> 24) & 0xf; + vep = (le32_to_cpu(hdr->ucode_version) >> 28) & 0xf; + DRM_INFO("Found VCN firmware Version ENC: %hu.%hu DEC: %hu VEP: %hu Revision: %hu\n", + enc_major, enc_minor, dec_ver, vep, fw_rev); + } else { + unsigned int version_major, version_minor, family_id; + + family_id = le32_to_cpu(hdr->ucode_version) & 0xff; + version_major = (le32_to_cpu(hdr->ucode_version) >> 24) & 0xff; + version_minor = (le32_to_cpu(hdr->ucode_version) >> 8) & 0xff; + DRM_INFO("Found VCN firmware Version: %hu.%hu Family ID: %hu\n", + version_major, version_minor, family_id); + } bo_size = AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8) + AMDGPU_VCN_STACK_SIZE + AMDGPU_VCN_HEAP_SIZE -- cgit v1.2.3 From 4de9f38bb2cce3a4821ffb8a83d6b08f6e37d905 Mon Sep 17 00:00:00 2001 From: Shirish S Date: Tue, 26 Jun 2018 09:32:39 +0530 Subject: drm/amd/display: release spinlock before committing updates to stream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, amdgpu_do_flip() spinlocks crtc->dev->event_lock and releases it only after committing updates to the stream. dc_commit_updates_for_stream() should be moved out of spinlock for the below reasons: 1. event_lock is supposed to protect access to acrct->pflip_status _only_ 2. dc_commit_updates_for_stream() has potential sleep's and also its not appropriate to be in an atomic state for such long sequences of code. Signed-off-by: Shirish S Suggested-by: Andrey Grodzovsky Reviewed-by: Michel Dänzer Reviewed-by: Harry Wentland Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 689dbdf44bbf..3a8d6356afc2 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3928,10 +3928,11 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc, if (acrtc->base.state->event) prepare_flip_isr(acrtc); + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + surface_updates->surface = dc_stream_get_status(acrtc_state->stream)->plane_states[0]; surface_updates->flip_addr = &addr; - dc_commit_updates_for_stream(adev->dm.dc, surface_updates, 1, @@ -3944,9 +3945,6 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc, __func__, addr.address.grph.addr.high_part, addr.address.grph.addr.low_part); - - - spin_unlock_irqrestore(&crtc->dev->event_lock, flags); } /* -- cgit v1.2.3 From 3d63b7e4ae0dc5e02d28ddd2fa1f945defc68d81 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sat, 26 May 2018 09:53:13 +0900 Subject: n_tty: Fix stall at n_tty_receive_char_special(). syzbot is reporting stalls at n_tty_receive_char_special() [1]. This is because comparison is not working as expected since ldata->read_head can change at any moment. Mitigate this by explicitly masking with buffer size when checking condition for "while" loops. [1] https://syzkaller.appspot.com/bug?id=3d7481a346958d9469bebbeb0537d5f056bdd6e8 Signed-off-by: Tetsuo Handa Reported-by: syzbot Fixes: bc5a5e3f45d04784 ("n_tty: Don't wrap input buffer indices at buffer size") Cc: stable Cc: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index cbe98bc2b998..b279f8730e04 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -124,6 +124,8 @@ struct n_tty_data { struct mutex output_lock; }; +#define MASK(x) ((x) & (N_TTY_BUF_SIZE - 1)) + static inline size_t read_cnt(struct n_tty_data *ldata) { return ldata->read_head - ldata->read_tail; @@ -978,14 +980,15 @@ static void eraser(unsigned char c, struct tty_struct *tty) } seen_alnums = 0; - while (ldata->read_head != ldata->canon_head) { + while (MASK(ldata->read_head) != MASK(ldata->canon_head)) { head = ldata->read_head; /* erase a single possibly multibyte character */ do { head--; c = read_buf(ldata, head); - } while (is_continuation(c, tty) && head != ldata->canon_head); + } while (is_continuation(c, tty) && + MASK(head) != MASK(ldata->canon_head)); /* do not partially erase */ if (is_continuation(c, tty)) @@ -1027,7 +1030,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) * This info is used to go back the correct * number of columns. */ - while (tail != ldata->canon_head) { + while (MASK(tail) != MASK(ldata->canon_head)) { tail--; c = read_buf(ldata, tail); if (c == '\t') { @@ -1302,7 +1305,7 @@ n_tty_receive_char_special(struct tty_struct *tty, unsigned char c) finish_erasing(ldata); echo_char(c, tty); echo_char_raw('\n', ldata); - while (tail != ldata->read_head) { + while (MASK(tail) != MASK(ldata->read_head)) { echo_char(read_buf(ldata, tail), tty); tail++; } @@ -2411,7 +2414,7 @@ static unsigned long inq_canon(struct n_tty_data *ldata) tail = ldata->read_tail; nr = head - tail; /* Skip EOF-chars.. */ - while (head != tail) { + while (MASK(head) != MASK(tail)) { if (test_bit(tail & (N_TTY_BUF_SIZE - 1), ldata->read_flags) && read_buf(ldata, tail) == __DISABLED_CHAR) nr--; -- cgit v1.2.3 From ebec3f8f5271139df618ebdf8427e24ba102ba94 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sat, 26 May 2018 09:53:14 +0900 Subject: n_tty: Access echo_* variables carefully. syzbot is reporting stalls at __process_echoes() [1]. This is because since ldata->echo_commit < ldata->echo_tail becomes true for some reason, the discard loop is serving as almost infinite loop. This patch tries to avoid falling into ldata->echo_commit < ldata->echo_tail situation by making access to echo_* variables more carefully. Since reset_buffer_flags() is called without output_lock held, it should not touch echo_* variables. And omit a call to reset_buffer_flags() from n_tty_open() by using vzalloc(). Since add_echo_byte() is called without output_lock held, it needs memory barrier between storing into echo_buf[] and incrementing echo_head counter. echo_buf() needs corresponding memory barrier before reading echo_buf[]. Lack of handling the possibility of not-yet-stored multi-byte operation might be the reason of falling into ldata->echo_commit < ldata->echo_tail situation, for if I do WARN_ON(ldata->echo_commit == tail + 1) prior to echo_buf(ldata, tail + 1), the WARN_ON() fires. Also, explicitly masking with buffer for the former "while" loop, and use ldata->echo_commit > tail for the latter "while" loop. [1] https://syzkaller.appspot.com/bug?id=17f23b094cd80df750e5b0f8982c521ee6bcbf40 Signed-off-by: Tetsuo Handa Reported-by: syzbot Cc: Peter Hurley Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index b279f8730e04..431742201709 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -143,6 +143,7 @@ static inline unsigned char *read_buf_addr(struct n_tty_data *ldata, size_t i) static inline unsigned char echo_buf(struct n_tty_data *ldata, size_t i) { + smp_rmb(); /* Matches smp_wmb() in add_echo_byte(). */ return ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; } @@ -318,9 +319,7 @@ static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata) static void reset_buffer_flags(struct n_tty_data *ldata) { ldata->read_head = ldata->canon_head = ldata->read_tail = 0; - ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0; ldata->commit_head = 0; - ldata->echo_mark = 0; ldata->line_start = 0; ldata->erasing = 0; @@ -619,12 +618,19 @@ static size_t __process_echoes(struct tty_struct *tty) old_space = space = tty_write_room(tty); tail = ldata->echo_tail; - while (ldata->echo_commit != tail) { + while (MASK(ldata->echo_commit) != MASK(tail)) { c = echo_buf(ldata, tail); if (c == ECHO_OP_START) { unsigned char op; int no_space_left = 0; + /* + * Since add_echo_byte() is called without holding + * output_lock, we might see only portion of multi-byte + * operation. + */ + if (MASK(ldata->echo_commit) == MASK(tail + 1)) + goto not_yet_stored; /* * If the buffer byte is the start of a multi-byte * operation, get the next byte, which is either the @@ -636,6 +642,8 @@ static size_t __process_echoes(struct tty_struct *tty) unsigned int num_chars, num_bs; case ECHO_OP_ERASE_TAB: + if (MASK(ldata->echo_commit) == MASK(tail + 2)) + goto not_yet_stored; num_chars = echo_buf(ldata, tail + 2); /* @@ -730,7 +738,8 @@ static size_t __process_echoes(struct tty_struct *tty) /* If the echo buffer is nearly full (so that the possibility exists * of echo overrun before the next commit), then discard enough * data at the tail to prevent a subsequent overrun */ - while (ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) { + while (ldata->echo_commit > tail && + ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) { if (echo_buf(ldata, tail) == ECHO_OP_START) { if (echo_buf(ldata, tail + 1) == ECHO_OP_ERASE_TAB) tail += 3; @@ -740,6 +749,7 @@ static size_t __process_echoes(struct tty_struct *tty) tail++; } + not_yet_stored: ldata->echo_tail = tail; return old_space - space; } @@ -750,6 +760,7 @@ static void commit_echoes(struct tty_struct *tty) size_t nr, old, echoed; size_t head; + mutex_lock(&ldata->output_lock); head = ldata->echo_head; ldata->echo_mark = head; old = ldata->echo_commit - ldata->echo_tail; @@ -758,10 +769,12 @@ static void commit_echoes(struct tty_struct *tty) * is over the threshold (and try again each time another * block is accumulated) */ nr = head - ldata->echo_tail; - if (nr < ECHO_COMMIT_WATERMARK || (nr % ECHO_BLOCK > old % ECHO_BLOCK)) + if (nr < ECHO_COMMIT_WATERMARK || + (nr % ECHO_BLOCK > old % ECHO_BLOCK)) { + mutex_unlock(&ldata->output_lock); return; + } - mutex_lock(&ldata->output_lock); ldata->echo_commit = head; echoed = __process_echoes(tty); mutex_unlock(&ldata->output_lock); @@ -812,7 +825,9 @@ static void flush_echoes(struct tty_struct *tty) static inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata) { - *echo_buf_addr(ldata, ldata->echo_head++) = c; + *echo_buf_addr(ldata, ldata->echo_head) = c; + smp_wmb(); /* Matches smp_rmb() in echo_buf(). */ + ldata->echo_head++; } /** @@ -1881,30 +1896,21 @@ static int n_tty_open(struct tty_struct *tty) struct n_tty_data *ldata; /* Currently a malloc failure here can panic */ - ldata = vmalloc(sizeof(*ldata)); + ldata = vzalloc(sizeof(*ldata)); if (!ldata) - goto err; + return -ENOMEM; ldata->overrun_time = jiffies; mutex_init(&ldata->atomic_read_lock); mutex_init(&ldata->output_lock); tty->disc_data = ldata; - reset_buffer_flags(tty->disc_data); - ldata->column = 0; - ldata->canon_column = 0; - ldata->num_overrun = 0; - ldata->no_room = 0; - ldata->lnext = 0; tty->closing = 0; /* indicate buffer work may resume */ clear_bit(TTY_LDISC_HALTED, &tty->flags); n_tty_set_termios(tty, NULL); tty_unthrottle(tty); - return 0; -err: - return -ENOMEM; } static inline int input_available_p(struct tty_struct *tty, int poll) -- cgit v1.2.3 From 20dcff436e9fcd2e106b0ccc48a52206bc176d70 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 6 Jun 2018 21:00:41 +0300 Subject: serial: 8250_pci: Remove stalled entries in blacklist After the commit 7d8905d06405 ("serial: 8250_pci: Enable device after we check black list") pure serial multi-port cards, such as CH355, got blacklisted and thus not being enumerated anymore. Previously, it seems, blacklisting them was on purpose to shut up pciserial_init_one() about record duplication. So, remove the entries from blacklist in order to get cards enumerated. Fixes: 7d8905d06405 ("serial: 8250_pci: Enable device after we check black list") Reported-by: Matt Turner Cc: Sergej Pupykin Cc: Alexandr Petrenko Signed-off-by: Andy Shevchenko Reviewed-and-Tested-by: Matt Turner Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 3296a05cda2d..f80a300b5d68 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -3339,9 +3339,7 @@ static const struct pci_device_id blacklist[] = { /* multi-io cards handled by parport_serial */ { PCI_DEVICE(0x4348, 0x7053), }, /* WCH CH353 2S1P */ { PCI_DEVICE(0x4348, 0x5053), }, /* WCH CH353 1S1P */ - { PCI_DEVICE(0x4348, 0x7173), }, /* WCH CH355 4S */ { PCI_DEVICE(0x1c00, 0x3250), }, /* WCH CH382 2S1P */ - { PCI_DEVICE(0x1c00, 0x3470), }, /* WCH CH384 4S */ /* Moxa Smartio MUE boards handled by 8250_moxa */ { PCI_VDEVICE(MOXA, 0x1024), }, -- cgit v1.2.3 From bc6cf3669d22371f573ab0305b3abf13887c0786 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 13 Jun 2018 17:08:59 +0200 Subject: serdev: fix memleak on module unload Make sure to free all resources associated with the ida on module exit. Fixes: cd6484e1830b ("serdev: Introduce new bus for serial attached devices") Cc: stable # 4.11 Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serdev/core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index df93b727e984..9e59f4788589 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -617,6 +617,7 @@ EXPORT_SYMBOL_GPL(__serdev_device_driver_register); static void __exit serdev_exit(void) { bus_unregister(&serdev_bus_type); + ida_destroy(&ctrl_ida); } module_exit(serdev_exit); -- cgit v1.2.3 From 21eff69aaaa0e766ca0ce445b477698dc6a9f55a Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Thu, 14 Jun 2018 12:23:09 +0200 Subject: vt: prevent leaking uninitialized data to userspace via /dev/vcs* KMSAN reported an infoleak when reading from /dev/vcs*: BUG: KMSAN: kernel-infoleak in vcs_read+0x18ba/0x1cc0 Call Trace: ... kmsan_copy_to_user+0x7a/0x160 mm/kmsan/kmsan.c:1253 copy_to_user ./include/linux/uaccess.h:184 vcs_read+0x18ba/0x1cc0 drivers/tty/vt/vc_screen.c:352 __vfs_read+0x1b2/0x9d0 fs/read_write.c:416 vfs_read+0x36c/0x6b0 fs/read_write.c:452 ... Uninit was created at: kmsan_save_stack_with_flags mm/kmsan/kmsan.c:279 kmsan_internal_poison_shadow+0xb8/0x1b0 mm/kmsan/kmsan.c:189 kmsan_kmalloc+0x94/0x100 mm/kmsan/kmsan.c:315 __kmalloc+0x13a/0x350 mm/slub.c:3818 kmalloc ./include/linux/slab.h:517 vc_allocate+0x438/0x800 drivers/tty/vt/vt.c:787 con_install+0x8c/0x640 drivers/tty/vt/vt.c:2880 tty_driver_install_tty drivers/tty/tty_io.c:1224 tty_init_dev+0x1b5/0x1020 drivers/tty/tty_io.c:1324 tty_open_by_driver drivers/tty/tty_io.c:1959 tty_open+0x17b4/0x2ed0 drivers/tty/tty_io.c:2007 chrdev_open+0xc25/0xd90 fs/char_dev.c:417 do_dentry_open+0xccc/0x1440 fs/open.c:794 vfs_open+0x1b6/0x2f0 fs/open.c:908 ... Bytes 0-79 of 240 are uninitialized Consistently allocating |vc_screenbuf| with kzalloc() fixes the problem Reported-by: syzbot+17a8efdf800000@syzkaller.appspotmail.com Signed-off-by: Alexander Potapenko Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 1eb1a376a041..15eb6c829d39 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -784,7 +784,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ if (!*vc->vc_uni_pagedir_loc) con_set_default_unimap(vc); - vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); + vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_KERNEL); if (!vc->vc_screenbuf) goto err_free; @@ -871,7 +871,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, if (new_screen_size > (4 << 20)) return -EINVAL; - newscreen = kmalloc(new_screen_size, GFP_USER); + newscreen = kzalloc(new_screen_size, GFP_USER); if (!newscreen) return -ENOMEM; -- cgit v1.2.3 From 682630f00a219a1b0696abe9c0967e660068187b Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Mon, 25 Jun 2018 20:58:17 +0300 Subject: nvme-rdma: fix possible double free of controller async event buffer If reconnect/reset failed where the controller async event buffer was freed, we might end up freeing it again as we call nvme_rdma_destroy_admin_queue again in the remove path. Given that the sequence is guaranteed to serialize by .ctrl_stop, we simply set ctrl->async_event_sqe.data to NULL and don't free it in future visits. Reported-by: Max Gurtovoy Tested-by: Max Gurtovoy Signed-off-by: Sagi Grimberg Signed-off-by: Christoph Hellwig --- drivers/nvme/host/rdma.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index 9544625c0b7d..518c5b09038c 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -732,8 +732,11 @@ static void nvme_rdma_destroy_admin_queue(struct nvme_rdma_ctrl *ctrl, blk_cleanup_queue(ctrl->ctrl.admin_q); nvme_rdma_free_tagset(&ctrl->ctrl, ctrl->ctrl.admin_tagset); } - nvme_rdma_free_qe(ctrl->device->dev, &ctrl->async_event_sqe, - sizeof(struct nvme_command), DMA_TO_DEVICE); + if (ctrl->async_event_sqe.data) { + nvme_rdma_free_qe(ctrl->device->dev, &ctrl->async_event_sqe, + sizeof(struct nvme_command), DMA_TO_DEVICE); + ctrl->async_event_sqe.data = NULL; + } nvme_rdma_free_queue(&ctrl->queues[0]); } -- cgit v1.2.3 From a11e1d432b51f63ba698d044441284a661f01144 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 28 Jun 2018 09:43:44 -0700 Subject: Revert changes to convert to ->poll_mask() and aio IOCB_CMD_POLL The poll() changes were not well thought out, and completely unexplained. They also caused a huge performance regression, because "->poll()" was no longer a trivial file operation that just called down to the underlying file operations, but instead did at least two indirect calls. Indirect calls are sadly slow now with the Spectre mitigation, but the performance problem could at least be largely mitigated by changing the "->get_poll_head()" operation to just have a per-file-descriptor pointer to the poll head instead. That gets rid of one of the new indirections. But that doesn't fix the new complexity that is completely unwarranted for the regular case. The (undocumented) reason for the poll() changes was some alleged AIO poll race fixing, but we don't make the common case slower and more complex for some uncommon special case, so this all really needs way more explanations and most likely a fundamental redesign. [ This revert is a revert of about 30 different commits, not reverted individually because that would just be unnecessarily messy - Linus ] Cc: Al Viro Cc: Christoph Hellwig Signed-off-by: Linus Torvalds --- Documentation/filesystems/Locking | 7 +- Documentation/filesystems/vfs.txt | 13 ---- crypto/af_alg.c | 13 +++- crypto/algif_aead.c | 4 +- crypto/algif_skcipher.c | 4 +- drivers/char/random.c | 29 ++++---- drivers/isdn/mISDN/socket.c | 2 +- drivers/net/ppp/pppoe.c | 2 +- fs/aio.c | 148 +------------------------------------- fs/eventfd.c | 19 ++--- fs/eventpoll.c | 15 ++-- fs/pipe.c | 22 +++--- fs/select.c | 23 ------ fs/timerfd.c | 22 +++--- include/crypto/if_alg.h | 3 +- include/linux/fs.h | 2 - include/linux/net.h | 1 - include/linux/poll.h | 12 ++-- include/linux/skbuff.h | 3 +- include/net/bluetooth/bluetooth.h | 2 +- include/net/iucv/af_iucv.h | 2 + include/net/sctp/sctp.h | 3 +- include/net/tcp.h | 3 +- include/net/tls.h | 6 +- include/net/udp.h | 2 +- include/uapi/linux/aio_abi.h | 8 ++- net/appletalk/ddp.c | 2 +- net/atm/common.c | 11 ++- net/atm/common.h | 2 +- net/atm/pvc.c | 2 +- net/atm/svc.c | 2 +- net/ax25/af_ax25.c | 2 +- net/bluetooth/af_bluetooth.c | 7 +- net/bluetooth/hci_sock.c | 2 +- net/bluetooth/l2cap_sock.c | 2 +- net/bluetooth/rfcomm/sock.c | 2 +- net/bluetooth/sco.c | 2 +- net/caif/caif_socket.c | 12 ++-- net/can/bcm.c | 2 +- net/can/raw.c | 2 +- net/core/datagram.c | 13 ++-- net/dccp/dccp.h | 3 +- net/dccp/ipv4.c | 2 +- net/dccp/ipv6.c | 2 +- net/dccp/proto.c | 13 +++- net/decnet/af_decnet.c | 6 +- net/ieee802154/socket.c | 4 +- net/ipv4/af_inet.c | 8 +-- net/ipv4/tcp.c | 23 ++++-- net/ipv4/udp.c | 10 +-- net/ipv6/af_inet6.c | 4 +- net/ipv6/raw.c | 4 +- net/iucv/af_iucv.c | 7 +- net/kcm/kcmsock.c | 10 +-- net/key/af_key.c | 2 +- net/l2tp/l2tp_ip.c | 2 +- net/l2tp/l2tp_ip6.c | 2 +- net/l2tp/l2tp_ppp.c | 2 +- net/llc/af_llc.c | 2 +- net/netlink/af_netlink.c | 2 +- net/netrom/af_netrom.c | 2 +- net/nfc/llcp_sock.c | 9 ++- net/nfc/rawsock.c | 4 +- net/packet/af_packet.c | 9 +-- net/phonet/socket.c | 9 ++- net/qrtr/qrtr.c | 2 +- net/rose/af_rose.c | 2 +- net/rxrpc/af_rxrpc.c | 10 ++- net/sctp/ipv6.c | 2 +- net/sctp/protocol.c | 2 +- net/sctp/socket.c | 4 +- net/smc/af_smc.c | 12 +++- net/socket.c | 48 ++----------- net/tipc/socket.c | 14 ++-- net/tls/tls_main.c | 2 +- net/tls/tls_sw.c | 19 ++--- net/unix/af_unix.c | 30 +++++--- net/vmw_vsock/af_vsock.c | 19 +++-- net/x25/af_x25.c | 2 +- net/xdp/xsk.c | 7 +- 80 files changed, 301 insertions(+), 450 deletions(-) (limited to 'drivers') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 2c391338c675..37bf0a9de75c 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -441,8 +441,6 @@ prototypes: int (*iterate) (struct file *, struct dir_context *); int (*iterate_shared) (struct file *, struct dir_context *); __poll_t (*poll) (struct file *, struct poll_table_struct *); - struct wait_queue_head * (*get_poll_head)(struct file *, __poll_t); - __poll_t (*poll_mask) (struct file *, __poll_t); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); @@ -473,7 +471,7 @@ prototypes: }; locking rules: - All except for ->poll_mask may block. + All may block. ->llseek() locking has moved from llseek to the individual llseek implementations. If your fs is not using generic_file_llseek, you @@ -505,9 +503,6 @@ in sys_read() and friends. the lease within the individual filesystem to record the result of the operation -->poll_mask can be called with or without the waitqueue lock for the waitqueue -returned from ->get_poll_head. - --------------------------- dquot_operations ------------------------------- prototypes: int (*write_dquot) (struct dquot *); diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 829a7b7857a4..f608180ad59d 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -857,8 +857,6 @@ struct file_operations { ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); int (*iterate) (struct file *, struct dir_context *); __poll_t (*poll) (struct file *, struct poll_table_struct *); - struct wait_queue_head * (*get_poll_head)(struct file *, __poll_t); - __poll_t (*poll_mask) (struct file *, __poll_t); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); @@ -903,17 +901,6 @@ otherwise noted. activity on this file and (optionally) go to sleep until there is activity. Called by the select(2) and poll(2) system calls - get_poll_head: Returns the struct wait_queue_head that callers can - wait on. Callers need to check the returned events using ->poll_mask - once woken. Can return NULL to indicate polling is not supported, - or any error code using the ERR_PTR convention to indicate that a - grave error occured and ->poll_mask shall not be called. - - poll_mask: return the mask of EPOLL* values describing the file descriptor - state. Called either before going to sleep on the waitqueue returned by - get_poll_head, or after it has been woken. If ->get_poll_head and - ->poll_mask are implemented ->poll does not need to be implement. - unlocked_ioctl: called by the ioctl(2) system call. compat_ioctl: called by the ioctl(2) system call when 32 bit system calls diff --git a/crypto/af_alg.c b/crypto/af_alg.c index 49fa8582138b..314c52c967e5 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -1060,12 +1060,19 @@ void af_alg_async_cb(struct crypto_async_request *_req, int err) } EXPORT_SYMBOL_GPL(af_alg_async_cb); -__poll_t af_alg_poll_mask(struct socket *sock, __poll_t events) +/** + * af_alg_poll - poll system call handler + */ +__poll_t af_alg_poll(struct file *file, struct socket *sock, + poll_table *wait) { struct sock *sk = sock->sk; struct alg_sock *ask = alg_sk(sk); struct af_alg_ctx *ctx = ask->private; - __poll_t mask = 0; + __poll_t mask; + + sock_poll_wait(file, sk_sleep(sk), wait); + mask = 0; if (!ctx->more || ctx->used) mask |= EPOLLIN | EPOLLRDNORM; @@ -1075,7 +1082,7 @@ __poll_t af_alg_poll_mask(struct socket *sock, __poll_t events) return mask; } -EXPORT_SYMBOL_GPL(af_alg_poll_mask); +EXPORT_SYMBOL_GPL(af_alg_poll); /** * af_alg_alloc_areq - allocate struct af_alg_async_req diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c index 825524f27438..c40a8c7ee8ae 100644 --- a/crypto/algif_aead.c +++ b/crypto/algif_aead.c @@ -375,7 +375,7 @@ static struct proto_ops algif_aead_ops = { .sendmsg = aead_sendmsg, .sendpage = af_alg_sendpage, .recvmsg = aead_recvmsg, - .poll_mask = af_alg_poll_mask, + .poll = af_alg_poll, }; static int aead_check_key(struct socket *sock) @@ -471,7 +471,7 @@ static struct proto_ops algif_aead_ops_nokey = { .sendmsg = aead_sendmsg_nokey, .sendpage = aead_sendpage_nokey, .recvmsg = aead_recvmsg_nokey, - .poll_mask = af_alg_poll_mask, + .poll = af_alg_poll, }; static void *aead_bind(const char *name, u32 type, u32 mask) diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index 4c04eb9888ad..cfdaab2b7d76 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -206,7 +206,7 @@ static struct proto_ops algif_skcipher_ops = { .sendmsg = skcipher_sendmsg, .sendpage = af_alg_sendpage, .recvmsg = skcipher_recvmsg, - .poll_mask = af_alg_poll_mask, + .poll = af_alg_poll, }; static int skcipher_check_key(struct socket *sock) @@ -302,7 +302,7 @@ static struct proto_ops algif_skcipher_ops_nokey = { .sendmsg = skcipher_sendmsg_nokey, .sendpage = skcipher_sendpage_nokey, .recvmsg = skcipher_recvmsg_nokey, - .poll_mask = af_alg_poll_mask, + .poll = af_alg_poll, }; static void *skcipher_bind(const char *name, u32 type, u32 mask) diff --git a/drivers/char/random.c b/drivers/char/random.c index a8fb0020ba5c..cd888d4ee605 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -402,7 +402,8 @@ static struct poolinfo { /* * Static global variables */ -static DECLARE_WAIT_QUEUE_HEAD(random_wait); +static DECLARE_WAIT_QUEUE_HEAD(random_read_wait); +static DECLARE_WAIT_QUEUE_HEAD(random_write_wait); static struct fasync_struct *fasync; static DEFINE_SPINLOCK(random_ready_list_lock); @@ -721,8 +722,8 @@ retry: /* should we wake readers? */ if (entropy_bits >= random_read_wakeup_bits && - wq_has_sleeper(&random_wait)) { - wake_up_interruptible_poll(&random_wait, POLLIN); + wq_has_sleeper(&random_read_wait)) { + wake_up_interruptible(&random_read_wait); kill_fasync(&fasync, SIGIO, POLL_IN); } /* If the input pool is getting full, send some @@ -1396,7 +1397,7 @@ retry: trace_debit_entropy(r->name, 8 * ibytes); if (ibytes && (r->entropy_count >> ENTROPY_SHIFT) < random_write_wakeup_bits) { - wake_up_interruptible_poll(&random_wait, POLLOUT); + wake_up_interruptible(&random_write_wait); kill_fasync(&fasync, SIGIO, POLL_OUT); } @@ -1838,7 +1839,7 @@ _random_read(int nonblock, char __user *buf, size_t nbytes) if (nonblock) return -EAGAIN; - wait_event_interruptible(random_wait, + wait_event_interruptible(random_read_wait, ENTROPY_BITS(&input_pool) >= random_read_wakeup_bits); if (signal_pending(current)) @@ -1875,17 +1876,14 @@ urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) return ret; } -static struct wait_queue_head * -random_get_poll_head(struct file *file, __poll_t events) -{ - return &random_wait; -} - static __poll_t -random_poll_mask(struct file *file, __poll_t events) +random_poll(struct file *file, poll_table * wait) { - __poll_t mask = 0; + __poll_t mask; + poll_wait(file, &random_read_wait, wait); + poll_wait(file, &random_write_wait, wait); + mask = 0; if (ENTROPY_BITS(&input_pool) >= random_read_wakeup_bits) mask |= EPOLLIN | EPOLLRDNORM; if (ENTROPY_BITS(&input_pool) < random_write_wakeup_bits) @@ -1992,8 +1990,7 @@ static int random_fasync(int fd, struct file *filp, int on) const struct file_operations random_fops = { .read = random_read, .write = random_write, - .get_poll_head = random_get_poll_head, - .poll_mask = random_poll_mask, + .poll = random_poll, .unlocked_ioctl = random_ioctl, .fasync = random_fasync, .llseek = noop_llseek, @@ -2326,7 +2323,7 @@ void add_hwgenerator_randomness(const char *buffer, size_t count, * We'll be woken up again once below random_write_wakeup_thresh, * or when the calling thread is about to terminate. */ - wait_event_interruptible(random_wait, kthread_should_stop() || + wait_event_interruptible(random_write_wait, kthread_should_stop() || ENTROPY_BITS(&input_pool) <= random_write_wakeup_bits); mix_pool_bytes(poolp, buffer, count); credit_entropy_bits(poolp, entropy); diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c index 98f90aadd141..18c0a1281914 100644 --- a/drivers/isdn/mISDN/socket.c +++ b/drivers/isdn/mISDN/socket.c @@ -588,7 +588,7 @@ static const struct proto_ops data_sock_ops = { .getname = data_sock_getname, .sendmsg = mISDN_sock_sendmsg, .recvmsg = mISDN_sock_recvmsg, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = data_sock_setsockopt, diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index de51e8f70f44..ce61231e96ea 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -1107,7 +1107,7 @@ static const struct proto_ops pppoe_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = pppoe_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = sock_no_setsockopt, diff --git a/fs/aio.c b/fs/aio.c index e1d20124ec0e..210df9da1283 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -5,7 +5,6 @@ * Implements an efficient asynchronous io interface. * * Copyright 2000, 2001, 2002 Red Hat, Inc. All Rights Reserved. - * Copyright 2018 Christoph Hellwig. * * See ../COPYING for licensing terms. */ @@ -165,22 +164,10 @@ struct fsync_iocb { bool datasync; }; -struct poll_iocb { - struct file *file; - __poll_t events; - struct wait_queue_head *head; - - union { - struct wait_queue_entry wait; - struct work_struct work; - }; -}; - struct aio_kiocb { union { struct kiocb rw; struct fsync_iocb fsync; - struct poll_iocb poll; }; struct kioctx *ki_ctx; @@ -1590,6 +1577,7 @@ static int aio_fsync(struct fsync_iocb *req, struct iocb *iocb, bool datasync) if (unlikely(iocb->aio_buf || iocb->aio_offset || iocb->aio_nbytes || iocb->aio_rw_flags)) return -EINVAL; + req->file = fget(iocb->aio_fildes); if (unlikely(!req->file)) return -EBADF; @@ -1604,137 +1592,6 @@ static int aio_fsync(struct fsync_iocb *req, struct iocb *iocb, bool datasync) return 0; } -/* need to use list_del_init so we can check if item was present */ -static inline bool __aio_poll_remove(struct poll_iocb *req) -{ - if (list_empty(&req->wait.entry)) - return false; - list_del_init(&req->wait.entry); - return true; -} - -static inline void __aio_poll_complete(struct aio_kiocb *iocb, __poll_t mask) -{ - fput(iocb->poll.file); - aio_complete(iocb, mangle_poll(mask), 0); -} - -static void aio_poll_work(struct work_struct *work) -{ - struct aio_kiocb *iocb = container_of(work, struct aio_kiocb, poll.work); - - if (!list_empty_careful(&iocb->ki_list)) - aio_remove_iocb(iocb); - __aio_poll_complete(iocb, iocb->poll.events); -} - -static int aio_poll_cancel(struct kiocb *iocb) -{ - struct aio_kiocb *aiocb = container_of(iocb, struct aio_kiocb, rw); - struct poll_iocb *req = &aiocb->poll; - struct wait_queue_head *head = req->head; - bool found = false; - - spin_lock(&head->lock); - found = __aio_poll_remove(req); - spin_unlock(&head->lock); - - if (found) { - req->events = 0; - INIT_WORK(&req->work, aio_poll_work); - schedule_work(&req->work); - } - return 0; -} - -static int aio_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync, - void *key) -{ - struct poll_iocb *req = container_of(wait, struct poll_iocb, wait); - struct aio_kiocb *iocb = container_of(req, struct aio_kiocb, poll); - struct file *file = req->file; - __poll_t mask = key_to_poll(key); - - assert_spin_locked(&req->head->lock); - - /* for instances that support it check for an event match first: */ - if (mask && !(mask & req->events)) - return 0; - - mask = file->f_op->poll_mask(file, req->events) & req->events; - if (!mask) - return 0; - - __aio_poll_remove(req); - - /* - * Try completing without a context switch if we can acquire ctx_lock - * without spinning. Otherwise we need to defer to a workqueue to - * avoid a deadlock due to the lock order. - */ - if (spin_trylock(&iocb->ki_ctx->ctx_lock)) { - list_del_init(&iocb->ki_list); - spin_unlock(&iocb->ki_ctx->ctx_lock); - - __aio_poll_complete(iocb, mask); - } else { - req->events = mask; - INIT_WORK(&req->work, aio_poll_work); - schedule_work(&req->work); - } - - return 1; -} - -static ssize_t aio_poll(struct aio_kiocb *aiocb, struct iocb *iocb) -{ - struct kioctx *ctx = aiocb->ki_ctx; - struct poll_iocb *req = &aiocb->poll; - __poll_t mask; - - /* reject any unknown events outside the normal event mask. */ - if ((u16)iocb->aio_buf != iocb->aio_buf) - return -EINVAL; - /* reject fields that are not defined for poll */ - if (iocb->aio_offset || iocb->aio_nbytes || iocb->aio_rw_flags) - return -EINVAL; - - req->events = demangle_poll(iocb->aio_buf) | EPOLLERR | EPOLLHUP; - req->file = fget(iocb->aio_fildes); - if (unlikely(!req->file)) - return -EBADF; - if (!file_has_poll_mask(req->file)) - goto out_fail; - - req->head = req->file->f_op->get_poll_head(req->file, req->events); - if (!req->head) - goto out_fail; - if (IS_ERR(req->head)) { - mask = EPOLLERR; - goto done; - } - - init_waitqueue_func_entry(&req->wait, aio_poll_wake); - aiocb->ki_cancel = aio_poll_cancel; - - spin_lock_irq(&ctx->ctx_lock); - spin_lock(&req->head->lock); - mask = req->file->f_op->poll_mask(req->file, req->events) & req->events; - if (!mask) { - __add_wait_queue(req->head, &req->wait); - list_add_tail(&aiocb->ki_list, &ctx->active_reqs); - } - spin_unlock(&req->head->lock); - spin_unlock_irq(&ctx->ctx_lock); -done: - if (mask) - __aio_poll_complete(aiocb, mask); - return 0; -out_fail: - fput(req->file); - return -EINVAL; /* same as no support for IOCB_CMD_POLL */ -} - static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, bool compat) { @@ -1808,9 +1665,6 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, case IOCB_CMD_FDSYNC: ret = aio_fsync(&req->fsync, &iocb, true); break; - case IOCB_CMD_POLL: - ret = aio_poll(req, &iocb); - break; default: pr_debug("invalid aio operation %d\n", iocb.aio_lio_opcode); ret = -EINVAL; diff --git a/fs/eventfd.c b/fs/eventfd.c index ceb1031f1cac..08d3bd602f73 100644 --- a/fs/eventfd.c +++ b/fs/eventfd.c @@ -101,20 +101,14 @@ static int eventfd_release(struct inode *inode, struct file *file) return 0; } -static struct wait_queue_head * -eventfd_get_poll_head(struct file *file, __poll_t events) -{ - struct eventfd_ctx *ctx = file->private_data; - - return &ctx->wqh; -} - -static __poll_t eventfd_poll_mask(struct file *file, __poll_t eventmask) +static __poll_t eventfd_poll(struct file *file, poll_table *wait) { struct eventfd_ctx *ctx = file->private_data; __poll_t events = 0; u64 count; + poll_wait(file, &ctx->wqh, wait); + /* * All writes to ctx->count occur within ctx->wqh.lock. This read * can be done outside ctx->wqh.lock because we know that poll_wait @@ -156,11 +150,11 @@ static __poll_t eventfd_poll_mask(struct file *file, __poll_t eventmask) count = READ_ONCE(ctx->count); if (count > 0) - events |= (EPOLLIN & eventmask); + events |= EPOLLIN; if (count == ULLONG_MAX) events |= EPOLLERR; if (ULLONG_MAX - 1 > count) - events |= (EPOLLOUT & eventmask); + events |= EPOLLOUT; return events; } @@ -311,8 +305,7 @@ static const struct file_operations eventfd_fops = { .show_fdinfo = eventfd_show_fdinfo, #endif .release = eventfd_release, - .get_poll_head = eventfd_get_poll_head, - .poll_mask = eventfd_poll_mask, + .poll = eventfd_poll, .read = eventfd_read, .write = eventfd_write, .llseek = noop_llseek, diff --git a/fs/eventpoll.c b/fs/eventpoll.c index ea4436f409fb..67db22fe99c5 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -922,18 +922,14 @@ static __poll_t ep_read_events_proc(struct eventpoll *ep, struct list_head *head return 0; } -static struct wait_queue_head *ep_eventpoll_get_poll_head(struct file *file, - __poll_t eventmask) -{ - struct eventpoll *ep = file->private_data; - return &ep->poll_wait; -} - -static __poll_t ep_eventpoll_poll_mask(struct file *file, __poll_t eventmask) +static __poll_t ep_eventpoll_poll(struct file *file, poll_table *wait) { struct eventpoll *ep = file->private_data; int depth = 0; + /* Insert inside our poll wait queue */ + poll_wait(file, &ep->poll_wait, wait); + /* * Proceed to find out if wanted events are really available inside * the ready list. @@ -972,8 +968,7 @@ static const struct file_operations eventpoll_fops = { .show_fdinfo = ep_show_fdinfo, #endif .release = ep_eventpoll_release, - .get_poll_head = ep_eventpoll_get_poll_head, - .poll_mask = ep_eventpoll_poll_mask, + .poll = ep_eventpoll_poll, .llseek = noop_llseek, }; diff --git a/fs/pipe.c b/fs/pipe.c index bb0840e234f3..39d6f431da83 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -509,22 +509,19 @@ static long pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } } -static struct wait_queue_head * -pipe_get_poll_head(struct file *filp, __poll_t events) -{ - struct pipe_inode_info *pipe = filp->private_data; - - return &pipe->wait; -} - /* No kernel lock held - fine */ -static __poll_t pipe_poll_mask(struct file *filp, __poll_t events) +static __poll_t +pipe_poll(struct file *filp, poll_table *wait) { + __poll_t mask; struct pipe_inode_info *pipe = filp->private_data; - int nrbufs = pipe->nrbufs; - __poll_t mask = 0; + int nrbufs; + + poll_wait(filp, &pipe->wait, wait); /* Reading only -- no need for acquiring the semaphore. */ + nrbufs = pipe->nrbufs; + mask = 0; if (filp->f_mode & FMODE_READ) { mask = (nrbufs > 0) ? EPOLLIN | EPOLLRDNORM : 0; if (!pipe->writers && filp->f_version != pipe->w_counter) @@ -1023,8 +1020,7 @@ const struct file_operations pipefifo_fops = { .llseek = no_llseek, .read_iter = pipe_read, .write_iter = pipe_write, - .get_poll_head = pipe_get_poll_head, - .poll_mask = pipe_poll_mask, + .poll = pipe_poll, .unlocked_ioctl = pipe_ioctl, .release = pipe_release, .fasync = pipe_fasync, diff --git a/fs/select.c b/fs/select.c index 317891ff8165..4a6b6e4b21cb 100644 --- a/fs/select.c +++ b/fs/select.c @@ -34,29 +34,6 @@ #include -__poll_t vfs_poll(struct file *file, struct poll_table_struct *pt) -{ - if (file->f_op->poll) { - return file->f_op->poll(file, pt); - } else if (file_has_poll_mask(file)) { - unsigned int events = poll_requested_events(pt); - struct wait_queue_head *head; - - if (pt && pt->_qproc) { - head = file->f_op->get_poll_head(file, events); - if (!head) - return DEFAULT_POLLMASK; - if (IS_ERR(head)) - return EPOLLERR; - pt->_qproc(file, head, pt); - } - - return file->f_op->poll_mask(file, events); - } else { - return DEFAULT_POLLMASK; - } -} -EXPORT_SYMBOL_GPL(vfs_poll); /* * Estimate expected accuracy in ns from a timeval. diff --git a/fs/timerfd.c b/fs/timerfd.c index d84a2bee4f82..cdad49da3ff7 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -226,20 +226,21 @@ static int timerfd_release(struct inode *inode, struct file *file) kfree_rcu(ctx, rcu); return 0; } - -static struct wait_queue_head *timerfd_get_poll_head(struct file *file, - __poll_t eventmask) + +static __poll_t timerfd_poll(struct file *file, poll_table *wait) { struct timerfd_ctx *ctx = file->private_data; + __poll_t events = 0; + unsigned long flags; - return &ctx->wqh; -} + poll_wait(file, &ctx->wqh, wait); -static __poll_t timerfd_poll_mask(struct file *file, __poll_t eventmask) -{ - struct timerfd_ctx *ctx = file->private_data; + spin_lock_irqsave(&ctx->wqh.lock, flags); + if (ctx->ticks) + events |= EPOLLIN; + spin_unlock_irqrestore(&ctx->wqh.lock, flags); - return ctx->ticks ? EPOLLIN : 0; + return events; } static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count, @@ -363,8 +364,7 @@ static long timerfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg static const struct file_operations timerfd_fops = { .release = timerfd_release, - .get_poll_head = timerfd_get_poll_head, - .poll_mask = timerfd_poll_mask, + .poll = timerfd_poll, .read = timerfd_read, .llseek = noop_llseek, .show_fdinfo = timerfd_show, diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h index cc414db9da0a..482461d8931d 100644 --- a/include/crypto/if_alg.h +++ b/include/crypto/if_alg.h @@ -245,7 +245,8 @@ ssize_t af_alg_sendpage(struct socket *sock, struct page *page, int offset, size_t size, int flags); void af_alg_free_resources(struct af_alg_async_req *areq); void af_alg_async_cb(struct crypto_async_request *_req, int err); -__poll_t af_alg_poll_mask(struct socket *sock, __poll_t events); +__poll_t af_alg_poll(struct file *file, struct socket *sock, + poll_table *wait); struct af_alg_async_req *af_alg_alloc_areq(struct sock *sk, unsigned int areqlen); int af_alg_get_rsgl(struct sock *sk, struct msghdr *msg, int flags, diff --git a/include/linux/fs.h b/include/linux/fs.h index 5c91108846db..d78d146a98da 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1720,8 +1720,6 @@ struct file_operations { int (*iterate) (struct file *, struct dir_context *); int (*iterate_shared) (struct file *, struct dir_context *); __poll_t (*poll) (struct file *, struct poll_table_struct *); - struct wait_queue_head * (*get_poll_head)(struct file *, __poll_t); - __poll_t (*poll_mask) (struct file *, __poll_t); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); diff --git a/include/linux/net.h b/include/linux/net.h index 08b6eb964dd6..6554d3ba4396 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -147,7 +147,6 @@ struct proto_ops { int (*getname) (struct socket *sock, struct sockaddr *addr, int peer); - __poll_t (*poll_mask) (struct socket *sock, __poll_t events); __poll_t (*poll) (struct file *file, struct socket *sock, struct poll_table_struct *wait); int (*ioctl) (struct socket *sock, unsigned int cmd, diff --git a/include/linux/poll.h b/include/linux/poll.h index fdf86b4cbc71..7e0fdcf905d2 100644 --- a/include/linux/poll.h +++ b/include/linux/poll.h @@ -74,18 +74,18 @@ static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc) pt->_key = ~(__poll_t)0; /* all events enabled */ } -static inline bool file_has_poll_mask(struct file *file) +static inline bool file_can_poll(struct file *file) { - return file->f_op->get_poll_head && file->f_op->poll_mask; + return file->f_op->poll; } -static inline bool file_can_poll(struct file *file) +static inline __poll_t vfs_poll(struct file *file, struct poll_table_struct *pt) { - return file->f_op->poll || file_has_poll_mask(file); + if (unlikely(!file->f_op->poll)) + return DEFAULT_POLLMASK; + return file->f_op->poll(file, pt); } -__poll_t vfs_poll(struct file *file, struct poll_table_struct *pt); - struct poll_table_entry { struct file *filp; __poll_t key; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index c86885954994..164cdedf6012 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -3252,7 +3252,8 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags, int *peeked, int *off, int *err); struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, int *err); -__poll_t datagram_poll_mask(struct socket *sock, __poll_t events); +__poll_t datagram_poll(struct file *file, struct socket *sock, + struct poll_table_struct *wait); int skb_copy_datagram_iter(const struct sk_buff *from, int offset, struct iov_iter *to, int size); static inline int skb_copy_datagram_msg(const struct sk_buff *from, int offset, diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 53ce8176c313..ec9d6bc65855 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -271,7 +271,7 @@ int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, int flags); int bt_sock_stream_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, int flags); -__poll_t bt_sock_poll_mask(struct socket *sock, __poll_t events); +__poll_t bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait); int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo); int bt_sock_wait_ready(struct sock *sk, unsigned long flags); diff --git a/include/net/iucv/af_iucv.h b/include/net/iucv/af_iucv.h index b0eaeb02d46d..f4c21b5a1242 100644 --- a/include/net/iucv/af_iucv.h +++ b/include/net/iucv/af_iucv.h @@ -153,6 +153,8 @@ struct iucv_sock_list { atomic_t autobind_name; }; +__poll_t iucv_sock_poll(struct file *file, struct socket *sock, + poll_table *wait); void iucv_sock_link(struct iucv_sock_list *l, struct sock *s); void iucv_sock_unlink(struct iucv_sock_list *l, struct sock *s); void iucv_accept_enqueue(struct sock *parent, struct sock *sk); diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 30b3e2fe240a..8c2caa370e0f 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -109,7 +109,8 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb); int sctp_inet_listen(struct socket *sock, int backlog); void sctp_write_space(struct sock *sk); void sctp_data_ready(struct sock *sk); -__poll_t sctp_poll_mask(struct socket *sock, __poll_t events); +__poll_t sctp_poll(struct file *file, struct socket *sock, + poll_table *wait); void sctp_sock_rfree(struct sk_buff *skb); void sctp_copy_sock(struct sock *newsk, struct sock *sk, struct sctp_association *asoc); diff --git a/include/net/tcp.h b/include/net/tcp.h index 0448e7c5d2b4..800582b5dd54 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -388,7 +388,8 @@ bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst); void tcp_close(struct sock *sk, long timeout); void tcp_init_sock(struct sock *sk); void tcp_init_transfer(struct sock *sk, int bpf_op); -__poll_t tcp_poll_mask(struct socket *sock, __poll_t events); +__poll_t tcp_poll(struct file *file, struct socket *sock, + struct poll_table_struct *wait); int tcp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen); int tcp_setsockopt(struct sock *sk, int level, int optname, diff --git a/include/net/tls.h b/include/net/tls.h index 7f84ea3e217c..70c273777fe9 100644 --- a/include/net/tls.h +++ b/include/net/tls.h @@ -109,7 +109,8 @@ struct tls_sw_context_rx { struct strparser strp; void (*saved_data_ready)(struct sock *sk); - __poll_t (*sk_poll_mask)(struct socket *sock, __poll_t events); + unsigned int (*sk_poll)(struct file *file, struct socket *sock, + struct poll_table_struct *wait); struct sk_buff *recv_pkt; u8 control; bool decrypted; @@ -224,7 +225,8 @@ void tls_sw_free_resources_tx(struct sock *sk); void tls_sw_free_resources_rx(struct sock *sk); int tls_sw_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, int flags, int *addr_len); -__poll_t tls_sw_poll_mask(struct socket *sock, __poll_t events); +unsigned int tls_sw_poll(struct file *file, struct socket *sock, + struct poll_table_struct *wait); ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned int flags); diff --git a/include/net/udp.h b/include/net/udp.h index b1ea8b0f5e6a..81afdacd4fff 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -285,7 +285,7 @@ int udp_init_sock(struct sock *sk); int udp_pre_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); int __udp_disconnect(struct sock *sk, int flags); int udp_disconnect(struct sock *sk, int flags); -__poll_t udp_poll_mask(struct socket *sock, __poll_t events); +__poll_t udp_poll(struct file *file, struct socket *sock, poll_table *wait); struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, netdev_features_t features, bool is_ipv6); diff --git a/include/uapi/linux/aio_abi.h b/include/uapi/linux/aio_abi.h index d00221345c19..d4e768d55d14 100644 --- a/include/uapi/linux/aio_abi.h +++ b/include/uapi/linux/aio_abi.h @@ -39,8 +39,10 @@ enum { IOCB_CMD_PWRITE = 1, IOCB_CMD_FSYNC = 2, IOCB_CMD_FDSYNC = 3, - /* 4 was the experimental IOCB_CMD_PREADX */ - IOCB_CMD_POLL = 5, + /* These two are experimental. + * IOCB_CMD_PREADX = 4, + * IOCB_CMD_POLL = 5, + */ IOCB_CMD_NOOP = 6, IOCB_CMD_PREADV = 7, IOCB_CMD_PWRITEV = 8, @@ -109,7 +111,7 @@ struct iocb { #undef IFLITTLE struct __aio_sigset { - const sigset_t __user *sigmask; + sigset_t __user *sigmask; size_t sigsetsize; }; diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 55fdba05d7d9..9b6bc5abe946 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -1869,7 +1869,7 @@ static const struct proto_ops atalk_dgram_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = atalk_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = atalk_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = atalk_compat_ioctl, diff --git a/net/atm/common.c b/net/atm/common.c index ff5748b2190f..a7a68e509628 100644 --- a/net/atm/common.c +++ b/net/atm/common.c @@ -647,11 +647,16 @@ out: return error; } -__poll_t vcc_poll_mask(struct socket *sock, __poll_t events) +__poll_t vcc_poll(struct file *file, struct socket *sock, poll_table *wait) { struct sock *sk = sock->sk; - struct atm_vcc *vcc = ATM_SD(sock); - __poll_t mask = 0; + struct atm_vcc *vcc; + __poll_t mask; + + sock_poll_wait(file, sk_sleep(sk), wait); + mask = 0; + + vcc = ATM_SD(sock); /* exceptional events */ if (sk->sk_err) diff --git a/net/atm/common.h b/net/atm/common.h index 526796ad230f..5850649068bb 100644 --- a/net/atm/common.h +++ b/net/atm/common.h @@ -17,7 +17,7 @@ int vcc_connect(struct socket *sock, int itf, short vpi, int vci); int vcc_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, int flags); int vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len); -__poll_t vcc_poll_mask(struct socket *sock, __poll_t events); +__poll_t vcc_poll(struct file *file, struct socket *sock, poll_table *wait); int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); int vcc_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); int vcc_setsockopt(struct socket *sock, int level, int optname, diff --git a/net/atm/pvc.c b/net/atm/pvc.c index 9f75092fe778..2cb10af16afc 100644 --- a/net/atm/pvc.c +++ b/net/atm/pvc.c @@ -113,7 +113,7 @@ static const struct proto_ops pvc_proto_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = pvc_getname, - .poll_mask = vcc_poll_mask, + .poll = vcc_poll, .ioctl = vcc_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = vcc_compat_ioctl, diff --git a/net/atm/svc.c b/net/atm/svc.c index 53f4ad7087b1..2f91b766ac42 100644 --- a/net/atm/svc.c +++ b/net/atm/svc.c @@ -636,7 +636,7 @@ static const struct proto_ops svc_proto_ops = { .socketpair = sock_no_socketpair, .accept = svc_accept, .getname = svc_getname, - .poll_mask = vcc_poll_mask, + .poll = vcc_poll, .ioctl = svc_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = svc_compat_ioctl, diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index d1d2442ce573..c603d33d5410 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1941,7 +1941,7 @@ static const struct proto_ops ax25_proto_ops = { .socketpair = sock_no_socketpair, .accept = ax25_accept, .getname = ax25_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = ax25_ioctl, .listen = ax25_listen, .shutdown = ax25_shutdown, diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 510ab4f55df5..3264e1873219 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -437,13 +437,16 @@ static inline __poll_t bt_accept_poll(struct sock *parent) return 0; } -__poll_t bt_sock_poll_mask(struct socket *sock, __poll_t events) +__poll_t bt_sock_poll(struct file *file, struct socket *sock, + poll_table *wait) { struct sock *sk = sock->sk; __poll_t mask = 0; BT_DBG("sock %p, sk %p", sock, sk); + poll_wait(file, sk_sleep(sk), wait); + if (sk->sk_state == BT_LISTEN) return bt_accept_poll(sk); @@ -475,7 +478,7 @@ __poll_t bt_sock_poll_mask(struct socket *sock, __poll_t events) return mask; } -EXPORT_SYMBOL(bt_sock_poll_mask); +EXPORT_SYMBOL(bt_sock_poll); int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index d6c099861538..1506e1632394 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -1975,7 +1975,7 @@ static const struct proto_ops hci_sock_ops = { .sendmsg = hci_sock_sendmsg, .recvmsg = hci_sock_recvmsg, .ioctl = hci_sock_ioctl, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = hci_sock_setsockopt, diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 742a190034e6..686bdc6b35b0 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1653,7 +1653,7 @@ static const struct proto_ops l2cap_sock_ops = { .getname = l2cap_sock_getname, .sendmsg = l2cap_sock_sendmsg, .recvmsg = l2cap_sock_recvmsg, - .poll_mask = bt_sock_poll_mask, + .poll = bt_sock_poll, .ioctl = bt_sock_ioctl, .mmap = sock_no_mmap, .socketpair = sock_no_socketpair, diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 1cf57622473a..d606e9212291 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -1049,7 +1049,7 @@ static const struct proto_ops rfcomm_sock_ops = { .setsockopt = rfcomm_sock_setsockopt, .getsockopt = rfcomm_sock_getsockopt, .ioctl = rfcomm_sock_ioctl, - .poll_mask = bt_sock_poll_mask, + .poll = bt_sock_poll, .socketpair = sock_no_socketpair, .mmap = sock_no_mmap }; diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index d60dbc61d170..413b8ee49fec 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -1197,7 +1197,7 @@ static const struct proto_ops sco_sock_ops = { .getname = sco_sock_getname, .sendmsg = sco_sock_sendmsg, .recvmsg = sco_sock_recvmsg, - .poll_mask = bt_sock_poll_mask, + .poll = bt_sock_poll, .ioctl = bt_sock_ioctl, .mmap = sock_no_mmap, .socketpair = sock_no_socketpair, diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c index c7991867d622..a6fb1b3bcad9 100644 --- a/net/caif/caif_socket.c +++ b/net/caif/caif_socket.c @@ -934,11 +934,15 @@ static int caif_release(struct socket *sock) } /* Copied from af_unix.c:unix_poll(), added CAIF tx_flow handling */ -static __poll_t caif_poll_mask(struct socket *sock, __poll_t events) +static __poll_t caif_poll(struct file *file, + struct socket *sock, poll_table *wait) { struct sock *sk = sock->sk; + __poll_t mask; struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - __poll_t mask = 0; + + sock_poll_wait(file, sk_sleep(sk), wait); + mask = 0; /* exceptional events? */ if (sk->sk_err) @@ -972,7 +976,7 @@ static const struct proto_ops caif_seqpacket_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = sock_no_getname, - .poll_mask = caif_poll_mask, + .poll = caif_poll, .ioctl = sock_no_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, @@ -993,7 +997,7 @@ static const struct proto_ops caif_stream_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = sock_no_getname, - .poll_mask = caif_poll_mask, + .poll = caif_poll, .ioctl = sock_no_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, diff --git a/net/can/bcm.c b/net/can/bcm.c index 9393f25df08d..0af8f0db892a 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1660,7 +1660,7 @@ static const struct proto_ops bcm_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = sock_no_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = can_ioctl, /* use can_ioctl() from af_can.c */ .listen = sock_no_listen, .shutdown = sock_no_shutdown, diff --git a/net/can/raw.c b/net/can/raw.c index fd7e2f49ea6a..1051eee82581 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -843,7 +843,7 @@ static const struct proto_ops raw_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = raw_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = can_ioctl, /* use can_ioctl() from af_can.c */ .listen = sock_no_listen, .shutdown = sock_no_shutdown, diff --git a/net/core/datagram.c b/net/core/datagram.c index f19bf3dc2bd6..9938952c5c78 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -819,8 +819,9 @@ EXPORT_SYMBOL(skb_copy_and_csum_datagram_msg); /** * datagram_poll - generic datagram poll + * @file: file struct * @sock: socket - * @events to wait for + * @wait: poll table * * Datagram poll: Again totally generic. This also handles * sequenced packet sockets providing the socket receive queue @@ -830,10 +831,14 @@ EXPORT_SYMBOL(skb_copy_and_csum_datagram_msg); * and you use a different write policy from sock_writeable() * then please supply your own write_space callback. */ -__poll_t datagram_poll_mask(struct socket *sock, __poll_t events) +__poll_t datagram_poll(struct file *file, struct socket *sock, + poll_table *wait) { struct sock *sk = sock->sk; - __poll_t mask = 0; + __poll_t mask; + + sock_poll_wait(file, sk_sleep(sk), wait); + mask = 0; /* exceptional events? */ if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue)) @@ -866,4 +871,4 @@ __poll_t datagram_poll_mask(struct socket *sock, __poll_t events) return mask; } -EXPORT_SYMBOL(datagram_poll_mask); +EXPORT_SYMBOL(datagram_poll); diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 0ea2ee56ac1b..f91e3816806b 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -316,7 +316,8 @@ int dccp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, int flags, int *addr_len); void dccp_shutdown(struct sock *sk, int how); int inet_dccp_listen(struct socket *sock, int backlog); -__poll_t dccp_poll_mask(struct socket *sock, __poll_t events); +__poll_t dccp_poll(struct file *file, struct socket *sock, + poll_table *wait); int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); void dccp_req_err(struct sock *sk, u64 seq); diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index a9e478cd3787..b08feb219b44 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -984,7 +984,7 @@ static const struct proto_ops inet_dccp_ops = { .accept = inet_accept, .getname = inet_getname, /* FIXME: work on tcp_poll to rename it to inet_csk_poll */ - .poll_mask = dccp_poll_mask, + .poll = dccp_poll, .ioctl = inet_ioctl, /* FIXME: work on inet_listen to rename it to sock_common_listen */ .listen = inet_dccp_listen, diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 17fc4e0166ba..6344f1b18a6a 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -1070,7 +1070,7 @@ static const struct proto_ops inet6_dccp_ops = { .socketpair = sock_no_socketpair, .accept = inet_accept, .getname = inet6_getname, - .poll_mask = dccp_poll_mask, + .poll = dccp_poll, .ioctl = inet6_ioctl, .listen = inet_dccp_listen, .shutdown = inet_shutdown, diff --git a/net/dccp/proto.c b/net/dccp/proto.c index ca21c1c76da0..0d56e36a6db7 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -312,11 +312,20 @@ int dccp_disconnect(struct sock *sk, int flags) EXPORT_SYMBOL_GPL(dccp_disconnect); -__poll_t dccp_poll_mask(struct socket *sock, __poll_t events) +/* + * Wait for a DCCP event. + * + * Note that we don't need to lock the socket, as the upper poll layers + * take care of normal races (between the test and the event) and we don't + * go look at any of the socket buffers directly. + */ +__poll_t dccp_poll(struct file *file, struct socket *sock, + poll_table *wait) { __poll_t mask; struct sock *sk = sock->sk; + sock_poll_wait(file, sk_sleep(sk), wait); if (sk->sk_state == DCCP_LISTEN) return inet_csk_listen_poll(sk); @@ -358,7 +367,7 @@ __poll_t dccp_poll_mask(struct socket *sock, __poll_t events) return mask; } -EXPORT_SYMBOL_GPL(dccp_poll_mask); +EXPORT_SYMBOL_GPL(dccp_poll); int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg) { diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index 9a686d890bfa..7d6ff983ba2c 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -1207,11 +1207,11 @@ static int dn_getname(struct socket *sock, struct sockaddr *uaddr,int peer) } -static __poll_t dn_poll_mask(struct socket *sock, __poll_t events) +static __poll_t dn_poll(struct file *file, struct socket *sock, poll_table *wait) { struct sock *sk = sock->sk; struct dn_scp *scp = DN_SK(sk); - __poll_t mask = datagram_poll_mask(sock, events); + __poll_t mask = datagram_poll(file, sock, wait); if (!skb_queue_empty(&scp->other_receive_queue)) mask |= EPOLLRDBAND; @@ -2331,7 +2331,7 @@ static const struct proto_ops dn_proto_ops = { .socketpair = sock_no_socketpair, .accept = dn_accept, .getname = dn_getname, - .poll_mask = dn_poll_mask, + .poll = dn_poll, .ioctl = dn_ioctl, .listen = dn_listen, .shutdown = dn_shutdown, diff --git a/net/ieee802154/socket.c b/net/ieee802154/socket.c index a0768d2759b8..a60658c85a9a 100644 --- a/net/ieee802154/socket.c +++ b/net/ieee802154/socket.c @@ -423,7 +423,7 @@ static const struct proto_ops ieee802154_raw_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = sock_no_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = ieee802154_sock_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, @@ -969,7 +969,7 @@ static const struct proto_ops ieee802154_dgram_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = sock_no_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = ieee802154_sock_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 15e125558c76..b403499fdabe 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -986,7 +986,7 @@ const struct proto_ops inet_stream_ops = { .socketpair = sock_no_socketpair, .accept = inet_accept, .getname = inet_getname, - .poll_mask = tcp_poll_mask, + .poll = tcp_poll, .ioctl = inet_ioctl, .listen = inet_listen, .shutdown = inet_shutdown, @@ -1021,7 +1021,7 @@ const struct proto_ops inet_dgram_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = inet_getname, - .poll_mask = udp_poll_mask, + .poll = udp_poll, .ioctl = inet_ioctl, .listen = sock_no_listen, .shutdown = inet_shutdown, @@ -1042,7 +1042,7 @@ EXPORT_SYMBOL(inet_dgram_ops); /* * For SOCK_RAW sockets; should be the same as inet_dgram_ops but without - * udp_poll_mask + * udp_poll */ static const struct proto_ops inet_sockraw_ops = { .family = PF_INET, @@ -1053,7 +1053,7 @@ static const struct proto_ops inet_sockraw_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = inet_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = inet_ioctl, .listen = sock_no_listen, .shutdown = inet_shutdown, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 141acd92e58a..e7b53d2a971f 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -494,21 +494,32 @@ static inline bool tcp_stream_is_readable(const struct tcp_sock *tp, } /* - * Socket is not locked. We are protected from async events by poll logic and - * correct handling of state changes made by other threads is impossible in - * any case. + * Wait for a TCP event. + * + * Note that we don't need to lock the socket, as the upper poll layers + * take care of normal races (between the test and the event) and we don't + * go look at any of the socket buffers directly. */ -__poll_t tcp_poll_mask(struct socket *sock, __poll_t events) +__poll_t tcp_poll(struct file *file, struct socket *sock, poll_table *wait) { + __poll_t mask; struct sock *sk = sock->sk; const struct tcp_sock *tp = tcp_sk(sk); - __poll_t mask = 0; int state; + sock_poll_wait(file, sk_sleep(sk), wait); + state = inet_sk_state_load(sk); if (state == TCP_LISTEN) return inet_csk_listen_poll(sk); + /* Socket is not locked. We are protected from async events + * by poll logic and correct handling of state changes + * made by other threads is impossible in any case. + */ + + mask = 0; + /* * EPOLLHUP is certainly not done right. But poll() doesn't * have a notion of HUP in just one direction, and for a @@ -589,7 +600,7 @@ __poll_t tcp_poll_mask(struct socket *sock, __poll_t events) return mask; } -EXPORT_SYMBOL(tcp_poll_mask); +EXPORT_SYMBOL(tcp_poll); int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg) { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 9bb27df4dac5..24e116ddae79 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2591,7 +2591,7 @@ int compat_udp_getsockopt(struct sock *sk, int level, int optname, * udp_poll - wait for a UDP event. * @file - file struct * @sock - socket - * @events - events to wait for + * @wait - poll table * * This is same as datagram poll, except for the special case of * blocking sockets. If application is using a blocking fd @@ -2600,23 +2600,23 @@ int compat_udp_getsockopt(struct sock *sk, int level, int optname, * but then block when reading it. Add special case code * to work around these arguably broken applications. */ -__poll_t udp_poll_mask(struct socket *sock, __poll_t events) +__poll_t udp_poll(struct file *file, struct socket *sock, poll_table *wait) { - __poll_t mask = datagram_poll_mask(sock, events); + __poll_t mask = datagram_poll(file, sock, wait); struct sock *sk = sock->sk; if (!skb_queue_empty(&udp_sk(sk)->reader_queue)) mask |= EPOLLIN | EPOLLRDNORM; /* Check for false positives due to checksum errors */ - if ((mask & EPOLLRDNORM) && !(sock->file->f_flags & O_NONBLOCK) && + if ((mask & EPOLLRDNORM) && !(file->f_flags & O_NONBLOCK) && !(sk->sk_shutdown & RCV_SHUTDOWN) && first_packet_length(sk) == -1) mask &= ~(EPOLLIN | EPOLLRDNORM); return mask; } -EXPORT_SYMBOL(udp_poll_mask); +EXPORT_SYMBOL(udp_poll); int udp_abort(struct sock *sk, int err) { diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 74f2a261e8df..9ed0eae91758 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -570,7 +570,7 @@ const struct proto_ops inet6_stream_ops = { .socketpair = sock_no_socketpair, /* a do nothing */ .accept = inet_accept, /* ok */ .getname = inet6_getname, - .poll_mask = tcp_poll_mask, /* ok */ + .poll = tcp_poll, /* ok */ .ioctl = inet6_ioctl, /* must change */ .listen = inet_listen, /* ok */ .shutdown = inet_shutdown, /* ok */ @@ -603,7 +603,7 @@ const struct proto_ops inet6_dgram_ops = { .socketpair = sock_no_socketpair, /* a do nothing */ .accept = sock_no_accept, /* a do nothing */ .getname = inet6_getname, - .poll_mask = udp_poll_mask, /* ok */ + .poll = udp_poll, /* ok */ .ioctl = inet6_ioctl, /* must change */ .listen = sock_no_listen, /* ok */ .shutdown = inet_shutdown, /* ok */ diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index ce6f0d15b5dd..afc307c89d1a 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -1334,7 +1334,7 @@ void raw6_proc_exit(void) } #endif /* CONFIG_PROC_FS */ -/* Same as inet6_dgram_ops, sans udp_poll_mask. */ +/* Same as inet6_dgram_ops, sans udp_poll. */ const struct proto_ops inet6_sockraw_ops = { .family = PF_INET6, .owner = THIS_MODULE, @@ -1344,7 +1344,7 @@ const struct proto_ops inet6_sockraw_ops = { .socketpair = sock_no_socketpair, /* a do nothing */ .accept = sock_no_accept, /* a do nothing */ .getname = inet6_getname, - .poll_mask = datagram_poll_mask, /* ok */ + .poll = datagram_poll, /* ok */ .ioctl = inet6_ioctl, /* must change */ .listen = sock_no_listen, /* ok */ .shutdown = inet_shutdown, /* ok */ diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index 68e86257a549..893a022f9620 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -1488,11 +1488,14 @@ static inline __poll_t iucv_accept_poll(struct sock *parent) return 0; } -static __poll_t iucv_sock_poll_mask(struct socket *sock, __poll_t events) +__poll_t iucv_sock_poll(struct file *file, struct socket *sock, + poll_table *wait) { struct sock *sk = sock->sk; __poll_t mask = 0; + sock_poll_wait(file, sk_sleep(sk), wait); + if (sk->sk_state == IUCV_LISTEN) return iucv_accept_poll(sk); @@ -2385,7 +2388,7 @@ static const struct proto_ops iucv_sock_ops = { .getname = iucv_sock_getname, .sendmsg = iucv_sock_sendmsg, .recvmsg = iucv_sock_recvmsg, - .poll_mask = iucv_sock_poll_mask, + .poll = iucv_sock_poll, .ioctl = sock_no_ioctl, .mmap = sock_no_mmap, .socketpair = sock_no_socketpair, diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c index 84b7d5c6fec8..d3601d421571 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -1336,9 +1336,9 @@ static void init_kcm_sock(struct kcm_sock *kcm, struct kcm_mux *mux) struct list_head *head; int index = 0; - /* For SOCK_SEQPACKET sock type, datagram_poll_mask checks the sk_state, - * so we set sk_state, otherwise epoll_wait always returns right away - * with EPOLLHUP + /* For SOCK_SEQPACKET sock type, datagram_poll checks the sk_state, so + * we set sk_state, otherwise epoll_wait always returns right away with + * EPOLLHUP */ kcm->sk.sk_state = TCP_ESTABLISHED; @@ -1903,7 +1903,7 @@ static const struct proto_ops kcm_dgram_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = sock_no_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = kcm_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, @@ -1924,7 +1924,7 @@ static const struct proto_ops kcm_seqpacket_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = sock_no_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = kcm_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, diff --git a/net/key/af_key.c b/net/key/af_key.c index 8bdc1cbe490a..5e1d2946ffbf 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -3751,7 +3751,7 @@ static const struct proto_ops pfkey_ops = { /* Now the operations that really occur. */ .release = pfkey_release, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .sendmsg = pfkey_sendmsg, .recvmsg = pfkey_recvmsg, }; diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 181073bf6925..a9c05b2bc1b0 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -613,7 +613,7 @@ static const struct proto_ops l2tp_ip_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = l2tp_ip_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = inet_ioctl, .listen = sock_no_listen, .shutdown = inet_shutdown, diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index 336e4c00abbc..957369192ca1 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -754,7 +754,7 @@ static const struct proto_ops l2tp_ip6_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = l2tp_ip6_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = inet6_ioctl, .listen = sock_no_listen, .shutdown = inet_shutdown, diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 55188382845c..e398797878a9 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -1818,7 +1818,7 @@ static const struct proto_ops pppol2tp_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = pppol2tp_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = pppol2tp_setsockopt, diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 804de8490186..1beeea9549fa 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -1192,7 +1192,7 @@ static const struct proto_ops llc_ui_ops = { .socketpair = sock_no_socketpair, .accept = llc_ui_accept, .getname = llc_ui_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = llc_ui_ioctl, .listen = llc_ui_listen, .shutdown = llc_ui_shutdown, diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 1189b84413d5..393573a99a5a 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2658,7 +2658,7 @@ static const struct proto_ops netlink_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = netlink_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = netlink_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 93fbcafbf388..03f37c4e64fe 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -1355,7 +1355,7 @@ static const struct proto_ops nr_proto_ops = { .socketpair = sock_no_socketpair, .accept = nr_accept, .getname = nr_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = nr_ioctl, .listen = nr_listen, .shutdown = sock_no_shutdown, diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c index ab5bb14b49af..ea0c0c6f1874 100644 --- a/net/nfc/llcp_sock.c +++ b/net/nfc/llcp_sock.c @@ -548,13 +548,16 @@ static inline __poll_t llcp_accept_poll(struct sock *parent) return 0; } -static __poll_t llcp_sock_poll_mask(struct socket *sock, __poll_t events) +static __poll_t llcp_sock_poll(struct file *file, struct socket *sock, + poll_table *wait) { struct sock *sk = sock->sk; __poll_t mask = 0; pr_debug("%p\n", sk); + sock_poll_wait(file, sk_sleep(sk), wait); + if (sk->sk_state == LLCP_LISTEN) return llcp_accept_poll(sk); @@ -896,7 +899,7 @@ static const struct proto_ops llcp_sock_ops = { .socketpair = sock_no_socketpair, .accept = llcp_sock_accept, .getname = llcp_sock_getname, - .poll_mask = llcp_sock_poll_mask, + .poll = llcp_sock_poll, .ioctl = sock_no_ioctl, .listen = llcp_sock_listen, .shutdown = sock_no_shutdown, @@ -916,7 +919,7 @@ static const struct proto_ops llcp_rawsock_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = llcp_sock_getname, - .poll_mask = llcp_sock_poll_mask, + .poll = llcp_sock_poll, .ioctl = sock_no_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c index 60c322531c49..e2188deb08dc 100644 --- a/net/nfc/rawsock.c +++ b/net/nfc/rawsock.c @@ -284,7 +284,7 @@ static const struct proto_ops rawsock_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = sock_no_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = sock_no_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, @@ -304,7 +304,7 @@ static const struct proto_ops rawsock_raw_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = sock_no_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = sock_no_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index ff8e7e245c37..57634bc3da74 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -4076,11 +4076,12 @@ static int packet_ioctl(struct socket *sock, unsigned int cmd, return 0; } -static __poll_t packet_poll_mask(struct socket *sock, __poll_t events) +static __poll_t packet_poll(struct file *file, struct socket *sock, + poll_table *wait) { struct sock *sk = sock->sk; struct packet_sock *po = pkt_sk(sk); - __poll_t mask = datagram_poll_mask(sock, events); + __poll_t mask = datagram_poll(file, sock, wait); spin_lock_bh(&sk->sk_receive_queue.lock); if (po->rx_ring.pg_vec) { @@ -4422,7 +4423,7 @@ static const struct proto_ops packet_ops_spkt = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = packet_getname_spkt, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = packet_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, @@ -4443,7 +4444,7 @@ static const struct proto_ops packet_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = packet_getname, - .poll_mask = packet_poll_mask, + .poll = packet_poll, .ioctl = packet_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, diff --git a/net/phonet/socket.c b/net/phonet/socket.c index c295c4e20f01..30187990257f 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -340,12 +340,15 @@ static int pn_socket_getname(struct socket *sock, struct sockaddr *addr, return sizeof(struct sockaddr_pn); } -static __poll_t pn_socket_poll_mask(struct socket *sock, __poll_t events) +static __poll_t pn_socket_poll(struct file *file, struct socket *sock, + poll_table *wait) { struct sock *sk = sock->sk; struct pep_sock *pn = pep_sk(sk); __poll_t mask = 0; + poll_wait(file, sk_sleep(sk), wait); + if (sk->sk_state == TCP_CLOSE) return EPOLLERR; if (!skb_queue_empty(&sk->sk_receive_queue)) @@ -445,7 +448,7 @@ const struct proto_ops phonet_dgram_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = pn_socket_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = pn_socket_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, @@ -470,7 +473,7 @@ const struct proto_ops phonet_stream_ops = { .socketpair = sock_no_socketpair, .accept = pn_socket_accept, .getname = pn_socket_getname, - .poll_mask = pn_socket_poll_mask, + .poll = pn_socket_poll, .ioctl = pn_socket_ioctl, .listen = pn_socket_listen, .shutdown = sock_no_shutdown, diff --git a/net/qrtr/qrtr.c b/net/qrtr/qrtr.c index 1b5025ea5b04..2aa07b547b16 100644 --- a/net/qrtr/qrtr.c +++ b/net/qrtr/qrtr.c @@ -1023,7 +1023,7 @@ static const struct proto_ops qrtr_proto_ops = { .recvmsg = qrtr_recvmsg, .getname = qrtr_getname, .ioctl = qrtr_ioctl, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .shutdown = sock_no_shutdown, .setsockopt = sock_no_setsockopt, .getsockopt = sock_no_getsockopt, diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index ebe42e7eb456..d00a0ef39a56 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -1470,7 +1470,7 @@ static const struct proto_ops rose_proto_ops = { .socketpair = sock_no_socketpair, .accept = rose_accept, .getname = rose_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = rose_ioctl, .listen = rose_listen, .shutdown = sock_no_shutdown, diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 3b1ac93efee2..2b463047dd7b 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -734,11 +734,15 @@ static int rxrpc_getsockopt(struct socket *sock, int level, int optname, /* * permit an RxRPC socket to be polled */ -static __poll_t rxrpc_poll_mask(struct socket *sock, __poll_t events) +static __poll_t rxrpc_poll(struct file *file, struct socket *sock, + poll_table *wait) { struct sock *sk = sock->sk; struct rxrpc_sock *rx = rxrpc_sk(sk); - __poll_t mask = 0; + __poll_t mask; + + sock_poll_wait(file, sk_sleep(sk), wait); + mask = 0; /* the socket is readable if there are any messages waiting on the Rx * queue */ @@ -945,7 +949,7 @@ static const struct proto_ops rxrpc_rpc_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = sock_no_getname, - .poll_mask = rxrpc_poll_mask, + .poll = rxrpc_poll, .ioctl = sock_no_ioctl, .listen = rxrpc_listen, .shutdown = rxrpc_shutdown, diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 7339918a805d..0cd2e764f47f 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -1010,7 +1010,7 @@ static const struct proto_ops inet6_seqpacket_ops = { .socketpair = sock_no_socketpair, .accept = inet_accept, .getname = sctp_getname, - .poll_mask = sctp_poll_mask, + .poll = sctp_poll, .ioctl = inet6_ioctl, .listen = sctp_inet_listen, .shutdown = inet_shutdown, diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 5dffbc493008..67f73d3a1356 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1016,7 +1016,7 @@ static const struct proto_ops inet_seqpacket_ops = { .socketpair = sock_no_socketpair, .accept = inet_accept, .getname = inet_getname, /* Semantics are different. */ - .poll_mask = sctp_poll_mask, + .poll = sctp_poll, .ioctl = inet_ioctl, .listen = sctp_inet_listen, .shutdown = inet_shutdown, /* Looks harmless. */ diff --git a/net/sctp/socket.c b/net/sctp/socket.c index d20f7addee19..ce620e878538 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -7717,12 +7717,14 @@ out: * here, again, by modeling the current TCP/UDP code. We don't have * a good way to test with it yet. */ -__poll_t sctp_poll_mask(struct socket *sock, __poll_t events) +__poll_t sctp_poll(struct file *file, struct socket *sock, poll_table *wait) { struct sock *sk = sock->sk; struct sctp_sock *sp = sctp_sk(sk); __poll_t mask; + poll_wait(file, sk_sleep(sk), wait); + sock_rps_record_flow(sk); /* A TCP-style listening socket becomes readable when the accept queue diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index da7f02edcd37..973b4471b532 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -1273,7 +1273,8 @@ static __poll_t smc_accept_poll(struct sock *parent) return mask; } -static __poll_t smc_poll_mask(struct socket *sock, __poll_t events) +static __poll_t smc_poll(struct file *file, struct socket *sock, + poll_table *wait) { struct sock *sk = sock->sk; __poll_t mask = 0; @@ -1289,7 +1290,7 @@ static __poll_t smc_poll_mask(struct socket *sock, __poll_t events) if ((sk->sk_state == SMC_INIT) || smc->use_fallback) { /* delegate to CLC child sock */ release_sock(sk); - mask = smc->clcsock->ops->poll_mask(smc->clcsock, events); + mask = smc->clcsock->ops->poll(file, smc->clcsock, wait); lock_sock(sk); sk->sk_err = smc->clcsock->sk->sk_err; if (sk->sk_err) { @@ -1307,6 +1308,11 @@ static __poll_t smc_poll_mask(struct socket *sock, __poll_t events) } } } else { + if (sk->sk_state != SMC_CLOSED) { + release_sock(sk); + sock_poll_wait(file, sk_sleep(sk), wait); + lock_sock(sk); + } if (sk->sk_err) mask |= EPOLLERR; if ((sk->sk_shutdown == SHUTDOWN_MASK) || @@ -1619,7 +1625,7 @@ static const struct proto_ops smc_sock_ops = { .socketpair = sock_no_socketpair, .accept = smc_accept, .getname = smc_getname, - .poll_mask = smc_poll_mask, + .poll = smc_poll, .ioctl = smc_ioctl, .listen = smc_listen, .shutdown = smc_shutdown, diff --git a/net/socket.c b/net/socket.c index 8a109012608a..a564c6ed19d5 100644 --- a/net/socket.c +++ b/net/socket.c @@ -117,10 +117,8 @@ static ssize_t sock_write_iter(struct kiocb *iocb, struct iov_iter *from); static int sock_mmap(struct file *file, struct vm_area_struct *vma); static int sock_close(struct inode *inode, struct file *file); -static struct wait_queue_head *sock_get_poll_head(struct file *file, - __poll_t events); -static __poll_t sock_poll_mask(struct file *file, __poll_t); -static __poll_t sock_poll(struct file *file, struct poll_table_struct *wait); +static __poll_t sock_poll(struct file *file, + struct poll_table_struct *wait); static long sock_ioctl(struct file *file, unsigned int cmd, unsigned long arg); #ifdef CONFIG_COMPAT static long compat_sock_ioctl(struct file *file, @@ -143,8 +141,6 @@ static const struct file_operations socket_file_ops = { .llseek = no_llseek, .read_iter = sock_read_iter, .write_iter = sock_write_iter, - .get_poll_head = sock_get_poll_head, - .poll_mask = sock_poll_mask, .poll = sock_poll, .unlocked_ioctl = sock_ioctl, #ifdef CONFIG_COMPAT @@ -1130,48 +1126,14 @@ out_release: } EXPORT_SYMBOL(sock_create_lite); -static struct wait_queue_head *sock_get_poll_head(struct file *file, - __poll_t events) -{ - struct socket *sock = file->private_data; - - if (!sock->ops->poll_mask) - return NULL; - sock_poll_busy_loop(sock, events); - return sk_sleep(sock->sk); -} - -static __poll_t sock_poll_mask(struct file *file, __poll_t events) -{ - struct socket *sock = file->private_data; - - /* - * We need to be sure we are in sync with the socket flags modification. - * - * This memory barrier is paired in the wq_has_sleeper. - */ - smp_mb(); - - /* this socket can poll_ll so tell the system call */ - return sock->ops->poll_mask(sock, events) | - (sk_can_busy_loop(sock->sk) ? POLL_BUSY_LOOP : 0); -} - /* No kernel lock held - perfect */ static __poll_t sock_poll(struct file *file, poll_table *wait) { struct socket *sock = file->private_data; - __poll_t events = poll_requested_events(wait), mask = 0; - - if (sock->ops->poll) { - sock_poll_busy_loop(sock, events); - mask = sock->ops->poll(file, sock, wait); - } else if (sock->ops->poll_mask) { - sock_poll_wait(file, sock_get_poll_head(file, events), wait); - mask = sock->ops->poll_mask(sock, events); - } + __poll_t events = poll_requested_events(wait); - return mask | sock_poll_busy_flag(sock); + sock_poll_busy_loop(sock, events); + return sock->ops->poll(file, sock, wait) | sock_poll_busy_flag(sock); } static int sock_mmap(struct file *file, struct vm_area_struct *vma) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 14a5d055717d..930852c54d7a 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -692,9 +692,10 @@ static int tipc_getname(struct socket *sock, struct sockaddr *uaddr, } /** - * tipc_poll - read pollmask + * tipc_poll - read and possibly block on pollmask * @file: file structure associated with the socket * @sock: socket for which to calculate the poll bits + * @wait: ??? * * Returns pollmask value * @@ -708,12 +709,15 @@ static int tipc_getname(struct socket *sock, struct sockaddr *uaddr, * imply that the operation will succeed, merely that it should be performed * and will not block. */ -static __poll_t tipc_poll_mask(struct socket *sock, __poll_t events) +static __poll_t tipc_poll(struct file *file, struct socket *sock, + poll_table *wait) { struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); __poll_t revents = 0; + sock_poll_wait(file, sk_sleep(sk), wait); + if (sk->sk_shutdown & RCV_SHUTDOWN) revents |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM; if (sk->sk_shutdown == SHUTDOWN_MASK) @@ -3033,7 +3037,7 @@ static const struct proto_ops msg_ops = { .socketpair = tipc_socketpair, .accept = sock_no_accept, .getname = tipc_getname, - .poll_mask = tipc_poll_mask, + .poll = tipc_poll, .ioctl = tipc_ioctl, .listen = sock_no_listen, .shutdown = tipc_shutdown, @@ -3054,7 +3058,7 @@ static const struct proto_ops packet_ops = { .socketpair = tipc_socketpair, .accept = tipc_accept, .getname = tipc_getname, - .poll_mask = tipc_poll_mask, + .poll = tipc_poll, .ioctl = tipc_ioctl, .listen = tipc_listen, .shutdown = tipc_shutdown, @@ -3075,7 +3079,7 @@ static const struct proto_ops stream_ops = { .socketpair = tipc_socketpair, .accept = tipc_accept, .getname = tipc_getname, - .poll_mask = tipc_poll_mask, + .poll = tipc_poll, .ioctl = tipc_ioctl, .listen = tipc_listen, .shutdown = tipc_shutdown, diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c index a127d61e8af9..301f22430469 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c @@ -712,7 +712,7 @@ static int __init tls_register(void) build_protos(tls_prots[TLSV4], &tcp_prot); tls_sw_proto_ops = inet_stream_ops; - tls_sw_proto_ops.poll_mask = tls_sw_poll_mask; + tls_sw_proto_ops.poll = tls_sw_poll; tls_sw_proto_ops.splice_read = tls_sw_splice_read; #ifdef CONFIG_TLS_DEVICE diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index f127fac88acf..d2380548f8f6 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -919,22 +919,23 @@ splice_read_end: return copied ? : err; } -__poll_t tls_sw_poll_mask(struct socket *sock, __poll_t events) +unsigned int tls_sw_poll(struct file *file, struct socket *sock, + struct poll_table_struct *wait) { + unsigned int ret; struct sock *sk = sock->sk; struct tls_context *tls_ctx = tls_get_ctx(sk); struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); - __poll_t mask; - /* Grab EPOLLOUT and EPOLLHUP from the underlying socket */ - mask = ctx->sk_poll_mask(sock, events); + /* Grab POLLOUT and POLLHUP from the underlying socket */ + ret = ctx->sk_poll(file, sock, wait); - /* Clear EPOLLIN bits, and set based on recv_pkt */ - mask &= ~(EPOLLIN | EPOLLRDNORM); + /* Clear POLLIN bits, and set based on recv_pkt */ + ret &= ~(POLLIN | POLLRDNORM); if (ctx->recv_pkt) - mask |= EPOLLIN | EPOLLRDNORM; + ret |= POLLIN | POLLRDNORM; - return mask; + return ret; } static int tls_read_size(struct strparser *strp, struct sk_buff *skb) @@ -1191,7 +1192,7 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx) sk->sk_data_ready = tls_data_ready; write_unlock_bh(&sk->sk_callback_lock); - sw_ctx_rx->sk_poll_mask = sk->sk_socket->ops->poll_mask; + sw_ctx_rx->sk_poll = sk->sk_socket->ops->poll; strp_check_rcv(&sw_ctx_rx->strp); } diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 95b02a71fd47..e5473c03d667 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -638,8 +638,9 @@ static int unix_stream_connect(struct socket *, struct sockaddr *, static int unix_socketpair(struct socket *, struct socket *); static int unix_accept(struct socket *, struct socket *, int, bool); static int unix_getname(struct socket *, struct sockaddr *, int); -static __poll_t unix_poll_mask(struct socket *, __poll_t); -static __poll_t unix_dgram_poll_mask(struct socket *, __poll_t); +static __poll_t unix_poll(struct file *, struct socket *, poll_table *); +static __poll_t unix_dgram_poll(struct file *, struct socket *, + poll_table *); static int unix_ioctl(struct socket *, unsigned int, unsigned long); static int unix_shutdown(struct socket *, int); static int unix_stream_sendmsg(struct socket *, struct msghdr *, size_t); @@ -680,7 +681,7 @@ static const struct proto_ops unix_stream_ops = { .socketpair = unix_socketpair, .accept = unix_accept, .getname = unix_getname, - .poll_mask = unix_poll_mask, + .poll = unix_poll, .ioctl = unix_ioctl, .listen = unix_listen, .shutdown = unix_shutdown, @@ -703,7 +704,7 @@ static const struct proto_ops unix_dgram_ops = { .socketpair = unix_socketpair, .accept = sock_no_accept, .getname = unix_getname, - .poll_mask = unix_dgram_poll_mask, + .poll = unix_dgram_poll, .ioctl = unix_ioctl, .listen = sock_no_listen, .shutdown = unix_shutdown, @@ -725,7 +726,7 @@ static const struct proto_ops unix_seqpacket_ops = { .socketpair = unix_socketpair, .accept = unix_accept, .getname = unix_getname, - .poll_mask = unix_dgram_poll_mask, + .poll = unix_dgram_poll, .ioctl = unix_ioctl, .listen = unix_listen, .shutdown = unix_shutdown, @@ -2629,10 +2630,13 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return err; } -static __poll_t unix_poll_mask(struct socket *sock, __poll_t events) +static __poll_t unix_poll(struct file *file, struct socket *sock, poll_table *wait) { struct sock *sk = sock->sk; - __poll_t mask = 0; + __poll_t mask; + + sock_poll_wait(file, sk_sleep(sk), wait); + mask = 0; /* exceptional events? */ if (sk->sk_err) @@ -2661,11 +2665,15 @@ static __poll_t unix_poll_mask(struct socket *sock, __poll_t events) return mask; } -static __poll_t unix_dgram_poll_mask(struct socket *sock, __poll_t events) +static __poll_t unix_dgram_poll(struct file *file, struct socket *sock, + poll_table *wait) { struct sock *sk = sock->sk, *other; - int writable; - __poll_t mask = 0; + unsigned int writable; + __poll_t mask; + + sock_poll_wait(file, sk_sleep(sk), wait); + mask = 0; /* exceptional events? */ if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue)) @@ -2691,7 +2699,7 @@ static __poll_t unix_dgram_poll_mask(struct socket *sock, __poll_t events) } /* No write status requested, avoid expensive OUT tests. */ - if (!(events & (EPOLLWRBAND|EPOLLWRNORM|EPOLLOUT))) + if (!(poll_requested_events(wait) & (EPOLLWRBAND|EPOLLWRNORM|EPOLLOUT))) return mask; writable = unix_writable(sk); diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index bb5d5fa68c35..c1076c19b858 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -850,11 +850,18 @@ static int vsock_shutdown(struct socket *sock, int mode) return err; } -static __poll_t vsock_poll_mask(struct socket *sock, __poll_t events) +static __poll_t vsock_poll(struct file *file, struct socket *sock, + poll_table *wait) { - struct sock *sk = sock->sk; - struct vsock_sock *vsk = vsock_sk(sk); - __poll_t mask = 0; + struct sock *sk; + __poll_t mask; + struct vsock_sock *vsk; + + sk = sock->sk; + vsk = vsock_sk(sk); + + poll_wait(file, sk_sleep(sk), wait); + mask = 0; if (sk->sk_err) /* Signify that there has been an error on this socket. */ @@ -1084,7 +1091,7 @@ static const struct proto_ops vsock_dgram_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = vsock_getname, - .poll_mask = vsock_poll_mask, + .poll = vsock_poll, .ioctl = sock_no_ioctl, .listen = sock_no_listen, .shutdown = vsock_shutdown, @@ -1842,7 +1849,7 @@ static const struct proto_ops vsock_stream_ops = { .socketpair = sock_no_socketpair, .accept = vsock_accept, .getname = vsock_getname, - .poll_mask = vsock_poll_mask, + .poll = vsock_poll, .ioctl = sock_no_ioctl, .listen = vsock_listen, .shutdown = vsock_shutdown, diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index f93365ae0fdd..d49aa79b7997 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -1750,7 +1750,7 @@ static const struct proto_ops x25_proto_ops = { .socketpair = sock_no_socketpair, .accept = x25_accept, .getname = x25_getname, - .poll_mask = datagram_poll_mask, + .poll = datagram_poll, .ioctl = x25_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = compat_x25_ioctl, diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 3b3410ada097..59fb7d3c36a3 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -303,9 +303,10 @@ static int xsk_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len) return (xs->zc) ? xsk_zc_xmit(sk) : xsk_generic_xmit(sk, m, total_len); } -static __poll_t xsk_poll_mask(struct socket *sock, __poll_t events) +static unsigned int xsk_poll(struct file *file, struct socket *sock, + struct poll_table_struct *wait) { - __poll_t mask = datagram_poll_mask(sock, events); + unsigned int mask = datagram_poll(file, sock, wait); struct sock *sk = sock->sk; struct xdp_sock *xs = xdp_sk(sk); @@ -696,7 +697,7 @@ static const struct proto_ops xsk_proto_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = sock_no_getname, - .poll_mask = xsk_poll_mask, + .poll = xsk_poll, .ioctl = sock_no_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, -- cgit v1.2.3 From 4557641b4c7046625c026fb809c47ef0d43ae595 Mon Sep 17 00:00:00 2001 From: Ross Zwisler Date: Tue, 26 Jun 2018 16:30:39 -0600 Subject: pmem: only set QUEUE_FLAG_DAX for fsdax mode QUEUE_FLAG_DAX is an indication that a given block device supports filesystem DAX and should not be set for PMEM namespaces which are in "raw" mode. These namespaces lack struct page and are prevented from participating in filesystem DAX as of commit 569d0365f571 ("dax: require 'struct page' by default for filesystem dax"). Signed-off-by: Ross Zwisler Suggested-by: Mike Snitzer Fixes: 569d0365f571 ("dax: require 'struct page' by default for filesystem dax") Cc: stable@vger.kernel.org Acked-by: Dan Williams Reviewed-by: Toshi Kani Signed-off-by: Mike Snitzer --- drivers/nvdimm/pmem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index 68940356cad3..8b1fd7f1a224 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c @@ -414,7 +414,8 @@ static int pmem_attach_disk(struct device *dev, blk_queue_logical_block_size(q, pmem_sector_size(ndns)); blk_queue_max_hw_sectors(q, UINT_MAX); blk_queue_flag_set(QUEUE_FLAG_NONROT, q); - blk_queue_flag_set(QUEUE_FLAG_DAX, q); + if (pmem->pfn_flags & PFN_MAP) + blk_queue_flag_set(QUEUE_FLAG_DAX, q); q->queuedata = pmem; disk = alloc_disk_node(0, nid); -- cgit v1.2.3 From 15256f6cc4b44f2e70503758150267fd2a53c0d6 Mon Sep 17 00:00:00 2001 From: Ross Zwisler Date: Tue, 26 Jun 2018 16:30:40 -0600 Subject: dax: check for QUEUE_FLAG_DAX in bdev_dax_supported() Add an explicit check for QUEUE_FLAG_DAX to __bdev_dax_supported(). This is needed for DM configurations where the first element in the dm-linear or dm-stripe target supports DAX, but other elements do not. Without this check __bdev_dax_supported() will pass for such devices, letting a filesystem on that device mount with the DAX option. Signed-off-by: Ross Zwisler Suggested-by: Mike Snitzer Fixes: commit 545ed20e6df6 ("dm: add infrastructure for DAX support") Cc: stable@vger.kernel.org Acked-by: Dan Williams Reviewed-by: Toshi Kani Signed-off-by: Mike Snitzer --- drivers/dax/super.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/dax/super.c b/drivers/dax/super.c index 903d9c473749..45276abf03aa 100644 --- a/drivers/dax/super.c +++ b/drivers/dax/super.c @@ -86,6 +86,7 @@ bool __bdev_dax_supported(struct block_device *bdev, int blocksize) { struct dax_device *dax_dev; bool dax_enabled = false; + struct request_queue *q; pgoff_t pgoff; int err, id; void *kaddr; @@ -99,6 +100,13 @@ bool __bdev_dax_supported(struct block_device *bdev, int blocksize) return false; } + q = bdev_get_queue(bdev); + if (!q || !blk_queue_dax(q)) { + pr_debug("%s: error: request queue doesn't support dax\n", + bdevname(bdev, buf)); + return false; + } + err = bdev_dax_pgoff(bdev, 0, PAGE_SIZE, &pgoff); if (err) { pr_debug("%s: error: unaligned partition for dax\n", -- cgit v1.2.3 From dbc626597c39b24cefce09fbd8e9dea85869a801 Mon Sep 17 00:00:00 2001 From: Ross Zwisler Date: Tue, 26 Jun 2018 16:30:41 -0600 Subject: dm: prevent DAX mounts if not supported Currently device_supports_dax() just checks to see if the QUEUE_FLAG_DAX flag is set on the device's request queue to decide whether or not the device supports filesystem DAX. Really we should be using bdev_dax_supported() like filesystems do at mount time. This performs other tests like checking to make sure the dax_direct_access() path works. We also explicitly clear QUEUE_FLAG_DAX on the DM device's request queue if any of the underlying devices do not support DAX. This makes the handling of QUEUE_FLAG_DAX consistent with the setting/clearing of most other flags in dm_table_set_restrictions(). Now that bdev_dax_supported() explicitly checks for QUEUE_FLAG_DAX, this will ensure that filesystems built upon DM devices will only be able to mount with DAX if all underlying devices also support DAX. Signed-off-by: Ross Zwisler Fixes: commit 545ed20e6df6 ("dm: add infrastructure for DAX support") Cc: stable@vger.kernel.org Acked-by: Dan Williams Reviewed-by: Toshi Kani Signed-off-by: Mike Snitzer --- drivers/md/dm-table.c | 7 ++++--- drivers/md/dm.c | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 938766794c2e..3d0e2c198f06 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -885,9 +885,7 @@ EXPORT_SYMBOL_GPL(dm_table_set_type); static int device_supports_dax(struct dm_target *ti, struct dm_dev *dev, sector_t start, sector_t len, void *data) { - struct request_queue *q = bdev_get_queue(dev->bdev); - - return q && blk_queue_dax(q); + return bdev_dax_supported(dev->bdev, PAGE_SIZE); } static bool dm_table_supports_dax(struct dm_table *t) @@ -1907,6 +1905,9 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q, if (dm_table_supports_dax(t)) blk_queue_flag_set(QUEUE_FLAG_DAX, q); + else + blk_queue_flag_clear(QUEUE_FLAG_DAX, q); + if (dm_table_supports_dax_write_cache(t)) dax_write_cache(t->md->dax_dev, true); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index a3b103e8e3ce..b0dd7027848b 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1056,8 +1056,7 @@ static long dm_dax_direct_access(struct dax_device *dax_dev, pgoff_t pgoff, if (len < 1) goto out; nr_pages = min(len, nr_pages); - if (ti->type->direct_access) - ret = ti->type->direct_access(ti, pgoff, nr_pages, kaddr, pfn); + ret = ti->type->direct_access(ti, pgoff, nr_pages, kaddr, pfn); out: dm_put_live_table(md, srcu_idx); -- cgit v1.2.3 From 2a2c8ee2d72c4f1ba0f7fbb02dc74f971df0f934 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 16 Jun 2018 22:37:56 +0900 Subject: Revert "i2c: algo-bit: init the bus to a known state" This reverts commit 3e5f06bed72fe72166a6778f630241a893f67799. As per bugzilla #200045, this caused a regression. I don't really see a way to fix it without having the hardware. So, revert the patch and I will fix the issue I was seeing originally in the i2c-gpio driver itself. I couldn't find new users of this algorithm since, so there should be no one depending on the new behaviour. Reported-by: Sergey Larin Fixes: 3e5f06bed72f ("i2c: algo-bit: init the bus to a known state") Signed-off-by: Wolfram Sang Acked-by: Alex Deucher Tested-by: Sergey Larin Signed-off-by: Wolfram Sang Cc: stable@kernel.org --- drivers/i2c/algos/i2c-algo-bit.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c index 4a34f311e1ff..0c0eb16d710f 100644 --- a/drivers/i2c/algos/i2c-algo-bit.c +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -647,11 +647,6 @@ static int __i2c_bit_add_bus(struct i2c_adapter *adap, if (bit_adap->getscl == NULL) adap->quirks = &i2c_bit_quirk_no_clk_stretch; - /* Bring bus to a known state. Looks like STOP if bus is not free yet */ - setscl(bit_adap, 1); - udelay(bit_adap->udelay); - setsda(bit_adap, 1); - ret = add_adapter(adap); if (ret < 0) return ret; -- cgit v1.2.3 From 2173ed0adc7f0473e6b6ad636d8684a0d82da5e9 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 16 Jun 2018 22:37:57 +0900 Subject: i2c: algos: bit: mention our experience about initial states So, if somebody wants to re-implement this in the future, we pinpoint to a problem case. Signed-off-by: Wolfram Sang Acked-by: Alex Deucher Signed-off-by: Wolfram Sang --- drivers/i2c/algos/i2c-algo-bit.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c index 0c0eb16d710f..6ec65adaba49 100644 --- a/drivers/i2c/algos/i2c-algo-bit.c +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -647,6 +647,11 @@ static int __i2c_bit_add_bus(struct i2c_adapter *adap, if (bit_adap->getscl == NULL) adap->quirks = &i2c_bit_quirk_no_clk_stretch; + /* + * We tried forcing SCL/SDA to an initial state here. But that caused a + * regression, sadly. Check Bugzilla #200045 for details. + */ + ret = add_adapter(adap); if (ret < 0) return ret; -- cgit v1.2.3 From 9aa613674f89d01248ae2e4afe691b515ff8fbb6 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 20 Jun 2018 11:43:23 +0200 Subject: i2c: smbus: kill memory leak on emulated and failed DMA SMBus xfers If DMA safe memory was allocated, but the subsequent I2C transfer fails the memory is leaked. Plug this leak. Fixes: 8a77821e74d6 ("i2c: smbus: use DMA safe buffers for emulated SMBus transactions") Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang Cc: stable@kernel.org --- drivers/i2c/i2c-core-smbus.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index f3f683041e7f..51970bae3c4a 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -465,15 +465,18 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, status = i2c_transfer(adapter, msg, num); if (status < 0) - return status; - if (status != num) - return -EIO; + goto cleanup; + if (status != num) { + status = -EIO; + goto cleanup; + } + status = 0; /* Check PEC if last message is a read */ if (i && (msg[num-1].flags & I2C_M_RD)) { status = i2c_smbus_check_pec(partial_pec, &msg[num-1]); if (status < 0) - return status; + goto cleanup; } if (read_write == I2C_SMBUS_READ) @@ -499,12 +502,13 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, break; } +cleanup: if (msg[0].flags & I2C_M_DMA_SAFE) kfree(msg[0].buf); if (msg[1].flags & I2C_M_DMA_SAFE) kfree(msg[1].buf); - return 0; + return status; } /** -- cgit v1.2.3 From 12b731dd46d9ee646318e6e9dc587314a3908a46 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 16 Jun 2018 21:56:36 +0900 Subject: i2c: gpio: initialize SCL to HIGH again It seems that during the conversion from gpio* to gpiod*, the initial state of SCL was wrongly switched to LOW. Fix it to be HIGH again. Fixes: 7bb75029ef34 ("i2c: gpio: Enforce open drain through gpiolib") Signed-off-by: Wolfram Sang Tested-by: Geert Uytterhoeven Reviewed-by: Linus Walleij Signed-off-by: Wolfram Sang Cc: stable@kernel.org --- drivers/i2c/busses/i2c-gpio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 005e6e0330c2..66f85bbf3591 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -279,9 +279,9 @@ static int i2c_gpio_probe(struct platform_device *pdev) * required for an I2C bus. */ if (pdata->scl_is_open_drain) - gflags = GPIOD_OUT_LOW; + gflags = GPIOD_OUT_HIGH; else - gflags = GPIOD_OUT_LOW_OPEN_DRAIN; + gflags = GPIOD_OUT_HIGH_OPEN_DRAIN; priv->scl = i2c_gpio_get_desc(dev, "scl", 1, gflags); if (IS_ERR(priv->scl)) return PTR_ERR(priv->scl); -- cgit v1.2.3 From fad2d4ef636654e926d374ef038f4cd4286661f6 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 25 Jun 2018 15:51:30 -0700 Subject: drbd: Fix drbd_request_prepare() discard handling Fix the test that verifies whether bio_op(bio) represents a discard or write zeroes operation. Compile-tested only. Cc: Philipp Reisner Cc: Lars Ellenberg Fixes: 7435e9018f91 ("drbd: zero-out partial unaligned discards on local backend") Signed-off-by: Bart Van Assche Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- drivers/block/drbd/drbd_req.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index a47e4987ee46..d146fedc38bb 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -1244,8 +1244,8 @@ drbd_request_prepare(struct drbd_device *device, struct bio *bio, unsigned long _drbd_start_io_acct(device, req); /* process discards always from our submitter thread */ - if ((bio_op(bio) & REQ_OP_WRITE_ZEROES) || - (bio_op(bio) & REQ_OP_DISCARD)) + if (bio_op(bio) == REQ_OP_WRITE_ZEROES || + bio_op(bio) == REQ_OP_DISCARD) goto queue_for_submitter_thread; if (rw == WRITE && req->private_bio && req->i.size -- cgit v1.2.3 From 9544bc5347207a68eb308cc8aaaed6c3a687cabd Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 29 Jun 2018 08:48:06 -0600 Subject: sg: remove ->sg_magic member This was introduced more than a decade ago when sg chaining was added, but we never really caught anything with it. The scatterlist entry size can be critical, since drivers allocate it, so remove the magic member. Recently it's been triggering allocation stalls and failures in NVMe. Tested-by: Jordan Glover Acked-by: Christoph Hellwig Signed-off-by: Jens Axboe --- drivers/gpu/drm/i915/i915_drv.h | 3 --- include/linux/scatterlist.h | 18 ------------------ lib/scatterlist.c | 6 ------ tools/virtio/linux/scatterlist.h | 18 ------------------ 4 files changed, 45 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 34c125e2d90c..9180f67746b4 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2238,9 +2238,6 @@ static inline struct scatterlist *____sg_next(struct scatterlist *sg) **/ static inline struct scatterlist *__sg_next(struct scatterlist *sg) { -#ifdef CONFIG_DEBUG_SG - BUG_ON(sg->sg_magic != SG_MAGIC); -#endif return sg_is_last(sg) ? NULL : ____sg_next(sg); } diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index 51f52020ad5f..093aa57120b0 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h @@ -9,9 +9,6 @@ #include struct scatterlist { -#ifdef CONFIG_DEBUG_SG - unsigned long sg_magic; -#endif unsigned long page_link; unsigned int offset; unsigned int length; @@ -64,7 +61,6 @@ struct sg_table { * */ -#define SG_MAGIC 0x87654321 #define SG_CHAIN 0x01UL #define SG_END 0x02UL @@ -98,7 +94,6 @@ static inline void sg_assign_page(struct scatterlist *sg, struct page *page) */ BUG_ON((unsigned long) page & (SG_CHAIN | SG_END)); #ifdef CONFIG_DEBUG_SG - BUG_ON(sg->sg_magic != SG_MAGIC); BUG_ON(sg_is_chain(sg)); #endif sg->page_link = page_link | (unsigned long) page; @@ -129,7 +124,6 @@ static inline void sg_set_page(struct scatterlist *sg, struct page *page, static inline struct page *sg_page(struct scatterlist *sg) { #ifdef CONFIG_DEBUG_SG - BUG_ON(sg->sg_magic != SG_MAGIC); BUG_ON(sg_is_chain(sg)); #endif return (struct page *)((sg)->page_link & ~(SG_CHAIN | SG_END)); @@ -195,9 +189,6 @@ static inline void sg_chain(struct scatterlist *prv, unsigned int prv_nents, **/ static inline void sg_mark_end(struct scatterlist *sg) { -#ifdef CONFIG_DEBUG_SG - BUG_ON(sg->sg_magic != SG_MAGIC); -#endif /* * Set termination bit, clear potential chain bit */ @@ -215,9 +206,6 @@ static inline void sg_mark_end(struct scatterlist *sg) **/ static inline void sg_unmark_end(struct scatterlist *sg) { -#ifdef CONFIG_DEBUG_SG - BUG_ON(sg->sg_magic != SG_MAGIC); -#endif sg->page_link &= ~SG_END; } @@ -260,12 +248,6 @@ static inline void *sg_virt(struct scatterlist *sg) static inline void sg_init_marker(struct scatterlist *sgl, unsigned int nents) { -#ifdef CONFIG_DEBUG_SG - unsigned int i; - - for (i = 0; i < nents; i++) - sgl[i].sg_magic = SG_MAGIC; -#endif sg_mark_end(&sgl[nents - 1]); } diff --git a/lib/scatterlist.c b/lib/scatterlist.c index 06dad7a072fd..d4ae67d6cd1e 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -24,9 +24,6 @@ **/ struct scatterlist *sg_next(struct scatterlist *sg) { -#ifdef CONFIG_DEBUG_SG - BUG_ON(sg->sg_magic != SG_MAGIC); -#endif if (sg_is_last(sg)) return NULL; @@ -111,10 +108,7 @@ struct scatterlist *sg_last(struct scatterlist *sgl, unsigned int nents) for_each_sg(sgl, sg, nents, i) ret = sg; -#ifdef CONFIG_DEBUG_SG - BUG_ON(sgl[0].sg_magic != SG_MAGIC); BUG_ON(!sg_is_last(ret)); -#endif return ret; } EXPORT_SYMBOL(sg_last); diff --git a/tools/virtio/linux/scatterlist.h b/tools/virtio/linux/scatterlist.h index 9a45f90e2d08..369ee308b668 100644 --- a/tools/virtio/linux/scatterlist.h +++ b/tools/virtio/linux/scatterlist.h @@ -36,7 +36,6 @@ static inline void sg_assign_page(struct scatterlist *sg, struct page *page) */ BUG_ON((unsigned long) page & 0x03); #ifdef CONFIG_DEBUG_SG - BUG_ON(sg->sg_magic != SG_MAGIC); BUG_ON(sg_is_chain(sg)); #endif sg->page_link = page_link | (unsigned long) page; @@ -67,7 +66,6 @@ static inline void sg_set_page(struct scatterlist *sg, struct page *page, static inline struct page *sg_page(struct scatterlist *sg) { #ifdef CONFIG_DEBUG_SG - BUG_ON(sg->sg_magic != SG_MAGIC); BUG_ON(sg_is_chain(sg)); #endif return (struct page *)((sg)->page_link & ~0x3); @@ -116,9 +114,6 @@ static inline void sg_chain(struct scatterlist *prv, unsigned int prv_nents, **/ static inline void sg_mark_end(struct scatterlist *sg) { -#ifdef CONFIG_DEBUG_SG - BUG_ON(sg->sg_magic != SG_MAGIC); -#endif /* * Set termination bit, clear potential chain bit */ @@ -136,17 +131,11 @@ static inline void sg_mark_end(struct scatterlist *sg) **/ static inline void sg_unmark_end(struct scatterlist *sg) { -#ifdef CONFIG_DEBUG_SG - BUG_ON(sg->sg_magic != SG_MAGIC); -#endif sg->page_link &= ~0x02; } static inline struct scatterlist *sg_next(struct scatterlist *sg) { -#ifdef CONFIG_DEBUG_SG - BUG_ON(sg->sg_magic != SG_MAGIC); -#endif if (sg_is_last(sg)) return NULL; @@ -160,13 +149,6 @@ static inline struct scatterlist *sg_next(struct scatterlist *sg) static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents) { memset(sgl, 0, sizeof(*sgl) * nents); -#ifdef CONFIG_DEBUG_SG - { - unsigned int i; - for (i = 0; i < nents; i++) - sgl[i].sg_magic = SG_MAGIC; - } -#endif sg_mark_end(&sgl[nents - 1]); } -- cgit v1.2.3 From efc6362c6f8c1e74b340e2611f1b35e7d557ce7b Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Thu, 29 Mar 2018 15:10:54 +0200 Subject: mtd: rawnand: atmel: add module param to avoid using dma On a sama5d31 with a Full-HD dual LVDS panel (132MHz pixel clock) NAND flash accesses have a tendency to cause display disturbances. Add a module param to disable DMA from the NAND controller, since that fixes the display problem for me. Signed-off-by: Peter Rosin Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/atmel/nand-controller.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index 12f6753d47ae..e686fe73159e 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -129,6 +129,11 @@ #define DEFAULT_TIMEOUT_MS 1000 #define MIN_DMA_LEN 128 +static bool atmel_nand_avoid_dma __read_mostly; + +MODULE_PARM_DESC(avoiddma, "Avoid using DMA"); +module_param_named(avoiddma, atmel_nand_avoid_dma, bool, 0400); + enum atmel_nand_rb_type { ATMEL_NAND_NO_RB, ATMEL_NAND_NATIVE_RB, @@ -1977,7 +1982,7 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc, return ret; } - if (nc->caps->has_dma) { + if (nc->caps->has_dma && !atmel_nand_avoid_dma) { dma_cap_mask_t mask; dma_cap_zero(mask); -- cgit v1.2.3 From ac8cf0b9e784acaf6fa20de89cb23b156834ca01 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 20 Jun 2018 09:44:43 +0200 Subject: mtd: rawnand: micron: Update ecc_stats.corrected Even if we can't update ecc_stats.corrected with an accurate value we should at least increase the number of bitflips so that MTD users can know that there was some bitflips. Just add chip->ecc.strength to mtd->ecc_stats.corrected which should account for the worst case situation. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_micron.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index 5ec4c90a637d..203faba0a9c1 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -137,18 +137,19 @@ micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, if (ret) goto out; - if (status & NAND_STATUS_FAIL) + if (status & NAND_STATUS_FAIL) { mtd->ecc_stats.failed++; - - /* - * The internal ECC doesn't tell us the number of bitflips - * that have been corrected, but tells us if it recommends to - * rewrite the block. If it's the case, then we pretend we had - * a number of bitflips equal to the ECC strength, which will - * hint the NAND core to rewrite the block. - */ - else if (status & NAND_STATUS_WRITE_RECOMMENDED) + } else if (status & NAND_STATUS_WRITE_RECOMMENDED) { + /* + * The internal ECC doesn't tell us the number of bitflips + * that have been corrected, but tells us if it recommends to + * rewrite the block. If it's the case, then we pretend we had + * a number of bitflips equal to the ECC strength, which will + * hint the NAND core to rewrite the block. + */ + mtd->ecc_stats.corrected += chip->ecc.strength; max_bitflips = chip->ecc.strength; + } ret = nand_read_data_op(chip, buf, mtd->writesize, false); if (!ret && oob_required) -- cgit v1.2.3 From 16c4fba03a45ac8c6048a96e02abc933e1354852 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sun, 24 Jun 2018 22:53:55 +0200 Subject: mtd: rawnand: hynix: fix decoding the OOB size on H27UCG8T2BTR The datasheet of the H27UCG8T2BTR states that this chip has a page size of "16,384 + 1,280(Spare) bytes". The description of the "4th Byte of Device Identifier Description" indicates that bits 6, 3 and 2 are encoding the "Redundant Area Size / 8KB", where 640 bytes is a value of 0x6 (110 in binary notation). hynix_nand_extract_oobsize decodes an OOB size of 640 bytes for this chip. Kernel boot log extract before this patch: nand: Could not find valid ONFI parameter page; aborting nand: device found, Manufacturer ID: 0xad, Chip ID: 0xde nand: Hynix NAND 8GiB 3,3V 8-bit nand: 8192 MiB, MLC, erase size: 4096 KiB, page size: 16384, OOB size: 640 However, based on the description in the datasheet we need to multiply the OOB size with 2, because it's "640 spare bytes per 8192 bytes page size" and this NAND chip has a page size of 16384 (= 2 * 8192). After this patch the kernel boot log reports: nand: Could not find valid ONFI parameter page; aborting nand: device found, Manufacturer ID: 0xad, Chip ID: 0xde nand: Hynix NAND 8GiB 3,3V 8-bit nand: 8192 MiB, MLC, erase size: 4096 KiB, page size: 16384, OOB size: 1280 Signed-off-by: Martin Blumenstingl Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_hynix.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_hynix.c b/drivers/mtd/nand/raw/nand_hynix.c index d542908a0ebb..8cbe77f447c7 100644 --- a/drivers/mtd/nand/raw/nand_hynix.c +++ b/drivers/mtd/nand/raw/nand_hynix.c @@ -473,6 +473,19 @@ static void hynix_nand_extract_oobsize(struct nand_chip *chip, WARN(1, "Invalid OOB size"); break; } + + /* + * The datasheet of H27UCG8T2BTR mentions that the "Redundant + * Area Size" is encoded "per 8KB" (page size). This chip uses + * a page size of 16KiB. The datasheet mentions an OOB size of + * 1.280 bytes, but the OOB size encoded in the ID bytes (using + * the existing logic above) is 640 bytes. + * Update the OOB size for this chip by taking the value + * determined above and scaling it to the actual page size (so + * the actual OOB size for this chip is: 640 * 16k / 8k). + */ + if (chip->id.data[1] == 0xde) + mtd->oobsize *= mtd->writesize / SZ_8K; } } -- cgit v1.2.3 From f308d7353d1f5c3ddedc784a367edc72fc51bbaa Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sun, 24 Jun 2018 23:27:22 +0200 Subject: mtd: rawnand: add Reed-Solomon error correction algorithm Add Reed-Solomon (RS) to the enumeration of ECC algorithms. Signed-off-by: Stefan Agner Reviewed-by: Boris Brezillon Acked-by: Rob Herring Signed-off-by: Miquel Raynal --- Documentation/devicetree/bindings/mtd/nand.txt | 2 +- drivers/mtd/nand/raw/nand_base.c | 1 + include/linux/mtd/rawnand.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/mtd/nand.txt b/Documentation/devicetree/bindings/mtd/nand.txt index 8bb11d809429..eaef8c657aa5 100644 --- a/Documentation/devicetree/bindings/mtd/nand.txt +++ b/Documentation/devicetree/bindings/mtd/nand.txt @@ -25,7 +25,7 @@ Optional NAND chip properties: Deprecated values: "soft_bch": use "soft" and nand-ecc-algo instead - nand-ecc-algo: string, algorithm of NAND ECC. - Supported values are: "hamming", "bch". + Valid values are: "hamming", "bch", "rs". - nand-bus-width : 8 or 16 bus width if not present 8 - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index b01d15ec4c56..d0af5347f89d 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5777,6 +5777,7 @@ static int of_get_nand_ecc_mode(struct device_node *np) static const char * const nand_ecc_algos[] = { [NAND_ECC_HAMMING] = "hamming", [NAND_ECC_BCH] = "bch", + [NAND_ECC_RS] = "rs", }; static int of_get_nand_ecc_algo(struct device_node *np) diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 3e8ec3b8a39c..2d9cb7acbc3d 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -121,6 +121,7 @@ enum nand_ecc_algo { NAND_ECC_UNKNOWN, NAND_ECC_HAMMING, NAND_ECC_BCH, + NAND_ECC_RS, }; /* -- cgit v1.2.3 From f922bd798bb94e0adcce26ddd811245443173268 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sun, 24 Jun 2018 23:27:23 +0200 Subject: mtd: rawnand: add an option to specify NAND chip as a boot device Allow to define a NAND chip as a boot device. This can be helpful for the selection of the ECC algorithm and strength in case the boot ROM supports only a subset of controller provided options. Signed-off-by: Stefan Agner Reviewed-by: Boris Brezillon Acked-by: Rob Herring Signed-off-by: Miquel Raynal --- Documentation/devicetree/bindings/mtd/nand.txt | 4 ++++ drivers/mtd/nand/raw/nand_base.c | 3 +++ include/linux/mtd/rawnand.h | 6 ++++++ 3 files changed, 13 insertions(+) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/mtd/nand.txt b/Documentation/devicetree/bindings/mtd/nand.txt index eaef8c657aa5..e949c778e983 100644 --- a/Documentation/devicetree/bindings/mtd/nand.txt +++ b/Documentation/devicetree/bindings/mtd/nand.txt @@ -43,6 +43,10 @@ Optional NAND chip properties: This is particularly useful when only the in-band area is used by the upper layers, and you want to make your NAND as reliable as possible. +- nand-is-boot-medium: Whether the NAND chip is a boot medium. Drivers might use + this information to select ECC algorithms supported by + the boot ROM or similar restrictions. + - nand-rb: shall contain the native Ready/Busy ids. The ECC strength and ECC step size properties define the correction capability diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index d0af5347f89d..1c633f80c3e1 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5859,6 +5859,9 @@ static int nand_dt_init(struct nand_chip *chip) if (of_get_nand_bus_width(dn) == 16) chip->options |= NAND_BUSWIDTH_16; + if (of_property_read_bool(dn, "nand-is-boot-medium")) + chip->options |= NAND_IS_BOOT_MEDIUM; + if (of_get_nand_on_flash_bbt(dn)) chip->bbt_options |= NAND_BBT_USE_FLASH; diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 2d9cb7acbc3d..80aeeca03f36 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -219,6 +219,12 @@ enum nand_ecc_algo { */ #define NAND_WAIT_TCCS 0x00200000 +/* + * Whether the NAND chip is a boot medium. Drivers might use this information + * to select ECC algorithms supported by the boot ROM or similar restrictions. + */ +#define NAND_IS_BOOT_MEDIUM 0x00400000 + /* Options set by nand scan */ /* Nand scan has allocated controller struct */ #define NAND_CONTROLLER_ALLOC 0x80000000 -- cgit v1.2.3 From d7d9f8ec77fe90472a649d1c2adba43a2e306eeb Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sun, 24 Jun 2018 23:27:25 +0200 Subject: mtd: rawnand: add NVIDIA Tegra NAND Flash controller driver Add support for the NAND flash controller found on NVIDIA Tegra 2 SoCs. This implementation does not make use of the command queue feature. Regular operations using ->exec_op() use PIO mode for data transfers. Raw, ECC and OOB read/writes make use of the DMA mode for data transfer. Signed-off-by: Lucas Stach Signed-off-by: Stefan Agner Reviewed-by: Dmitry Osipenko Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- MAINTAINERS | 7 + drivers/mtd/nand/raw/Kconfig | 10 + drivers/mtd/nand/raw/Makefile | 1 + drivers/mtd/nand/raw/tegra_nand.c | 1230 +++++++++++++++++++++++++++++++++++++ 4 files changed, 1248 insertions(+) create mode 100644 drivers/mtd/nand/raw/tegra_nand.c (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 07d1576fc766..63700cefbf56 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14054,6 +14054,13 @@ M: Laxman Dewangan S: Supported F: drivers/input/keyboard/tegra-kbc.c +TEGRA NAND DRIVER +M: Stefan Agner +M: Lucas Stach +S: Maintained +F: Documentation/devicetree/bindings/mtd/nvidia-tegra20-nand.txt +F: drivers/mtd/nand/raw/tegra_nand.c + TEGRA PWM DRIVER M: Thierry Reding S: Supported diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 6871ff0fd300..6074a946708a 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -530,4 +530,14 @@ config MTD_NAND_MTK Enables support for NAND controller on MTK SoCs. This controller is found on mt27xx, mt81xx, mt65xx SoCs. +config MTD_NAND_TEGRA + tristate "Support for NAND controller on NVIDIA Tegra" + depends on ARCH_TEGRA || COMPILE_TEST + help + Enables support for NAND flash controller on NVIDIA Tegra SoC. + The driver has been developed and tested on a Tegra 2 SoC. DMA + support, raw read/write page as well as HW ECC read/write page + is supported. Extra OOB bytes when using HW ECC are currently + not supported. + endif # MTD_NAND diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index 165b7ef9e9a1..d5a5f9832b88 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o +obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o nand-objs += nand_amd.o diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c new file mode 100644 index 000000000000..9f7de36be893 --- /dev/null +++ b/drivers/mtd/nand/raw/tegra_nand.c @@ -0,0 +1,1230 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Stefan Agner + * Copyright (C) 2014-2015 Lucas Stach + * Copyright (C) 2012 Avionic Design GmbH + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define COMMAND 0x00 +#define COMMAND_GO BIT(31) +#define COMMAND_CLE BIT(30) +#define COMMAND_ALE BIT(29) +#define COMMAND_PIO BIT(28) +#define COMMAND_TX BIT(27) +#define COMMAND_RX BIT(26) +#define COMMAND_SEC_CMD BIT(25) +#define COMMAND_AFT_DAT BIT(24) +#define COMMAND_TRANS_SIZE(size) ((((size) - 1) & 0xf) << 20) +#define COMMAND_A_VALID BIT(19) +#define COMMAND_B_VALID BIT(18) +#define COMMAND_RD_STATUS_CHK BIT(17) +#define COMMAND_RBSY_CHK BIT(16) +#define COMMAND_CE(x) BIT(8 + ((x) & 0x7)) +#define COMMAND_CLE_SIZE(size) ((((size) - 1) & 0x3) << 4) +#define COMMAND_ALE_SIZE(size) ((((size) - 1) & 0xf) << 0) + +#define STATUS 0x04 + +#define ISR 0x08 +#define ISR_CORRFAIL_ERR BIT(24) +#define ISR_UND BIT(7) +#define ISR_OVR BIT(6) +#define ISR_CMD_DONE BIT(5) +#define ISR_ECC_ERR BIT(4) + +#define IER 0x0c +#define IER_ERR_TRIG_VAL(x) (((x) & 0xf) << 16) +#define IER_UND BIT(7) +#define IER_OVR BIT(6) +#define IER_CMD_DONE BIT(5) +#define IER_ECC_ERR BIT(4) +#define IER_GIE BIT(0) + +#define CONFIG 0x10 +#define CONFIG_HW_ECC BIT(31) +#define CONFIG_ECC_SEL BIT(30) +#define CONFIG_ERR_COR BIT(29) +#define CONFIG_PIPE_EN BIT(28) +#define CONFIG_TVAL_4 (0 << 24) +#define CONFIG_TVAL_6 (1 << 24) +#define CONFIG_TVAL_8 (2 << 24) +#define CONFIG_SKIP_SPARE BIT(23) +#define CONFIG_BUS_WIDTH_16 BIT(21) +#define CONFIG_COM_BSY BIT(20) +#define CONFIG_PS_256 (0 << 16) +#define CONFIG_PS_512 (1 << 16) +#define CONFIG_PS_1024 (2 << 16) +#define CONFIG_PS_2048 (3 << 16) +#define CONFIG_PS_4096 (4 << 16) +#define CONFIG_SKIP_SPARE_SIZE_4 (0 << 14) +#define CONFIG_SKIP_SPARE_SIZE_8 (1 << 14) +#define CONFIG_SKIP_SPARE_SIZE_12 (2 << 14) +#define CONFIG_SKIP_SPARE_SIZE_16 (3 << 14) +#define CONFIG_TAG_BYTE_SIZE(x) ((x) & 0xff) + +#define TIMING_1 0x14 +#define TIMING_TRP_RESP(x) (((x) & 0xf) << 28) +#define TIMING_TWB(x) (((x) & 0xf) << 24) +#define TIMING_TCR_TAR_TRR(x) (((x) & 0xf) << 20) +#define TIMING_TWHR(x) (((x) & 0xf) << 16) +#define TIMING_TCS(x) (((x) & 0x3) << 14) +#define TIMING_TWH(x) (((x) & 0x3) << 12) +#define TIMING_TWP(x) (((x) & 0xf) << 8) +#define TIMING_TRH(x) (((x) & 0x3) << 4) +#define TIMING_TRP(x) (((x) & 0xf) << 0) + +#define RESP 0x18 + +#define TIMING_2 0x1c +#define TIMING_TADL(x) ((x) & 0xf) + +#define CMD_REG1 0x20 +#define CMD_REG2 0x24 +#define ADDR_REG1 0x28 +#define ADDR_REG2 0x2c + +#define DMA_MST_CTRL 0x30 +#define DMA_MST_CTRL_GO BIT(31) +#define DMA_MST_CTRL_IN (0 << 30) +#define DMA_MST_CTRL_OUT BIT(30) +#define DMA_MST_CTRL_PERF_EN BIT(29) +#define DMA_MST_CTRL_IE_DONE BIT(28) +#define DMA_MST_CTRL_REUSE BIT(27) +#define DMA_MST_CTRL_BURST_1 (2 << 24) +#define DMA_MST_CTRL_BURST_4 (3 << 24) +#define DMA_MST_CTRL_BURST_8 (4 << 24) +#define DMA_MST_CTRL_BURST_16 (5 << 24) +#define DMA_MST_CTRL_IS_DONE BIT(20) +#define DMA_MST_CTRL_EN_A BIT(2) +#define DMA_MST_CTRL_EN_B BIT(1) + +#define DMA_CFG_A 0x34 +#define DMA_CFG_B 0x38 + +#define FIFO_CTRL 0x3c +#define FIFO_CTRL_CLR_ALL BIT(3) + +#define DATA_PTR 0x40 +#define TAG_PTR 0x44 +#define ECC_PTR 0x48 + +#define DEC_STATUS 0x4c +#define DEC_STATUS_A_ECC_FAIL BIT(1) +#define DEC_STATUS_ERR_COUNT_MASK 0x00ff0000 +#define DEC_STATUS_ERR_COUNT_SHIFT 16 + +#define HWSTATUS_CMD 0x50 +#define HWSTATUS_MASK 0x54 +#define HWSTATUS_RDSTATUS_MASK(x) (((x) & 0xff) << 24) +#define HWSTATUS_RDSTATUS_VALUE(x) (((x) & 0xff) << 16) +#define HWSTATUS_RBSY_MASK(x) (((x) & 0xff) << 8) +#define HWSTATUS_RBSY_VALUE(x) (((x) & 0xff) << 0) + +#define BCH_CONFIG 0xcc +#define BCH_ENABLE BIT(0) +#define BCH_TVAL_4 (0 << 4) +#define BCH_TVAL_8 (1 << 4) +#define BCH_TVAL_14 (2 << 4) +#define BCH_TVAL_16 (3 << 4) + +#define DEC_STAT_RESULT 0xd0 +#define DEC_STAT_BUF 0xd4 +#define DEC_STAT_BUF_FAIL_SEC_FLAG_MASK 0xff000000 +#define DEC_STAT_BUF_FAIL_SEC_FLAG_SHIFT 24 +#define DEC_STAT_BUF_CORR_SEC_FLAG_MASK 0x00ff0000 +#define DEC_STAT_BUF_CORR_SEC_FLAG_SHIFT 16 +#define DEC_STAT_BUF_MAX_CORR_CNT_MASK 0x00001f00 +#define DEC_STAT_BUF_MAX_CORR_CNT_SHIFT 8 + +#define OFFSET(val, off) ((val) < (off) ? 0 : (val) - (off)) + +#define SKIP_SPARE_BYTES 4 +#define BITS_PER_STEP_RS 18 +#define BITS_PER_STEP_BCH 13 + +#define INT_MASK (IER_UND | IER_OVR | IER_CMD_DONE | IER_GIE) +#define HWSTATUS_CMD_DEFAULT NAND_STATUS_READY +#define HWSTATUS_MASK_DEFAULT (HWSTATUS_RDSTATUS_MASK(1) | \ + HWSTATUS_RDSTATUS_VALUE(0) | \ + HWSTATUS_RBSY_MASK(NAND_STATUS_READY) | \ + HWSTATUS_RBSY_VALUE(NAND_STATUS_READY)) + +struct tegra_nand_controller { + struct nand_hw_control controller; + struct device *dev; + void __iomem *regs; + int irq; + struct clk *clk; + struct completion command_complete; + struct completion dma_complete; + bool last_read_error; + int cur_cs; + struct nand_chip *chip; +}; + +struct tegra_nand_chip { + struct nand_chip chip; + struct gpio_desc *wp_gpio; + struct mtd_oob_region ecc; + u32 config; + u32 config_ecc; + u32 bch_config; + int cs[1]; +}; + +static inline struct tegra_nand_controller * + to_tegra_ctrl(struct nand_hw_control *hw_ctrl) +{ + return container_of(hw_ctrl, struct tegra_nand_controller, controller); +} + +static inline struct tegra_nand_chip *to_tegra_chip(struct nand_chip *chip) +{ + return container_of(chip, struct tegra_nand_chip, chip); +} + +static int tegra_nand_ooblayout_rs_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int bytes_per_step = DIV_ROUND_UP(BITS_PER_STEP_RS * chip->ecc.strength, + BITS_PER_BYTE); + + if (section > 0) + return -ERANGE; + + oobregion->offset = SKIP_SPARE_BYTES; + oobregion->length = round_up(bytes_per_step * chip->ecc.steps, 4); + + return 0; +} + +static int tegra_nand_ooblayout_no_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + return -ERANGE; +} + +static const struct mtd_ooblayout_ops tegra_nand_oob_rs_ops = { + .ecc = tegra_nand_ooblayout_rs_ecc, + .free = tegra_nand_ooblayout_no_free, +}; + +static int tegra_nand_ooblayout_bch_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int bytes_per_step = DIV_ROUND_UP(BITS_PER_STEP_BCH * chip->ecc.strength, + BITS_PER_BYTE); + + if (section > 0) + return -ERANGE; + + oobregion->offset = SKIP_SPARE_BYTES; + oobregion->length = round_up(bytes_per_step * chip->ecc.steps, 4); + + return 0; +} + +static const struct mtd_ooblayout_ops tegra_nand_oob_bch_ops = { + .ecc = tegra_nand_ooblayout_bch_ecc, + .free = tegra_nand_ooblayout_no_free, +}; + +static irqreturn_t tegra_nand_irq(int irq, void *data) +{ + struct tegra_nand_controller *ctrl = data; + u32 isr, dma; + + isr = readl_relaxed(ctrl->regs + ISR); + dma = readl_relaxed(ctrl->regs + DMA_MST_CTRL); + dev_dbg(ctrl->dev, "isr %08x\n", isr); + + if (!isr && !(dma & DMA_MST_CTRL_IS_DONE)) + return IRQ_NONE; + + /* + * The bit name is somewhat missleading: This is also set when + * HW ECC was successful. The data sheet states: + * Correctable OR Un-correctable errors occurred in the DMA transfer... + */ + if (isr & ISR_CORRFAIL_ERR) + ctrl->last_read_error = true; + + if (isr & ISR_CMD_DONE) + complete(&ctrl->command_complete); + + if (isr & ISR_UND) + dev_err(ctrl->dev, "FIFO underrun\n"); + + if (isr & ISR_OVR) + dev_err(ctrl->dev, "FIFO overrun\n"); + + /* handle DMA interrupts */ + if (dma & DMA_MST_CTRL_IS_DONE) { + writel_relaxed(dma, ctrl->regs + DMA_MST_CTRL); + complete(&ctrl->dma_complete); + } + + /* clear interrupts */ + writel_relaxed(isr, ctrl->regs + ISR); + + return IRQ_HANDLED; +} + +static const char * const tegra_nand_reg_names[] = { + "COMMAND", + "STATUS", + "ISR", + "IER", + "CONFIG", + "TIMING", + NULL, + "TIMING2", + "CMD_REG1", + "CMD_REG2", + "ADDR_REG1", + "ADDR_REG2", + "DMA_MST_CTRL", + "DMA_CFG_A", + "DMA_CFG_B", + "FIFO_CTRL", +}; + +static void tegra_nand_dump_reg(struct tegra_nand_controller *ctrl) +{ + u32 reg; + int i; + + dev_err(ctrl->dev, "Tegra NAND controller register dump\n"); + for (i = 0; i < ARRAY_SIZE(tegra_nand_reg_names); i++) { + const char *reg_name = tegra_nand_reg_names[i]; + + if (!reg_name) + continue; + + reg = readl_relaxed(ctrl->regs + (i * 4)); + dev_err(ctrl->dev, "%s: 0x%08x\n", reg_name, reg); + } +} + +static void tegra_nand_controller_abort(struct tegra_nand_controller *ctrl) +{ + u32 isr, dma; + + disable_irq(ctrl->irq); + + /* Abort current command/DMA operation */ + writel_relaxed(0, ctrl->regs + DMA_MST_CTRL); + writel_relaxed(0, ctrl->regs + COMMAND); + + /* clear interrupts */ + isr = readl_relaxed(ctrl->regs + ISR); + writel_relaxed(isr, ctrl->regs + ISR); + dma = readl_relaxed(ctrl->regs + DMA_MST_CTRL); + writel_relaxed(dma, ctrl->regs + DMA_MST_CTRL); + + reinit_completion(&ctrl->command_complete); + reinit_completion(&ctrl->dma_complete); + + enable_irq(ctrl->irq); +} + +static int tegra_nand_cmd(struct nand_chip *chip, + const struct nand_subop *subop) +{ + const struct nand_op_instr *instr; + const struct nand_op_instr *instr_data_in = NULL; + struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); + unsigned int op_id, size = 0, offset = 0; + bool first_cmd = true; + u32 reg, cmd = 0; + int ret; + + for (op_id = 0; op_id < subop->ninstrs; op_id++) { + unsigned int naddrs, i; + const u8 *addrs; + u32 addr1 = 0, addr2 = 0; + + instr = &subop->instrs[op_id]; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + if (first_cmd) { + cmd |= COMMAND_CLE; + writel_relaxed(instr->ctx.cmd.opcode, + ctrl->regs + CMD_REG1); + } else { + cmd |= COMMAND_SEC_CMD; + writel_relaxed(instr->ctx.cmd.opcode, + ctrl->regs + CMD_REG2); + } + first_cmd = false; + break; + + case NAND_OP_ADDR_INSTR: + offset = nand_subop_get_addr_start_off(subop, op_id); + naddrs = nand_subop_get_num_addr_cyc(subop, op_id); + addrs = &instr->ctx.addr.addrs[offset]; + + cmd |= COMMAND_ALE | COMMAND_ALE_SIZE(naddrs); + for (i = 0; i < min_t(unsigned int, 4, naddrs); i++) + addr1 |= *addrs++ << (BITS_PER_BYTE * i); + naddrs -= i; + for (i = 0; i < min_t(unsigned int, 4, naddrs); i++) + addr2 |= *addrs++ << (BITS_PER_BYTE * i); + + writel_relaxed(addr1, ctrl->regs + ADDR_REG1); + writel_relaxed(addr2, ctrl->regs + ADDR_REG2); + break; + + case NAND_OP_DATA_IN_INSTR: + size = nand_subop_get_data_len(subop, op_id); + offset = nand_subop_get_data_start_off(subop, op_id); + + cmd |= COMMAND_TRANS_SIZE(size) | COMMAND_PIO | + COMMAND_RX | COMMAND_A_VALID; + + instr_data_in = instr; + break; + + case NAND_OP_DATA_OUT_INSTR: + size = nand_subop_get_data_len(subop, op_id); + offset = nand_subop_get_data_start_off(subop, op_id); + + cmd |= COMMAND_TRANS_SIZE(size) | COMMAND_PIO | + COMMAND_TX | COMMAND_A_VALID; + memcpy(®, instr->ctx.data.buf.out + offset, size); + + writel_relaxed(reg, ctrl->regs + RESP); + break; + + case NAND_OP_WAITRDY_INSTR: + cmd |= COMMAND_RBSY_CHK; + break; + } + } + + cmd |= COMMAND_GO | COMMAND_CE(ctrl->cur_cs); + writel_relaxed(cmd, ctrl->regs + COMMAND); + ret = wait_for_completion_timeout(&ctrl->command_complete, + msecs_to_jiffies(500)); + if (!ret) { + dev_err(ctrl->dev, "COMMAND timeout\n"); + tegra_nand_dump_reg(ctrl); + tegra_nand_controller_abort(ctrl); + return -ETIMEDOUT; + } + + if (instr_data_in) { + reg = readl_relaxed(ctrl->regs + RESP); + memcpy(instr_data_in->ctx.data.buf.in + offset, ®, size); + } + + return 0; +} + +static const struct nand_op_parser tegra_nand_op_parser = NAND_OP_PARSER( + NAND_OP_PARSER_PATTERN(tegra_nand_cmd, + NAND_OP_PARSER_PAT_CMD_ELEM(true), + NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8), + NAND_OP_PARSER_PAT_CMD_ELEM(true), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)), + NAND_OP_PARSER_PATTERN(tegra_nand_cmd, + NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, 4)), + NAND_OP_PARSER_PATTERN(tegra_nand_cmd, + NAND_OP_PARSER_PAT_CMD_ELEM(true), + NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8), + NAND_OP_PARSER_PAT_CMD_ELEM(true), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(true), + NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 4)), + ); + +static int tegra_nand_exec_op(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only) +{ + return nand_op_parser_exec_op(chip, &tegra_nand_op_parser, op, + check_only); +} + +static void tegra_nand_select_chip(struct mtd_info *mtd, int die_nr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct tegra_nand_chip *nand = to_tegra_chip(chip); + struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); + + if (die_nr < 0 || die_nr > 1) { + ctrl->cur_cs = -1; + return; + } + + ctrl->cur_cs = nand->cs[die_nr]; +} + +static void tegra_nand_hw_ecc(struct tegra_nand_controller *ctrl, + struct nand_chip *chip, bool enable) +{ + struct tegra_nand_chip *nand = to_tegra_chip(chip); + + if (chip->ecc.algo == NAND_ECC_BCH && enable) + writel_relaxed(nand->bch_config, ctrl->regs + BCH_CONFIG); + else + writel_relaxed(0, ctrl->regs + BCH_CONFIG); + + if (enable) + writel_relaxed(nand->config_ecc, ctrl->regs + CONFIG); + else + writel_relaxed(nand->config, ctrl->regs + CONFIG); +} + +static int tegra_nand_page_xfer(struct mtd_info *mtd, struct nand_chip *chip, + void *buf, void *oob_buf, int oob_len, int page, + bool read) +{ + struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); + enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + dma_addr_t dma_addr = 0, dma_addr_oob = 0; + u32 addr1, cmd, dma_ctrl; + int ret; + + if (read) { + writel_relaxed(NAND_CMD_READ0, ctrl->regs + CMD_REG1); + writel_relaxed(NAND_CMD_READSTART, ctrl->regs + CMD_REG2); + } else { + writel_relaxed(NAND_CMD_SEQIN, ctrl->regs + CMD_REG1); + writel_relaxed(NAND_CMD_PAGEPROG, ctrl->regs + CMD_REG2); + } + cmd = COMMAND_CLE | COMMAND_SEC_CMD; + + /* Lower 16-bits are column, by default 0 */ + addr1 = page << 16; + + if (!buf) + addr1 |= mtd->writesize; + writel_relaxed(addr1, ctrl->regs + ADDR_REG1); + + if (chip->options & NAND_ROW_ADDR_3) { + writel_relaxed(page >> 16, ctrl->regs + ADDR_REG2); + cmd |= COMMAND_ALE | COMMAND_ALE_SIZE(5); + } else { + cmd |= COMMAND_ALE | COMMAND_ALE_SIZE(4); + } + + if (buf) { + dma_addr = dma_map_single(ctrl->dev, buf, mtd->writesize, dir); + ret = dma_mapping_error(ctrl->dev, dma_addr); + if (ret) { + dev_err(ctrl->dev, "dma mapping error\n"); + return -EINVAL; + } + + writel_relaxed(mtd->writesize - 1, ctrl->regs + DMA_CFG_A); + writel_relaxed(dma_addr, ctrl->regs + DATA_PTR); + } + + if (oob_buf) { + dma_addr_oob = dma_map_single(ctrl->dev, oob_buf, mtd->oobsize, + dir); + ret = dma_mapping_error(ctrl->dev, dma_addr_oob); + if (ret) { + dev_err(ctrl->dev, "dma mapping error\n"); + ret = -EINVAL; + goto err_unmap_dma_page; + } + + writel_relaxed(oob_len - 1, ctrl->regs + DMA_CFG_B); + writel_relaxed(dma_addr_oob, ctrl->regs + TAG_PTR); + } + + dma_ctrl = DMA_MST_CTRL_GO | DMA_MST_CTRL_PERF_EN | + DMA_MST_CTRL_IE_DONE | DMA_MST_CTRL_IS_DONE | + DMA_MST_CTRL_BURST_16; + + if (buf) + dma_ctrl |= DMA_MST_CTRL_EN_A; + if (oob_buf) + dma_ctrl |= DMA_MST_CTRL_EN_B; + + if (read) + dma_ctrl |= DMA_MST_CTRL_IN | DMA_MST_CTRL_REUSE; + else + dma_ctrl |= DMA_MST_CTRL_OUT; + + writel_relaxed(dma_ctrl, ctrl->regs + DMA_MST_CTRL); + + cmd |= COMMAND_GO | COMMAND_RBSY_CHK | COMMAND_TRANS_SIZE(9) | + COMMAND_CE(ctrl->cur_cs); + + if (buf) + cmd |= COMMAND_A_VALID; + if (oob_buf) + cmd |= COMMAND_B_VALID; + + if (read) + cmd |= COMMAND_RX; + else + cmd |= COMMAND_TX | COMMAND_AFT_DAT; + + writel_relaxed(cmd, ctrl->regs + COMMAND); + + ret = wait_for_completion_timeout(&ctrl->command_complete, + msecs_to_jiffies(500)); + if (!ret) { + dev_err(ctrl->dev, "COMMAND timeout\n"); + tegra_nand_dump_reg(ctrl); + tegra_nand_controller_abort(ctrl); + ret = -ETIMEDOUT; + goto err_unmap_dma; + } + + ret = wait_for_completion_timeout(&ctrl->dma_complete, + msecs_to_jiffies(500)); + if (!ret) { + dev_err(ctrl->dev, "DMA timeout\n"); + tegra_nand_dump_reg(ctrl); + tegra_nand_controller_abort(ctrl); + ret = -ETIMEDOUT; + goto err_unmap_dma; + } + ret = 0; + +err_unmap_dma: + if (oob_buf) + dma_unmap_single(ctrl->dev, dma_addr_oob, mtd->oobsize, dir); +err_unmap_dma_page: + if (buf) + dma_unmap_single(ctrl->dev, dma_addr, mtd->writesize, dir); + + return ret; +} + +static int tegra_nand_read_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, + int oob_required, int page) +{ + void *oob_buf = oob_required ? chip->oob_poi : NULL; + + return tegra_nand_page_xfer(mtd, chip, buf, oob_buf, + mtd->oobsize, page, true); +} + +static int tegra_nand_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const u8 *buf, + int oob_required, int page) +{ + void *oob_buf = oob_required ? chip->oob_poi : NULL; + + return tegra_nand_page_xfer(mtd, chip, (void *)buf, oob_buf, + mtd->oobsize, page, false); +} + +static int tegra_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return tegra_nand_page_xfer(mtd, chip, NULL, chip->oob_poi, + mtd->oobsize, page, true); +} + +static int tegra_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return tegra_nand_page_xfer(mtd, chip, NULL, chip->oob_poi, + mtd->oobsize, page, false); +} + +static int tegra_nand_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, + int oob_required, int page) +{ + struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); + struct tegra_nand_chip *nand = to_tegra_chip(chip); + void *oob_buf = oob_required ? chip->oob_poi : NULL; + u32 dec_stat, max_corr_cnt; + unsigned long fail_sec_flag; + int ret; + + tegra_nand_hw_ecc(ctrl, chip, true); + ret = tegra_nand_page_xfer(mtd, chip, buf, oob_buf, 0, page, true); + tegra_nand_hw_ecc(ctrl, chip, false); + if (ret) + return ret; + + /* No correctable or un-correctable errors, page must have 0 bitflips */ + if (!ctrl->last_read_error) + return 0; + + /* + * Correctable or un-correctable errors occurred. Use DEC_STAT_BUF + * which contains information for all ECC selections. + * + * Note that since we do not use Command Queues DEC_RESULT does not + * state the number of pages we can read from the DEC_STAT_BUF. But + * since CORRFAIL_ERR did occur during page read we do have a valid + * result in DEC_STAT_BUF. + */ + ctrl->last_read_error = false; + dec_stat = readl_relaxed(ctrl->regs + DEC_STAT_BUF); + + fail_sec_flag = (dec_stat & DEC_STAT_BUF_FAIL_SEC_FLAG_MASK) >> + DEC_STAT_BUF_FAIL_SEC_FLAG_SHIFT; + + max_corr_cnt = (dec_stat & DEC_STAT_BUF_MAX_CORR_CNT_MASK) >> + DEC_STAT_BUF_MAX_CORR_CNT_SHIFT; + + if (fail_sec_flag) { + int bit, max_bitflips = 0; + + /* + * Since we do not support subpage writes, a complete page + * is either written or not. We can take a shortcut here by + * checking wheather any of the sector has been successful + * read. If at least one sectors has been read successfully, + * the page must have been a written previously. It cannot + * be an erased page. + * + * E.g. controller might return fail_sec_flag with 0x4, which + * would mean only the third sector failed to correct. The + * page must have been written and the third sector is really + * not correctable anymore. + */ + if (fail_sec_flag ^ GENMASK(chip->ecc.steps - 1, 0)) { + mtd->ecc_stats.failed += hweight8(fail_sec_flag); + return max_corr_cnt; + } + + /* + * All sectors failed to correct, but the ECC isn't smart + * enough to figure out if a page is really just erased. + * Read OOB data and check whether data/OOB is completely + * erased or if error correction just failed for all sub- + * pages. + */ + ret = tegra_nand_read_oob(mtd, chip, page); + if (ret < 0) + return ret; + + for_each_set_bit(bit, &fail_sec_flag, chip->ecc.steps) { + u8 *data = buf + (chip->ecc.size * bit); + u8 *oob = chip->oob_poi + nand->ecc.offset + + (chip->ecc.bytes * bit); + + ret = nand_check_erased_ecc_chunk(data, chip->ecc.size, + oob, chip->ecc.bytes, + NULL, 0, + chip->ecc.strength); + if (ret < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += ret; + max_bitflips = max(ret, max_bitflips); + } + } + + return max_t(unsigned int, max_corr_cnt, max_bitflips); + } else { + int corr_sec_flag; + + corr_sec_flag = (dec_stat & DEC_STAT_BUF_CORR_SEC_FLAG_MASK) >> + DEC_STAT_BUF_CORR_SEC_FLAG_SHIFT; + + /* + * The value returned in the register is the maximum of + * bitflips encountered in any of the ECC regions. As there is + * no way to get the number of bitflips in a specific regions + * we are not able to deliver correct stats but instead + * overestimate the number of corrected bitflips by assuming + * that all regions where errors have been corrected + * encountered the maximum number of bitflips. + */ + mtd->ecc_stats.corrected += max_corr_cnt * hweight8(corr_sec_flag); + + return max_corr_cnt; + } +} + +static int tegra_nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const u8 *buf, + int oob_required, int page) +{ + struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); + void *oob_buf = oob_required ? chip->oob_poi : NULL; + int ret; + + tegra_nand_hw_ecc(ctrl, chip, true); + ret = tegra_nand_page_xfer(mtd, chip, (void *)buf, oob_buf, + 0, page, false); + tegra_nand_hw_ecc(ctrl, chip, false); + + return ret; +} + +static void tegra_nand_setup_timing(struct tegra_nand_controller *ctrl, + const struct nand_sdr_timings *timings) +{ + /* + * The period (and all other timings in this function) is in ps, + * so need to take care here to avoid integer overflows. + */ + unsigned int rate = clk_get_rate(ctrl->clk) / 1000000; + unsigned int period = DIV_ROUND_UP(1000000, rate); + u32 val, reg = 0; + + val = DIV_ROUND_UP(max3(timings->tAR_min, timings->tRR_min, + timings->tRC_min), period); + reg |= TIMING_TCR_TAR_TRR(OFFSET(val, 3)); + + val = DIV_ROUND_UP(max(max(timings->tCS_min, timings->tCH_min), + max(timings->tALS_min, timings->tALH_min)), + period); + reg |= TIMING_TCS(OFFSET(val, 2)); + + val = DIV_ROUND_UP(max(timings->tRP_min, timings->tREA_max) + 6000, + period); + reg |= TIMING_TRP(OFFSET(val, 1)) | TIMING_TRP_RESP(OFFSET(val, 1)); + + reg |= TIMING_TWB(OFFSET(DIV_ROUND_UP(timings->tWB_max, period), 1)); + reg |= TIMING_TWHR(OFFSET(DIV_ROUND_UP(timings->tWHR_min, period), 1)); + reg |= TIMING_TWH(OFFSET(DIV_ROUND_UP(timings->tWH_min, period), 1)); + reg |= TIMING_TWP(OFFSET(DIV_ROUND_UP(timings->tWP_min, period), 1)); + reg |= TIMING_TRH(OFFSET(DIV_ROUND_UP(timings->tREH_min, period), 1)); + + writel_relaxed(reg, ctrl->regs + TIMING_1); + + val = DIV_ROUND_UP(timings->tADL_min, period); + reg = TIMING_TADL(OFFSET(val, 3)); + + writel_relaxed(reg, ctrl->regs + TIMING_2); +} + +static int tegra_nand_setup_data_interface(struct mtd_info *mtd, int csline, + const struct nand_data_interface *conf) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); + const struct nand_sdr_timings *timings; + + timings = nand_get_sdr_timings(conf); + if (IS_ERR(timings)) + return PTR_ERR(timings); + + if (csline == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + tegra_nand_setup_timing(ctrl, timings); + + return 0; +} + +static const int rs_strength_bootable[] = { 4 }; +static const int rs_strength[] = { 4, 6, 8 }; +static const int bch_strength_bootable[] = { 8, 16 }; +static const int bch_strength[] = { 4, 8, 14, 16 }; + +static int tegra_nand_get_strength(struct nand_chip *chip, const int *strength, + int strength_len, int bits_per_step, + int oobsize) +{ + bool maximize = chip->ecc.options & NAND_ECC_MAXIMIZE; + int i; + + /* + * Loop through available strengths. Backwards in case we try to + * maximize the BCH strength. + */ + for (i = 0; i < strength_len; i++) { + int strength_sel, bytes_per_step, bytes_per_page; + + if (maximize) { + strength_sel = strength[strength_len - i - 1]; + } else { + strength_sel = strength[i]; + + if (strength_sel < chip->ecc_strength_ds) + continue; + } + + bytes_per_step = DIV_ROUND_UP(bits_per_step * strength_sel, + BITS_PER_BYTE); + bytes_per_page = round_up(bytes_per_step * chip->ecc.steps, 4); + + /* Check whether strength fits OOB */ + if (bytes_per_page < (oobsize - SKIP_SPARE_BYTES)) + return strength_sel; + } + + return -EINVAL; +} + +static int tegra_nand_select_strength(struct nand_chip *chip, int oobsize) +{ + const int *strength; + int strength_len, bits_per_step; + + switch (chip->ecc.algo) { + case NAND_ECC_RS: + bits_per_step = BITS_PER_STEP_RS; + if (chip->options & NAND_IS_BOOT_MEDIUM) { + strength = rs_strength_bootable; + strength_len = ARRAY_SIZE(rs_strength_bootable); + } else { + strength = rs_strength; + strength_len = ARRAY_SIZE(rs_strength); + } + break; + case NAND_ECC_BCH: + bits_per_step = BITS_PER_STEP_BCH; + if (chip->options & NAND_IS_BOOT_MEDIUM) { + strength = bch_strength_bootable; + strength_len = ARRAY_SIZE(bch_strength_bootable); + } else { + strength = bch_strength; + strength_len = ARRAY_SIZE(bch_strength); + } + break; + default: + return -EINVAL; + } + + return tegra_nand_get_strength(chip, strength, strength_len, + bits_per_step, oobsize); +} + +static int tegra_nand_chips_init(struct device *dev, + struct tegra_nand_controller *ctrl) +{ + struct device_node *np = dev->of_node; + struct device_node *np_nand; + int nsels, nchips = of_get_child_count(np); + struct tegra_nand_chip *nand; + struct mtd_info *mtd; + struct nand_chip *chip; + int bits_per_step; + int ret; + u32 cs; + + if (nchips != 1) { + dev_err(dev, "Currently only one NAND chip supported\n"); + return -EINVAL; + } + + np_nand = of_get_next_child(np, NULL); + + nsels = of_property_count_elems_of_size(np_nand, "reg", sizeof(u32)); + if (nsels != 1) { + dev_err(dev, "Missing/invalid reg property\n"); + return -EINVAL; + } + + /* Retrieve CS id, currently only single die NAND supported */ + ret = of_property_read_u32(np_nand, "reg", &cs); + if (ret) { + dev_err(dev, "could not retrieve reg property: %d\n", ret); + return ret; + } + + nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL); + if (!nand) + return -ENOMEM; + + nand->cs[0] = cs; + + nand->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_LOW); + + if (IS_ERR(nand->wp_gpio)) { + ret = PTR_ERR(nand->wp_gpio); + dev_err(dev, "Failed to request WP GPIO: %d\n", ret); + return ret; + } + + chip = &nand->chip; + chip->controller = &ctrl->controller; + + mtd = nand_to_mtd(chip); + + mtd->dev.parent = dev; + mtd->owner = THIS_MODULE; + + nand_set_flash_node(chip, np_nand); + + if (!mtd->name) + mtd->name = "tegra_nand"; + + chip->options = NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER; + chip->exec_op = tegra_nand_exec_op; + chip->select_chip = tegra_nand_select_chip; + chip->setup_data_interface = tegra_nand_setup_data_interface; + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; + + if (chip->bbt_options & NAND_BBT_USE_FLASH) + chip->bbt_options |= NAND_BBT_NO_OOB; + + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.size = 512; + chip->ecc.steps = mtd->writesize / chip->ecc.size; + if (chip->ecc_step_ds != 512) { + dev_err(dev, "Unsupported step size %d\n", chip->ecc_step_ds); + return -EINVAL; + } + + chip->ecc.read_page = tegra_nand_read_page_hwecc; + chip->ecc.write_page = tegra_nand_write_page_hwecc; + chip->ecc.read_page_raw = tegra_nand_read_page_raw; + chip->ecc.write_page_raw = tegra_nand_write_page_raw; + chip->ecc.read_oob = tegra_nand_read_oob; + chip->ecc.write_oob = tegra_nand_write_oob; + + if (chip->options & NAND_BUSWIDTH_16) + nand->config |= CONFIG_BUS_WIDTH_16; + + if (chip->ecc.algo == NAND_ECC_UNKNOWN) { + if (mtd->writesize < 2048) + chip->ecc.algo = NAND_ECC_RS; + else + chip->ecc.algo = NAND_ECC_BCH; + } + + if (chip->ecc.algo == NAND_ECC_BCH && mtd->writesize < 2048) { + dev_err(dev, "BCH supports 2K or 4K page size only\n"); + return -EINVAL; + } + + if (!chip->ecc.strength) { + ret = tegra_nand_select_strength(chip, mtd->oobsize); + if (ret < 0) { + dev_err(dev, "No valid strength found, minimum %d\n", + chip->ecc_strength_ds); + return ret; + } + + chip->ecc.strength = ret; + } + + nand->config_ecc = CONFIG_PIPE_EN | CONFIG_SKIP_SPARE | + CONFIG_SKIP_SPARE_SIZE_4; + + switch (chip->ecc.algo) { + case NAND_ECC_RS: + bits_per_step = BITS_PER_STEP_RS * chip->ecc.strength; + mtd_set_ooblayout(mtd, &tegra_nand_oob_rs_ops); + nand->config_ecc |= CONFIG_HW_ECC | CONFIG_ECC_SEL | + CONFIG_ERR_COR; + switch (chip->ecc.strength) { + case 4: + nand->config_ecc |= CONFIG_TVAL_4; + break; + case 6: + nand->config_ecc |= CONFIG_TVAL_6; + break; + case 8: + nand->config_ecc |= CONFIG_TVAL_8; + break; + default: + dev_err(dev, "ECC strength %d not supported\n", + chip->ecc.strength); + return -EINVAL; + } + break; + case NAND_ECC_BCH: + bits_per_step = BITS_PER_STEP_BCH * chip->ecc.strength; + mtd_set_ooblayout(mtd, &tegra_nand_oob_bch_ops); + nand->bch_config = BCH_ENABLE; + switch (chip->ecc.strength) { + case 4: + nand->bch_config |= BCH_TVAL_4; + break; + case 8: + nand->bch_config |= BCH_TVAL_8; + break; + case 14: + nand->bch_config |= BCH_TVAL_14; + break; + case 16: + nand->bch_config |= BCH_TVAL_16; + break; + default: + dev_err(dev, "ECC strength %d not supported\n", + chip->ecc.strength); + return -EINVAL; + } + break; + default: + dev_err(dev, "ECC algorithm not supported\n"); + return -EINVAL; + } + + dev_info(dev, "Using %s with strength %d per 512 byte step\n", + chip->ecc.algo == NAND_ECC_BCH ? "BCH" : "RS", + chip->ecc.strength); + + chip->ecc.bytes = DIV_ROUND_UP(bits_per_step, BITS_PER_BYTE); + + switch (mtd->writesize) { + case 256: + nand->config |= CONFIG_PS_256; + break; + case 512: + nand->config |= CONFIG_PS_512; + break; + case 1024: + nand->config |= CONFIG_PS_1024; + break; + case 2048: + nand->config |= CONFIG_PS_2048; + break; + case 4096: + nand->config |= CONFIG_PS_4096; + break; + default: + dev_err(dev, "Unsupported writesize %d\n", mtd->writesize); + return -ENODEV; + } + + /* Store complete configuration for HW ECC in config_ecc */ + nand->config_ecc |= nand->config; + + /* Non-HW ECC read/writes complete OOB */ + nand->config |= CONFIG_TAG_BYTE_SIZE(mtd->oobsize - 1); + writel_relaxed(nand->config, ctrl->regs + CONFIG); + + ret = nand_scan_tail(mtd); + if (ret) + return ret; + + mtd_ooblayout_ecc(mtd, 0, &nand->ecc); + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + dev_err(dev, "Failed to register mtd device: %d\n", ret); + nand_cleanup(chip); + return ret; + } + + ctrl->chip = chip; + + return 0; +} + +static int tegra_nand_probe(struct platform_device *pdev) +{ + struct reset_control *rst; + struct tegra_nand_controller *ctrl; + struct resource *res; + int err = 0; + + ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + ctrl->dev = &pdev->dev; + nand_hw_control_init(&ctrl->controller); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctrl->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ctrl->regs)) + return PTR_ERR(ctrl->regs); + + rst = devm_reset_control_get(&pdev->dev, "nand"); + if (IS_ERR(rst)) + return PTR_ERR(rst); + + ctrl->clk = devm_clk_get(&pdev->dev, "nand"); + if (IS_ERR(ctrl->clk)) + return PTR_ERR(ctrl->clk); + + err = clk_prepare_enable(ctrl->clk); + if (err) + return err; + + err = reset_control_reset(rst); + if (err) { + dev_err(ctrl->dev, "Failed to reset HW: %d\n", err); + goto err_disable_clk; + } + + writel_relaxed(HWSTATUS_CMD_DEFAULT, ctrl->regs + HWSTATUS_CMD); + writel_relaxed(HWSTATUS_MASK_DEFAULT, ctrl->regs + HWSTATUS_MASK); + writel_relaxed(INT_MASK, ctrl->regs + IER); + + init_completion(&ctrl->command_complete); + init_completion(&ctrl->dma_complete); + + ctrl->irq = platform_get_irq(pdev, 0); + err = devm_request_irq(&pdev->dev, ctrl->irq, tegra_nand_irq, 0, + dev_name(&pdev->dev), ctrl); + if (err) { + dev_err(ctrl->dev, "Failed to get IRQ: %d\n", err); + goto err_disable_clk; + } + + writel_relaxed(DMA_MST_CTRL_IS_DONE, ctrl->regs + DMA_MST_CTRL); + + err = tegra_nand_chips_init(ctrl->dev, ctrl); + if (err) + goto err_disable_clk; + + platform_set_drvdata(pdev, ctrl); + + return 0; + +err_disable_clk: + clk_disable_unprepare(ctrl->clk); + return err; +} + +static int tegra_nand_remove(struct platform_device *pdev) +{ + struct tegra_nand_controller *ctrl = platform_get_drvdata(pdev); + struct nand_chip *chip = ctrl->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + ret = mtd_device_unregister(mtd); + if (ret) + return ret; + + nand_cleanup(chip); + + clk_disable_unprepare(ctrl->clk); + + return 0; +} + +static const struct of_device_id tegra_nand_of_match[] = { + { .compatible = "nvidia,tegra20-nand" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tegra_nand_of_match); + +static struct platform_driver tegra_nand_driver = { + .driver = { + .name = "tegra-nand", + .of_match_table = tegra_nand_of_match, + }, + .probe = tegra_nand_probe, + .remove = tegra_nand_remove, +}; +module_platform_driver(tegra_nand_driver); + +MODULE_DESCRIPTION("NVIDIA Tegra NAND driver"); +MODULE_AUTHOR("Thierry Reding "); +MODULE_AUTHOR("Lucas Stach "); +MODULE_AUTHOR("Stefan Agner "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From ed6d0285f81cbf27d8294b47ac0bc8f28041b186 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Mon, 25 Jun 2018 10:44:43 +1200 Subject: mtd: rawnand: marvell: Handle on-die ECC >From the controllers point of view this is the same as no or software only ECC. Signed-off-by: Chris Packham Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/marvell_nand.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index ebb1d141b900..ba6889bbe802 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2157,6 +2157,7 @@ static int marvell_nand_ecc_init(struct mtd_info *mtd, break; case NAND_ECC_NONE: case NAND_ECC_SOFT: + case NAND_ECC_ON_DIE: if (!nfc->caps->is_nfcv2 && mtd->writesize != SZ_512 && mtd->writesize != SZ_2K) { dev_err(nfc->dev, "NFCv1 cannot write %d bytes pages\n", -- cgit v1.2.3 From 00ce4e039ad5bded462931606c3063ff691964b7 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Mon, 25 Jun 2018 10:44:44 +1200 Subject: mtd: rawnand: add manufacturer fixup for ONFI parameter page This is called after the ONFI parameter page checksum is verified and allows us to override the contents of the parameter page. Suggested-by: Boris Brezillon Signed-off-by: Chris Packham Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_base.c | 4 ++++ include/linux/mtd/rawnand.h | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 1c633f80c3e1..f362c09a71d7 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5168,6 +5168,10 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) } } + if (chip->manufacturer.desc && chip->manufacturer.desc->ops && + chip->manufacturer.desc->ops->fixup_onfi_param_page) + chip->manufacturer.desc->ops->fixup_onfi_param_page(chip, p); + /* Check version */ val = le16_to_cpu(p->revision); if (val & (1 << 5)) diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 80aeeca03f36..dc4538b58b84 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -785,11 +785,15 @@ nand_get_sdr_timings(const struct nand_data_interface *conf) * implementation) if any. * @cleanup: the ->init() function may have allocated resources, ->cleanup() * is here to let vendor specific code release those resources. + * @fixup_onfi_param_page: apply vendor specific fixups to the ONFI parameter + * page. This is called after the checksum is verified. */ struct nand_manufacturer_ops { void (*detect)(struct nand_chip *chip); int (*init)(struct nand_chip *chip); void (*cleanup)(struct nand_chip *chip); + void (*fixup_onfi_param_page)(struct nand_chip *chip, + struct nand_onfi_params *p); }; /** -- cgit v1.2.3 From 872b71ff084ab125c68073d9e69acfd7095f2015 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Mon, 25 Jun 2018 10:44:45 +1200 Subject: mtd: rawnand: add defines for ONFI version bits Add defines for the ONFI version bits and use them in nand_flash_detect_onfi(). Signed-off-by: Chris Packham Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_base.c | 10 +++++----- include/linux/mtd/rawnand.h | 11 +++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index f362c09a71d7..dbf6e80e9ab5 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5174,15 +5174,15 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) /* Check version */ val = le16_to_cpu(p->revision); - if (val & (1 << 5)) + if (val & ONFI_VERSION_2_3) chip->parameters.onfi.version = 23; - else if (val & (1 << 4)) + else if (val & ONFI_VERSION_2_2) chip->parameters.onfi.version = 22; - else if (val & (1 << 3)) + else if (val & ONFI_VERSION_2_1) chip->parameters.onfi.version = 21; - else if (val & (1 << 2)) + else if (val & ONFI_VERSION_2_0) chip->parameters.onfi.version = 20; - else if (val & (1 << 1)) + else if (val & ONFI_VERSION_1_0) chip->parameters.onfi.version = 10; if (!chip->parameters.onfi.version) { diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index dc4538b58b84..fbac4741da52 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -237,6 +237,17 @@ enum nand_ecc_algo { /* Keep gcc happy */ struct nand_chip; +/* ONFI version bits */ +#define ONFI_VERSION_1_0 BIT(1) +#define ONFI_VERSION_2_0 BIT(2) +#define ONFI_VERSION_2_1 BIT(3) +#define ONFI_VERSION_2_2 BIT(4) +#define ONFI_VERSION_2_3 BIT(5) +#define ONFI_VERSION_3_0 BIT(6) +#define ONFI_VERSION_3_1 BIT(7) +#define ONFI_VERSION_3_2 BIT(8) +#define ONFI_VERSION_4_0 BIT(9) + /* ONFI features */ #define ONFI_FEATURE_16_BIT_BUS (1 << 0) #define ONFI_FEATURE_EXT_PARAM_PAGE (1 << 7) -- cgit v1.2.3 From 243f37cb1f63dfdc4451802aa519fb3c3d9c9d32 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Mon, 25 Jun 2018 10:44:46 +1200 Subject: mtd: rawnand: micron: add fixup for ONFI revision Some Micron NAND chips (MT29F1G08ABAFAWP-ITE:F) report 00 00 for the revision number field of the ONFI parameter page. Rather than rejecting these outright assume ONFI version 1.0 if the revision number is 00 00. Signed-off-by: Chris Packham Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_micron.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index 203faba0a9c1..d30bd4df9b12 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -290,6 +290,19 @@ static int micron_nand_init(struct nand_chip *chip) return 0; } +static void micron_fixup_onfi_param_page(struct nand_chip *chip, + struct nand_onfi_params *p) +{ + /* + * MT29F1G08ABAFAWP-ITE:F and possibly others report 00 00 for the + * revision number field of the ONFI parameter page. Assume ONFI + * version 1.0 if the revision number is 00 00. + */ + if (le16_to_cpu(p->revision) == 0) + p->revision = cpu_to_le16(ONFI_VERSION_1_0); +} + const struct nand_manufacturer_ops micron_nand_manuf_ops = { .init = micron_nand_init, + .fixup_onfi_param_page = micron_fixup_onfi_param_page, }; -- cgit v1.2.3 From 181ace9e1b2e1c5c2be590bc9603baa65f3a16d8 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Wed, 20 Jun 2018 12:57:28 +0530 Subject: mtd: rawnand: helper function for setting up ECC configuration commit 2c8f8afa7f92 ("mtd: nand: add generic helpers to check, match, maximize ECC settings") provides generic helpers which drivers can use for setting up ECC parameters. Since same board can have different ECC strength nand chips so following is the logic for setting up ECC strength and ECC step size, which can be used by most of the drivers. 1. If both ECC step size and ECC strength are already set (usually by DT) then just check whether this setting is supported by NAND controller. 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength supported by NAND controller. 3. Otherwise, try to match the ECC step size and ECC strength closest to the chip's requirement. If available OOB size can't fit the chip requirement then select maximum ECC strength which can be fit with available OOB size. This patch introduces nand_ecc_choose_conf function which calls the required helper functions for the above logic. The drivers can use this single function instead of calling the 3 helper functions individually. Signed-off-by: Abhishek Sahu Reviewed-by: Masahiro Yamada Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_base.c | 33 +++++++++++++++++++++++++++++++++ include/linux/mtd/rawnand.h | 3 +++ 2 files changed, 36 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index dbf6e80e9ab5..2858d0b09c1d 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -6295,6 +6295,39 @@ int nand_maximize_ecc(struct nand_chip *chip, } EXPORT_SYMBOL_GPL(nand_maximize_ecc); +/** + * nand_ecc_choose_conf - Set the ECC strength and ECC step size + * @chip: nand chip info structure + * @caps: ECC engine caps info structure + * @oobavail: OOB size that the ECC engine can use + * + * Choose the ECC configuration according to following logic + * + * 1. If both ECC step size and ECC strength are already set (usually by DT) + * then check if it is supported by this controller. + * 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength. + * 3. Otherwise, try to match the ECC step size and ECC strength closest + * to the chip's requirement. If available OOB size can't fit the chip + * requirement then fallback to the maximum ECC step size and ECC strength. + * + * On success, the chosen ECC settings are set. + */ +int nand_ecc_choose_conf(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail) +{ + if (chip->ecc.size && chip->ecc.strength) + return nand_check_ecc_caps(chip, caps, oobavail); + + if (chip->ecc.options & NAND_ECC_MAXIMIZE) + return nand_maximize_ecc(chip, caps, oobavail); + + if (!nand_match_ecc_req(chip, caps, oobavail)) + return 0; + + return nand_maximize_ecc(chip, caps, oobavail); +} +EXPORT_SYMBOL_GPL(nand_ecc_choose_conf); + /* * Check if the chip configuration meet the datasheet requirements. diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index fbac4741da52..2ce305388471 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1672,6 +1672,9 @@ int nand_match_ecc_req(struct nand_chip *chip, int nand_maximize_ecc(struct nand_chip *chip, const struct nand_ecc_caps *caps, int oobavail); +int nand_ecc_choose_conf(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail); + /* Default write_oob implementation */ int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page); -- cgit v1.2.3 From f9801fda963ec9d0f6521695d2b0158249d85c94 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Wed, 20 Jun 2018 12:57:29 +0530 Subject: mtd: rawnand: denali: use helper function for ecc setup Use the NAND core helper function nand_ecc_choose_conf to tune the ECC parameters instead of the function locally defined. Signed-off-by: Abhishek Sahu Acked-by: Masahiro Yamada Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/denali.c | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c index 2a302a1d1430..a586a1d8b997 100644 --- a/drivers/mtd/nand/raw/denali.c +++ b/drivers/mtd/nand/raw/denali.c @@ -1120,33 +1120,6 @@ int denali_calc_ecc_bytes(int step_size, int strength) } EXPORT_SYMBOL(denali_calc_ecc_bytes); -static int denali_ecc_setup(struct mtd_info *mtd, struct nand_chip *chip, - struct denali_nand_info *denali) -{ - int oobavail = mtd->oobsize - denali->oob_skip_bytes; - int ret; - - /* - * If .size and .strength are already set (usually by DT), - * check if they are supported by this controller. - */ - if (chip->ecc.size && chip->ecc.strength) - return nand_check_ecc_caps(chip, denali->ecc_caps, oobavail); - - /* - * We want .size and .strength closest to the chip's requirement - * unless NAND_ECC_MAXIMIZE is requested. - */ - if (!(chip->ecc.options & NAND_ECC_MAXIMIZE)) { - ret = nand_match_ecc_req(chip, denali->ecc_caps, oobavail); - if (!ret) - return 0; - } - - /* Max ECC strength is the last thing we can do */ - return nand_maximize_ecc(chip, denali->ecc_caps, oobavail); -} - static int denali_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion) { @@ -1317,7 +1290,8 @@ int denali_init(struct denali_nand_info *denali) chip->ecc.mode = NAND_ECC_HW_SYNDROME; chip->options |= NAND_NO_SUBPAGE_WRITE; - ret = denali_ecc_setup(mtd, chip, denali); + ret = nand_ecc_choose_conf(chip, denali->ecc_caps, + mtd->oobsize - denali->oob_skip_bytes); if (ret) { dev_err(denali->dev, "Failed to setup ECC settings.\n"); goto disable_irq; -- cgit v1.2.3 From 320bdb5fb9c4a6b67ceadab2d7ed74c21c957561 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Wed, 20 Jun 2018 12:57:31 +0530 Subject: mtd: rawnand: qcom: remove dt property nand-ecc-step-size QCOM NAND controller supports only one step size (512) so nand-ecc-step-size DT property is redundant. This property can be removed and ecc step size can be assigned with 512 value. Signed-off-by: Abhishek Sahu Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/qcom_nandc.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 6a5519f0ff25..bf80a61dd941 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2325,15 +2325,8 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) bool wide_bus; int ecc_mode = 1; - /* - * the controller requires each step consists of 512 bytes of data. - * bail out if DT has populated a wrong step size. - */ - if (ecc->size != NANDC_STEP_SIZE) { - dev_err(nandc->dev, "invalid ecc size\n"); - return -EINVAL; - } - + /* controller only supports 512 bytes data steps */ + ecc->size = NANDC_STEP_SIZE; wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; if (ecc->strength >= 8) { -- cgit v1.2.3 From 7ddb937f2c1dc79a9d35e6319b96e05ba7b04a8f Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Wed, 20 Jun 2018 12:57:32 +0530 Subject: mtd: rawnand: qcom: use the ecc strength from device parameter Currently the driver uses the ECC strength specified in DT. The QPIC/EBI2 NAND supports 4 or 8-bit ECC correction. The same kind of board can have different NAND parts so use the ECC strength from device parameters if it is not specified in DT. Signed-off-by: Abhishek Sahu Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/qcom_nandc.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index bf80a61dd941..237578030a2a 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2315,19 +2315,39 @@ static const struct mtd_ooblayout_ops qcom_nand_ooblayout_ops = { .free = qcom_nand_ooblayout_free, }; +static int +qcom_nandc_calc_ecc_bytes(int step_size, int strength) +{ + return strength == 4 ? 12 : 16; +} +NAND_ECC_CAPS_SINGLE(qcom_nandc_ecc_caps, qcom_nandc_calc_ecc_bytes, + NANDC_STEP_SIZE, 4, 8); + static int qcom_nand_host_setup(struct qcom_nand_host *host) { struct nand_chip *chip = &host->chip; struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - int cwperpage, bad_block_byte; + int cwperpage, bad_block_byte, ret; bool wide_bus; int ecc_mode = 1; /* controller only supports 512 bytes data steps */ ecc->size = NANDC_STEP_SIZE; wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false; + cwperpage = mtd->writesize / NANDC_STEP_SIZE; + + /* + * Each CW has 4 available OOB bytes which will be protected with ECC + * so remaining bytes can be used for ECC. + */ + ret = nand_ecc_choose_conf(chip, &qcom_nandc_ecc_caps, + mtd->oobsize - (cwperpage * 4)); + if (ret) { + dev_err(nandc->dev, "No valid ECC settings possible\n"); + return ret; + } if (ecc->strength >= 8) { /* 8 bit ECC defaults to BCH ECC on all platforms */ @@ -2396,7 +2416,6 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops); - cwperpage = mtd->writesize / ecc->size; nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage, cwperpage); @@ -2412,12 +2431,6 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) * for 8 bit ECC */ host->cw_size = host->cw_data + ecc->bytes; - - if (ecc->bytes * (mtd->writesize / ecc->size) > mtd->oobsize) { - dev_err(nandc->dev, "ecc data doesn't fit in OOB area\n"); - return -EINVAL; - } - bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1; host->cfg0 = (cwperpage - 1) << CW_PER_PAGE -- cgit v1.2.3 From 6f20070d51a20e489ef117603210264c6bcde8a5 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Wed, 20 Jun 2018 12:57:33 +0530 Subject: mtd: rawnand: qcom: wait for desc completion in all BAM channels The BAM has 3 channels - tx, rx and command. command channel is used for register read/writes, tx channel for data writes and rx channel for data reads. Currently, the driver assumes the transfer completion once it gets all the command descriptors completed. Sometimes, there is race condition between data channel (tx/rx) and command channel completion. In these cases, the data present in buffer is not valid during small window between command descriptor completion and data descriptor completion. This patch generates NAND transfer completion when both (Data and Command) DMA channels have completed all its DMA descriptors. It assigns completion callback in last DMA descriptors of that channel and wait for completion. Fixes: 8d6b6d7e135e ("mtd: nand: qcom: support for command descriptor formation") Cc: stable@vger.kernel.org Signed-off-by: Abhishek Sahu Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/qcom_nandc.c | 53 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 237578030a2a..fc2014997dde 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -213,6 +213,8 @@ nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \ #define QPIC_PER_CW_CMD_SGL 32 #define QPIC_PER_CW_DATA_SGL 8 +#define QPIC_NAND_COMPLETION_TIMEOUT msecs_to_jiffies(2000) + /* * Flags used in DMA descriptor preparation helper functions * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma) @@ -245,6 +247,11 @@ nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \ * @tx_sgl_start - start index in data sgl for tx. * @rx_sgl_pos - current index in data sgl for rx. * @rx_sgl_start - start index in data sgl for rx. + * @wait_second_completion - wait for second DMA desc completion before making + * the NAND transfer completion. + * @txn_done - completion for NAND transfer. + * @last_data_desc - last DMA desc in data channel (tx/rx). + * @last_cmd_desc - last DMA desc in command channel. */ struct bam_transaction { struct bam_cmd_element *bam_ce; @@ -258,6 +265,10 @@ struct bam_transaction { u32 tx_sgl_start; u32 rx_sgl_pos; u32 rx_sgl_start; + bool wait_second_completion; + struct completion txn_done; + struct dma_async_tx_descriptor *last_data_desc; + struct dma_async_tx_descriptor *last_cmd_desc; }; /* @@ -504,6 +515,8 @@ alloc_bam_transaction(struct qcom_nand_controller *nandc) bam_txn->data_sgl = bam_txn_buf; + init_completion(&bam_txn->txn_done); + return bam_txn; } @@ -523,11 +536,33 @@ static void clear_bam_transaction(struct qcom_nand_controller *nandc) bam_txn->tx_sgl_start = 0; bam_txn->rx_sgl_pos = 0; bam_txn->rx_sgl_start = 0; + bam_txn->last_data_desc = NULL; + bam_txn->wait_second_completion = false; sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage * QPIC_PER_CW_CMD_SGL); sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage * QPIC_PER_CW_DATA_SGL); + + reinit_completion(&bam_txn->txn_done); +} + +/* Callback for DMA descriptor completion */ +static void qpic_bam_dma_done(void *data) +{ + struct bam_transaction *bam_txn = data; + + /* + * In case of data transfer with NAND, 2 callbacks will be generated. + * One for command channel and another one for data channel. + * If current transaction has data descriptors + * (i.e. wait_second_completion is true), then set this to false + * and wait for second DMA descriptor completion. + */ + if (bam_txn->wait_second_completion) + bam_txn->wait_second_completion = false; + else + complete(&bam_txn->txn_done); } static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip) @@ -756,6 +791,12 @@ static int prepare_bam_async_desc(struct qcom_nand_controller *nandc, desc->dma_desc = dma_desc; + /* update last data/command descriptor */ + if (chan == nandc->cmd_chan) + bam_txn->last_cmd_desc = dma_desc; + else + bam_txn->last_data_desc = dma_desc; + list_add_tail(&desc->node, &nandc->desc_list); return 0; @@ -1273,10 +1314,20 @@ static int submit_descs(struct qcom_nand_controller *nandc) cookie = dmaengine_submit(desc->dma_desc); if (nandc->props->is_bam) { + bam_txn->last_cmd_desc->callback = qpic_bam_dma_done; + bam_txn->last_cmd_desc->callback_param = bam_txn; + if (bam_txn->last_data_desc) { + bam_txn->last_data_desc->callback = qpic_bam_dma_done; + bam_txn->last_data_desc->callback_param = bam_txn; + bam_txn->wait_second_completion = true; + } + dma_async_issue_pending(nandc->tx_chan); dma_async_issue_pending(nandc->rx_chan); + dma_async_issue_pending(nandc->cmd_chan); - if (dma_sync_wait(nandc->cmd_chan, cookie) != DMA_COMPLETE) + if (!wait_for_completion_timeout(&bam_txn->txn_done, + QPIC_NAND_COMPLETION_TIMEOUT)) return -ETIMEDOUT; } else { if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) -- cgit v1.2.3 From 8eab72148808a74535c3322f6ca30aba77d2d4a9 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Wed, 20 Jun 2018 12:57:34 +0530 Subject: mtd: rawnand: qcom: erased page detection for uncorrectable errors only Following is the flow in the HW if controller tries to read erased page: 1. First ECC uncorrectable error will be generated from ECC engine since ECC engine first calculates the ECC with all 0xff and match the calculated ECC with ECC code in OOB (which is again all 0xff). 2. After getting ECC error, erased CW detection logic will be applied which is different for BCH and RS ECC a. For BCH, HW checks if all the bytes in page are 0xff and then it updates the status in separate register NAND_ERASED_CW_DETECT_STATUS. b. For RS ECC, the HW reports the same error when reading an erased CW, but it notifies that it is an erased CW by placing special characters at certain offsets in the buffer. So the erased CW detect status should be checked only if ECC engine generated the uncorrectable error. Currently for all other operational errors also (like TIMEOUT, MPU errors, etc.), the erased CW detect logic is being applied so fix this and return EIO for other operational errors. Signed-off-by: Abhishek Sahu Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/qcom_nandc.c | 67 +++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index fc2014997dde..0d931d51f85a 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1576,6 +1576,7 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, struct nand_ecc_ctrl *ecc = &chip->ecc; unsigned int max_bitflips = 0; struct read_stats *buf; + bool flash_op_err = false; int i; buf = (struct read_stats *)nandc->reg_read_buf; @@ -1597,8 +1598,18 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, buffer = le32_to_cpu(buf->buffer); erased_cw = le32_to_cpu(buf->erased_cw); - if (flash & (FS_OP_ERR | FS_MPU_ERR)) { + /* + * Check ECC failure for each codeword. ECC failure can + * happen in either of the following conditions + * 1. If number of bitflips are greater than ECC engine + * capability. + * 2. If this codeword contains all 0xff for which erased + * codeword detection check will be done. + */ + if ((flash & FS_OP_ERR) && (buffer & BS_UNCORRECTABLE_BIT)) { bool erased; + int ret, ecclen, extraooblen; + void *eccbuf; /* ignore erased codeword errors */ if (host->bch_enabled) { @@ -1616,29 +1627,36 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, continue; } - if (buffer & BS_UNCORRECTABLE_BIT) { - int ret, ecclen, extraooblen; - void *eccbuf; - - eccbuf = oob_buf ? oob_buf + oob_len : NULL; - ecclen = oob_buf ? host->ecc_bytes_hw : 0; - extraooblen = oob_buf ? oob_len : 0; - - /* - * make sure it isn't an erased page reported - * as not-erased by HW because of a few bitflips - */ - ret = nand_check_erased_ecc_chunk(data_buf, - data_len, eccbuf, ecclen, oob_buf, - extraooblen, ecc->strength); - if (ret < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += ret; - max_bitflips = - max_t(unsigned int, max_bitflips, ret); - } + eccbuf = oob_buf ? oob_buf + oob_len : NULL; + ecclen = oob_buf ? host->ecc_bytes_hw : 0; + extraooblen = oob_buf ? oob_len : 0; + + /* + * make sure it isn't an erased page reported + * as not-erased by HW because of a few bitflips + */ + ret = nand_check_erased_ecc_chunk(data_buf, + data_len, eccbuf, ecclen, oob_buf, + extraooblen, ecc->strength); + if (ret < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += ret; + max_bitflips = + max_t(unsigned int, max_bitflips, ret); } + /* + * Check if MPU or any other operational error (timeout, + * device failure, etc.) happened for this codeword and + * make flash_op_err true. If flash_op_err is set, then + * EIO will be returned for page read. + */ + } else if (flash & (FS_OP_ERR | FS_MPU_ERR)) { + flash_op_err = true; + /* + * No ECC or operational errors happened. Check the number of + * bits corrected and update the ecc_stats.corrected. + */ } else { unsigned int stat; @@ -1652,6 +1670,9 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, oob_buf += oob_len + ecc->bytes; } + if (flash_op_err) + return -EIO; + return max_bitflips; } -- cgit v1.2.3 From 2f610386736d9e5dc69fa06374138f9712690921 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Wed, 20 Jun 2018 12:57:35 +0530 Subject: mtd: rawnand: qcom: fix null pointer access for erased page detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parse_read_errors can be called with only oob_buf in which case data_buf will be NULL. If data_buf is NULL, then don’t treat this page as completely erased in case of ECC uncorrectable error for RS ECC. For BCH ECC, the controller itself tells regarding erased page in status register. Signed-off-by: Abhishek Sahu Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/qcom_nandc.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 0d931d51f85a..a831f9cb458a 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1611,13 +1611,24 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, int ret, ecclen, extraooblen; void *eccbuf; - /* ignore erased codeword errors */ + /* + * For BCH ECC, ignore erased codeword errors, if + * ERASED_CW bits are set. + */ if (host->bch_enabled) { erased = (erased_cw & ERASED_CW) == ERASED_CW ? true : false; - } else { + /* + * For RS ECC, HW reports the erased CW by placing + * special characters at certain offsets in the buffer. + * These special characters will be valid only if + * complete page is read i.e. data_buf is not NULL. + */ + } else if (data_buf) { erased = erased_chunk_check_and_fixup(data_buf, data_len); + } else { + erased = false; } if (erased) { @@ -1665,7 +1676,8 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, max_bitflips = max(max_bitflips, stat); } - data_buf += data_len; + if (data_buf) + data_buf += data_len; if (oob_buf) oob_buf += oob_len + ecc->bytes; } -- cgit v1.2.3 From add0cfa3c3e51149c2101765edf05c548ba5f2ee Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Wed, 20 Jun 2018 12:57:36 +0530 Subject: mtd: rawnand: qcom: parse read errors for read oob also read_page and read_oob both calls the read_page_ecc function. The QCOM NAND controller protect the OOB available bytes with ECC so read errors should be checked for read_oob also. This patch moves the error checking code inside read_page_ecc so caller does not have to check explicitly for read errors. Signed-off-by: Abhishek Sahu Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/qcom_nandc.c | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index a831f9cb458a..285b2ad328de 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1698,6 +1698,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, struct nand_chip *chip = &host->chip; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; + u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; int i, ret; config_nand_page_read(nandc); @@ -1758,12 +1759,14 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, } ret = submit_descs(nandc); - if (ret) - dev_err(nandc->dev, "failure to read page/oob\n"); - free_descs(nandc); - return ret; + if (ret) { + dev_err(nandc->dev, "failure to read page/oob\n"); + return ret; + } + + return parse_read_errors(host, data_buf_start, oob_buf_start); } /* @@ -1808,20 +1811,14 @@ static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, struct qcom_nand_host *host = to_qcom_nand_host(chip); struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); u8 *data_buf, *oob_buf = NULL; - int ret; nand_read_page_op(chip, page, 0, NULL, 0); data_buf = buf; oob_buf = oob_required ? chip->oob_poi : NULL; clear_bam_transaction(nandc); - ret = read_page_ecc(host, data_buf, oob_buf); - if (ret) { - dev_err(nandc->dev, "failure to read page\n"); - return ret; - } - return parse_read_errors(host, data_buf, oob_buf); + return read_page_ecc(host, data_buf, oob_buf); } /* implements ecc->read_page_raw() */ @@ -1911,7 +1908,6 @@ static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, struct qcom_nand_host *host = to_qcom_nand_host(chip); struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; - int ret; clear_read_regs(nandc); clear_bam_transaction(nandc); @@ -1920,11 +1916,7 @@ static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, set_address(host, 0, page); update_rw_regs(host, ecc->steps, true); - ret = read_page_ecc(host, NULL, chip->oob_poi); - if (ret) - dev_err(nandc->dev, "failure to read oob\n"); - - return ret; + return read_page_ecc(host, NULL, chip->oob_poi); } /* implements ecc->write_page() */ -- cgit v1.2.3 From 28eed9f6cab6ab28cdca4a43df4f8ed51dd5f8f8 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Wed, 20 Jun 2018 12:57:37 +0530 Subject: mtd: rawnand: qcom: modify write_oob to remove read codeword part QCOM NAND controller layout protects available OOB data bytes with ECC also so when ecc->write_oob() is being called then it can't update just OOB bytes. Currently, it first reads the last codeword which includes old OOB bytes. Then it updates the old OOB bytes with new ones and then again writes the codeword back. The reading codeword is unnecessary since user is responsible to have these bytes cleared to 0xFF. This patch removes the read part and updates the OOB bytes with data area padded with OxFF. Signed-off-by: Abhishek Sahu Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/qcom_nandc.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 285b2ad328de..28361b56ac45 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2064,11 +2064,9 @@ static int qcom_nandc_write_page_raw(struct mtd_info *mtd, /* * implements ecc->write_oob() * - * the NAND controller cannot write only data or only oob within a codeword, - * since ecc is calculated for the combined codeword. we first copy the - * entire contents for the last codeword(data + oob), replace the old oob - * with the new one in chip->oob_poi, and then write the entire codeword. - * this read-copy-write operation results in a slight performance loss. + * the NAND controller cannot write only data or only OOB within a codeword + * since ECC is calculated for the combined codeword. So update the OOB from + * chip->oob_poi, and pad the data area with OxFF before writing. */ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) @@ -2081,19 +2079,13 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int ret; host->use_ecc = true; - - clear_bam_transaction(nandc); - ret = copy_last_cw(host, page); - if (ret) - return ret; - - clear_read_regs(nandc); clear_bam_transaction(nandc); /* calculate the data and oob size for the last codeword/step */ data_size = ecc->size - ((ecc->steps - 1) << 2); oob_size = mtd->oobavail; + memset(nandc->data_buffer, 0xff, host->cw_data); /* override new oob content to last codeword */ mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, oob, 0, mtd->oobavail); -- cgit v1.2.3 From 783b5bf9bd91a59717df38ec3ba7d65b1f441f32 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Wed, 20 Jun 2018 12:57:38 +0530 Subject: mtd: rawnand: qcom: fix return value for raw page read Fix value returned by ->read_page_raw() to be the actual operation status, instead of always 0. Signed-off-by: Abhishek Sahu Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/qcom_nandc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 28361b56ac45..887b1f6aff14 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1898,7 +1898,7 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, free_descs(nandc); - return 0; + return ret; } /* implements ecc->read_oob() */ -- cgit v1.2.3 From 5bc36b2bf6e2c8bff723ebcd9ae9bd38156d21c0 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Wed, 20 Jun 2018 12:57:39 +0530 Subject: mtd: rawnand: qcom: check for operation errors in case of raw read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently there is no error checking for raw read. For raw reads, there won’t be any ECC failure but the operational failures are possible, so schedule the NAND_FLASH_STATUS read after each codeword. Signed-off-by: Abhishek Sahu Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/qcom_nandc.c | 58 +++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 887b1f6aff14..5999c397b771 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1096,7 +1096,8 @@ static void config_nand_page_read(struct qcom_nand_controller *nandc) * Helper to prepare DMA descriptors for configuring registers * before reading each codeword in NAND page. */ -static void config_nand_cw_read(struct qcom_nand_controller *nandc) +static void +config_nand_cw_read(struct qcom_nand_controller *nandc, bool use_ecc) { if (nandc->props->is_bam) write_reg_dma(nandc, NAND_READ_LOCATION_0, 4, @@ -1105,19 +1106,25 @@ static void config_nand_cw_read(struct qcom_nand_controller *nandc) write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); - read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, - NAND_BAM_NEXT_SGL); + if (use_ecc) { + read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0); + read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1, + NAND_BAM_NEXT_SGL); + } else { + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + } } /* * Helper to prepare dma descriptors to configure registers needed for reading a * single codeword in page */ -static void config_nand_single_cw_page_read(struct qcom_nand_controller *nandc) +static void +config_nand_single_cw_page_read(struct qcom_nand_controller *nandc, + bool use_ecc) { config_nand_page_read(nandc); - config_nand_cw_read(nandc); + config_nand_cw_read(nandc, use_ecc); } /* @@ -1198,7 +1205,7 @@ static int nandc_param(struct qcom_nand_host *host) nandc->buf_count = 512; memset(nandc->data_buffer, 0xff, nandc->buf_count); - config_nand_single_cw_page_read(nandc); + config_nand_single_cw_page_read(nandc, false); read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, nandc->buf_count, 0); @@ -1563,6 +1570,23 @@ struct read_stats { __le32 erased_cw; }; +/* reads back FLASH_STATUS register set by the controller */ +static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) +{ + struct nand_chip *chip = &host->chip; + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + int i; + + for (i = 0; i < cw_cnt; i++) { + u32 flash = le32_to_cpu(nandc->reg_read_buf[i]); + + if (flash & (FS_OP_ERR | FS_MPU_ERR)) + return -EIO; + } + + return 0; +} + /* * reads back status registers set by the controller to notify page read * errors. this is equivalent to what 'ecc->correct()' would do. @@ -1729,7 +1753,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, } } - config_nand_cw_read(nandc); + config_nand_cw_read(nandc, true); if (data_buf) read_data_dma(nandc, FLASH_BUF_ACC, data_buf, @@ -1791,7 +1815,7 @@ static int copy_last_cw(struct qcom_nand_host *host, int page) set_address(host, host->cw_size * (ecc->steps - 1), page); update_rw_regs(host, 1, true); - config_nand_single_cw_page_read(nandc); + config_nand_single_cw_page_read(nandc, host->use_ecc); read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); @@ -1874,7 +1898,7 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1); } - config_nand_cw_read(nandc); + config_nand_cw_read(nandc, false); read_data_dma(nandc, reg_off, data_buf, data_size1, 0); reg_off += data_size1; @@ -1893,12 +1917,13 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, } ret = submit_descs(nandc); - if (ret) - dev_err(nandc->dev, "failure to read raw page\n"); - free_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure to read raw page\n"); + return ret; + } - return ret; + return check_flash_errors(host, ecc->steps); } /* implements ecc->read_oob() */ @@ -2117,7 +2142,6 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; int page, ret, bbpos, bad = 0; - u32 flash_status; page = (int)(ofs >> chip->page_shift) & chip->pagemask; @@ -2134,9 +2158,7 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) if (ret) goto err; - flash_status = le32_to_cpu(nandc->reg_read_buf[0]); - - if (flash_status & (FS_OP_ERR | FS_MPU_ERR)) { + if (check_flash_errors(host, 1)) { dev_warn(nandc->dev, "error when trying to read BBM\n"); goto err; } -- cgit v1.2.3 From 85632c17194e35e00871cfd80713c9acf087ac4f Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Wed, 20 Jun 2018 12:57:40 +0530 Subject: mtd: rawnand: qcom: code reorganization for raw read Make separate function to perform raw read for one codeword and call this function multiple times for each codeword in case of raw page read. This separate function will help in subsequent patches related with erased codeword bitflip detection. It will decrease throughput for raw page read. Raw page read is used for debug purpose so it won't affect normal flash operations. Signed-off-by: Abhishek Sahu Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/qcom_nandc.c | 146 ++++++++++++++++++++------------------ 1 file changed, 78 insertions(+), 68 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 5999c397b771..160acdff7d86 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1587,6 +1587,74 @@ static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) return 0; } +/* performs raw read for one codeword */ +static int +qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, + u8 *data_buf, u8 *oob_buf, int page, int cw) +{ + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int data_size1, data_size2, oob_size1, oob_size2; + int ret, reg_off = FLASH_BUF_ACC, read_loc = 0; + + nand_read_page_op(chip, page, 0, NULL, 0); + host->use_ecc = false; + + clear_bam_transaction(nandc); + set_address(host, host->cw_size * cw, page); + update_rw_regs(host, 1, true); + config_nand_page_read(nandc); + + data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); + oob_size1 = host->bbm_size; + + if (cw == (ecc->steps - 1)) { + data_size2 = ecc->size - data_size1 - + ((ecc->steps - 1) * 4); + oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw + + host->spare_bytes; + } else { + data_size2 = host->cw_data - data_size1; + oob_size2 = host->ecc_bytes_hw + host->spare_bytes; + } + + if (nandc->props->is_bam) { + nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0); + read_loc += data_size1; + + nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0); + read_loc += oob_size1; + + nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0); + read_loc += data_size2; + + nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1); + } + + config_nand_cw_read(nandc, false); + + read_data_dma(nandc, reg_off, data_buf, data_size1, 0); + reg_off += data_size1; + + read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0); + reg_off += oob_size1; + + read_data_dma(nandc, reg_off, data_buf + data_size1, data_size2, 0); + reg_off += data_size2; + + read_data_dma(nandc, reg_off, oob_buf + oob_size1, oob_size2, 0); + + ret = submit_descs(nandc); + free_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure to read raw cw %d\n", cw); + return ret; + } + + return check_flash_errors(host, 1); +} + /* * reads back status registers set by the controller to notify page read * errors. this is equivalent to what 'ecc->correct()' would do. @@ -1851,79 +1919,21 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, int oob_required, int page) { struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - u8 *data_buf, *oob_buf; struct nand_ecc_ctrl *ecc = &chip->ecc; - int i, ret; - int read_loc; + int cw, ret; + u8 *data_buf = buf, *oob_buf = chip->oob_poi; - nand_read_page_op(chip, page, 0, NULL, 0); - data_buf = buf; - oob_buf = chip->oob_poi; - - host->use_ecc = false; - - clear_bam_transaction(nandc); - update_rw_regs(host, ecc->steps, true); - config_nand_page_read(nandc); - - for (i = 0; i < ecc->steps; i++) { - int data_size1, data_size2, oob_size1, oob_size2; - int reg_off = FLASH_BUF_ACC; - - data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); - oob_size1 = host->bbm_size; - - if (i == (ecc->steps - 1)) { - data_size2 = ecc->size - data_size1 - - ((ecc->steps - 1) << 2); - oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw + - host->spare_bytes; - } else { - data_size2 = host->cw_data - data_size1; - oob_size2 = host->ecc_bytes_hw + host->spare_bytes; - } - - if (nandc->props->is_bam) { - read_loc = 0; - nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0); - read_loc += data_size1; - - nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0); - read_loc += oob_size1; - - nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0); - read_loc += data_size2; - - nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1); - } - - config_nand_cw_read(nandc, false); - - read_data_dma(nandc, reg_off, data_buf, data_size1, 0); - reg_off += data_size1; - data_buf += data_size1; - - read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0); - reg_off += oob_size1; - oob_buf += oob_size1; - - read_data_dma(nandc, reg_off, data_buf, data_size2, 0); - reg_off += data_size2; - data_buf += data_size2; + for (cw = 0; cw < ecc->steps; cw++) { + ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, oob_buf, + page, cw); + if (ret) + return ret; - read_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); - oob_buf += oob_size2; + data_buf += host->cw_data; + oob_buf += ecc->bytes; } - ret = submit_descs(nandc); - free_descs(nandc); - if (ret) { - dev_err(nandc->dev, "failure to read raw page\n"); - return ret; - } - - return check_flash_errors(host, ecc->steps); + return 0; } /* implements ecc->read_oob() */ -- cgit v1.2.3 From 0cf5c7dbaa392d11cfae532cedeaad0ac48b2165 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Wed, 20 Jun 2018 12:57:42 +0530 Subject: mtd: rawnand: provide only single helper function for ECC conf Function nand_ecc_choose_conf() will be help for all the cases, so other helper functions can be made static. nand_check_ecc_caps(): Invoke nand_ecc_choose_conf() with both chip->ecc.size and chip->ecc.strength value set. nand_maximize_ecc(): Invoke nand_ecc_choose_conf() with NAND_ECC_MAXIMIZE flag. nand_match_ecc_req(): Invoke nand_ecc_choose_conf() with either chip->ecc.size or chip->ecc.strength value set and without NAND_ECC_MAXIMIZE flag. CC: Masahiro Yamada Signed-off-by: Abhishek Sahu Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_base.c | 39 +++++++++++++++------------------------ include/linux/mtd/rawnand.h | 9 --------- 2 files changed, 15 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 2858d0b09c1d..faac82b1e058 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -6085,24 +6085,17 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd) * by the controller and the calculated ECC bytes fit within the chip's OOB. * On success, the calculated ECC bytes is set. */ -int nand_check_ecc_caps(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail) +static int +nand_check_ecc_caps(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail) { struct mtd_info *mtd = nand_to_mtd(chip); const struct nand_ecc_step_info *stepinfo; int preset_step = chip->ecc.size; int preset_strength = chip->ecc.strength; - int nsteps, ecc_bytes; + int ecc_bytes, nsteps = mtd->writesize / preset_step; int i, j; - if (WARN_ON(oobavail < 0)) - return -EINVAL; - - if (!preset_step || !preset_strength) - return -ENODATA; - - nsteps = mtd->writesize / preset_step; - for (i = 0; i < caps->nstepinfos; i++) { stepinfo = &caps->stepinfos[i]; @@ -6135,7 +6128,6 @@ int nand_check_ecc_caps(struct nand_chip *chip, return -ENOTSUPP; } -EXPORT_SYMBOL_GPL(nand_check_ecc_caps); /** * nand_match_ecc_req - meet the chip's requirement with least ECC bytes @@ -6147,8 +6139,9 @@ EXPORT_SYMBOL_GPL(nand_check_ecc_caps); * number of ECC bytes (i.e. with the largest number of OOB-free bytes). * On success, the chosen ECC settings are set. */ -int nand_match_ecc_req(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail) +static int +nand_match_ecc_req(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail) { struct mtd_info *mtd = nand_to_mtd(chip); const struct nand_ecc_step_info *stepinfo; @@ -6159,9 +6152,6 @@ int nand_match_ecc_req(struct nand_chip *chip, int best_ecc_bytes_total = INT_MAX; int i, j; - if (WARN_ON(oobavail < 0)) - return -EINVAL; - /* No information provided by the NAND chip */ if (!req_step || !req_strength) return -ENOTSUPP; @@ -6220,7 +6210,6 @@ int nand_match_ecc_req(struct nand_chip *chip, return 0; } -EXPORT_SYMBOL_GPL(nand_match_ecc_req); /** * nand_maximize_ecc - choose the max ECC strength available @@ -6231,8 +6220,9 @@ EXPORT_SYMBOL_GPL(nand_match_ecc_req); * Choose the max ECC strength that is supported on the controller, and can fit * within the chip's OOB. On success, the chosen ECC settings are set. */ -int nand_maximize_ecc(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail) +static int +nand_maximize_ecc(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail) { struct mtd_info *mtd = nand_to_mtd(chip); const struct nand_ecc_step_info *stepinfo; @@ -6242,9 +6232,6 @@ int nand_maximize_ecc(struct nand_chip *chip, int best_strength, best_ecc_bytes; int i, j; - if (WARN_ON(oobavail < 0)) - return -EINVAL; - for (i = 0; i < caps->nstepinfos; i++) { stepinfo = &caps->stepinfos[i]; step_size = stepinfo->stepsize; @@ -6293,7 +6280,6 @@ int nand_maximize_ecc(struct nand_chip *chip, return 0; } -EXPORT_SYMBOL_GPL(nand_maximize_ecc); /** * nand_ecc_choose_conf - Set the ECC strength and ECC step size @@ -6315,6 +6301,11 @@ EXPORT_SYMBOL_GPL(nand_maximize_ecc); int nand_ecc_choose_conf(struct nand_chip *chip, const struct nand_ecc_caps *caps, int oobavail) { + struct mtd_info *mtd = nand_to_mtd(chip); + + if (WARN_ON(oobavail < 0 || oobavail > mtd->oobsize)) + return -EINVAL; + if (chip->ecc.size && chip->ecc.strength) return nand_check_ecc_caps(chip, caps, oobavail); diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 2ce305388471..0c6fb316b409 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1663,15 +1663,6 @@ int nand_check_erased_ecc_chunk(void *data, int datalen, void *extraoob, int extraooblen, int threshold); -int nand_check_ecc_caps(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail); - -int nand_match_ecc_req(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail); - -int nand_maximize_ecc(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail); - int nand_ecc_choose_conf(struct nand_chip *chip, const struct nand_ecc_caps *caps, int oobavail); -- cgit v1.2.3 From 7529df4652482c33ae1a99ee8189401146f13cb7 Mon Sep 17 00:00:00 2001 From: Peter Pan Date: Fri, 22 Jun 2018 14:28:23 +0200 Subject: mtd: nand: Add core infrastructure to support SPI NANDs Add a SPI NAND framework based on the generic NAND framework and the spi-mem infrastructure. In its current state, this framework supports the following features: - single/dual/quad IO modes - on-die ECC Signed-off-by: Peter Pan Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/Kconfig | 1 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/spi/Kconfig | 7 + drivers/mtd/nand/spi/Makefile | 3 + drivers/mtd/nand/spi/core.c | 1136 +++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 416 +++++++++++++++ include/linux/spi/spi-mem.h | 4 +- 7 files changed, 1567 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/Kconfig create mode 100644 drivers/mtd/nand/spi/Makefile create mode 100644 drivers/mtd/nand/spi/core.c create mode 100644 include/linux/mtd/spinand.h (limited to 'drivers') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 88c7d3b4ff8b..9033215e62ea 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -4,3 +4,4 @@ config MTD_NAND_CORE source "drivers/mtd/nand/onenand/Kconfig" source "drivers/mtd/nand/raw/Kconfig" +source "drivers/mtd/nand/spi/Kconfig" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 3f0cb87f1a57..7ecd80c0a66e 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o obj-y += onenand/ obj-y += raw/ +obj-y += spi/ diff --git a/drivers/mtd/nand/spi/Kconfig b/drivers/mtd/nand/spi/Kconfig new file mode 100644 index 000000000000..7c37d2929b68 --- /dev/null +++ b/drivers/mtd/nand/spi/Kconfig @@ -0,0 +1,7 @@ +menuconfig MTD_SPI_NAND + tristate "SPI NAND device Support" + select MTD_NAND_CORE + depends on SPI_MASTER + select SPI_MEM + help + This is the framework for the SPI NAND device drivers. diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile new file mode 100644 index 000000000000..2c473b765027 --- /dev/null +++ b/drivers/mtd/nand/spi/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +spinand-objs := core.o +obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c new file mode 100644 index 000000000000..4efbeb0d85b4 --- /dev/null +++ b/drivers/mtd/nand/spi/core.c @@ -0,0 +1,1136 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016-2017 Micron Technology, Inc. + * + * Authors: + * Peter Pan + * Boris Brezillon + */ + +#define pr_fmt(fmt) "spi-nand: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void spinand_cache_op_adjust_colum(struct spinand_device *spinand, + const struct nand_page_io_req *req, + u16 *column) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int shift; + + if (nand->memorg.planes_per_lun < 2) + return; + + /* The plane number is passed in MSB just above the column address */ + shift = fls(nand->memorg.pagesize); + *column |= req->pos.plane << shift; +} + +static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val) +{ + struct spi_mem_op op = SPINAND_GET_FEATURE_OP(reg, + spinand->scratchbuf); + int ret; + + ret = spi_mem_exec_op(spinand->spimem, &op); + if (ret) + return ret; + + *val = *spinand->scratchbuf; + return 0; +} + +static int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val) +{ + struct spi_mem_op op = SPINAND_SET_FEATURE_OP(reg, + spinand->scratchbuf); + + *spinand->scratchbuf = val; + return spi_mem_exec_op(spinand->spimem, &op); +} + +static int spinand_read_status(struct spinand_device *spinand, u8 *status) +{ + return spinand_read_reg_op(spinand, REG_STATUS, status); +} + +static int spinand_get_cfg(struct spinand_device *spinand, u8 *cfg) +{ + struct nand_device *nand = spinand_to_nand(spinand); + + if (WARN_ON(spinand->cur_target < 0 || + spinand->cur_target >= nand->memorg.ntargets)) + return -EINVAL; + + *cfg = spinand->cfg_cache[spinand->cur_target]; + return 0; +} + +static int spinand_set_cfg(struct spinand_device *spinand, u8 cfg) +{ + struct nand_device *nand = spinand_to_nand(spinand); + int ret; + + if (WARN_ON(spinand->cur_target < 0 || + spinand->cur_target >= nand->memorg.ntargets)) + return -EINVAL; + + if (spinand->cfg_cache[spinand->cur_target] == cfg) + return 0; + + ret = spinand_write_reg_op(spinand, REG_CFG, cfg); + if (ret) + return ret; + + spinand->cfg_cache[spinand->cur_target] = cfg; + return 0; +} + +/** + * spinand_upd_cfg() - Update the configuration register + * @spinand: the spinand device + * @mask: the mask encoding the bits to update in the config reg + * @val: the new value to apply + * + * Update the configuration register. + * + * Return: 0 on success, a negative error code otherwise. + */ +int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val) +{ + int ret; + u8 cfg; + + ret = spinand_get_cfg(spinand, &cfg); + if (ret) + return ret; + + cfg &= ~mask; + cfg |= val; + + return spinand_set_cfg(spinand, cfg); +} + +/** + * spinand_select_target() - Select a specific NAND target/die + * @spinand: the spinand device + * @target: the target/die to select + * + * Select a new target/die. If chip only has one die, this function is a NOOP. + * + * Return: 0 on success, a negative error code otherwise. + */ +int spinand_select_target(struct spinand_device *spinand, unsigned int target) +{ + struct nand_device *nand = spinand_to_nand(spinand); + int ret; + + if (WARN_ON(target >= nand->memorg.ntargets)) + return -EINVAL; + + if (spinand->cur_target == target) + return 0; + + if (nand->memorg.ntargets == 1) { + spinand->cur_target = target; + return 0; + } + + ret = spinand->select_target(spinand, target); + if (ret) + return ret; + + spinand->cur_target = target; + return 0; +} + +static int spinand_init_cfg_cache(struct spinand_device *spinand) +{ + struct nand_device *nand = spinand_to_nand(spinand); + struct device *dev = &spinand->spimem->spi->dev; + unsigned int target; + int ret; + + spinand->cfg_cache = devm_kcalloc(dev, + nand->memorg.ntargets, + sizeof(*spinand->cfg_cache), + GFP_KERNEL); + if (!spinand->cfg_cache) + return -ENOMEM; + + for (target = 0; target < nand->memorg.ntargets; target++) { + ret = spinand_select_target(spinand, target); + if (ret) + return ret; + + /* + * We use spinand_read_reg_op() instead of spinand_get_cfg() + * here to bypass the config cache. + */ + ret = spinand_read_reg_op(spinand, REG_CFG, + &spinand->cfg_cache[target]); + if (ret) + return ret; + } + + return 0; +} + +static int spinand_init_quad_enable(struct spinand_device *spinand) +{ + bool enable = false; + + if (!(spinand->flags & SPINAND_HAS_QE_BIT)) + return 0; + + if (spinand->op_templates.read_cache->data.buswidth == 4 || + spinand->op_templates.write_cache->data.buswidth == 4 || + spinand->op_templates.update_cache->data.buswidth == 4) + enable = true; + + return spinand_upd_cfg(spinand, CFG_QUAD_ENABLE, + enable ? CFG_QUAD_ENABLE : 0); +} + +static int spinand_ecc_enable(struct spinand_device *spinand, + bool enable) +{ + return spinand_upd_cfg(spinand, CFG_ECC_ENABLE, + enable ? CFG_ECC_ENABLE : 0); +} + +static int spinand_write_enable_op(struct spinand_device *spinand) +{ + struct spi_mem_op op = SPINAND_WR_EN_DIS_OP(true); + + return spi_mem_exec_op(spinand->spimem, &op); +} + +static int spinand_load_page_op(struct spinand_device *spinand, + const struct nand_page_io_req *req) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int row = nanddev_pos_to_row(nand, &req->pos); + struct spi_mem_op op = SPINAND_PAGE_READ_OP(row); + + return spi_mem_exec_op(spinand->spimem, &op); +} + +static int spinand_read_from_cache_op(struct spinand_device *spinand, + const struct nand_page_io_req *req) +{ + struct spi_mem_op op = *spinand->op_templates.read_cache; + struct nand_device *nand = spinand_to_nand(spinand); + struct mtd_info *mtd = nanddev_to_mtd(nand); + struct nand_page_io_req adjreq = *req; + unsigned int nbytes = 0; + void *buf = NULL; + u16 column = 0; + int ret; + + if (req->datalen) { + adjreq.datalen = nanddev_page_size(nand); + adjreq.dataoffs = 0; + adjreq.databuf.in = spinand->databuf; + buf = spinand->databuf; + nbytes = adjreq.datalen; + } + + if (req->ooblen) { + adjreq.ooblen = nanddev_per_page_oobsize(nand); + adjreq.ooboffs = 0; + adjreq.oobbuf.in = spinand->oobbuf; + nbytes += nanddev_per_page_oobsize(nand); + if (!buf) { + buf = spinand->oobbuf; + column = nanddev_page_size(nand); + } + } + + spinand_cache_op_adjust_colum(spinand, &adjreq, &column); + op.addr.val = column; + + /* + * Some controllers are limited in term of max RX data size. In this + * case, just repeat the READ_CACHE operation after updating the + * column. + */ + while (nbytes) { + op.data.buf.in = buf; + op.data.nbytes = nbytes; + ret = spi_mem_adjust_op_size(spinand->spimem, &op); + if (ret) + return ret; + + ret = spi_mem_exec_op(spinand->spimem, &op); + if (ret) + return ret; + + buf += op.data.nbytes; + nbytes -= op.data.nbytes; + op.addr.val += op.data.nbytes; + } + + if (req->datalen) + memcpy(req->databuf.in, spinand->databuf + req->dataoffs, + req->datalen); + + if (req->ooblen) { + if (req->mode == MTD_OPS_AUTO_OOB) + mtd_ooblayout_get_databytes(mtd, req->oobbuf.in, + spinand->oobbuf, + req->ooboffs, + req->ooblen); + else + memcpy(req->oobbuf.in, spinand->oobbuf + req->ooboffs, + req->ooblen); + } + + return 0; +} + +static int spinand_write_to_cache_op(struct spinand_device *spinand, + const struct nand_page_io_req *req) +{ + struct spi_mem_op op = *spinand->op_templates.write_cache; + struct nand_device *nand = spinand_to_nand(spinand); + struct mtd_info *mtd = nanddev_to_mtd(nand); + struct nand_page_io_req adjreq = *req; + unsigned int nbytes = 0; + void *buf = NULL; + u16 column = 0; + int ret; + + memset(spinand->databuf, 0xff, + nanddev_page_size(nand) + + nanddev_per_page_oobsize(nand)); + + if (req->datalen) { + memcpy(spinand->databuf + req->dataoffs, req->databuf.out, + req->datalen); + adjreq.dataoffs = 0; + adjreq.datalen = nanddev_page_size(nand); + adjreq.databuf.out = spinand->databuf; + nbytes = adjreq.datalen; + buf = spinand->databuf; + } + + if (req->ooblen) { + if (req->mode == MTD_OPS_AUTO_OOB) + mtd_ooblayout_set_databytes(mtd, req->oobbuf.out, + spinand->oobbuf, + req->ooboffs, + req->ooblen); + else + memcpy(spinand->oobbuf + req->ooboffs, req->oobbuf.out, + req->ooblen); + + adjreq.ooblen = nanddev_per_page_oobsize(nand); + adjreq.ooboffs = 0; + nbytes += nanddev_per_page_oobsize(nand); + if (!buf) { + buf = spinand->oobbuf; + column = nanddev_page_size(nand); + } + } + + spinand_cache_op_adjust_colum(spinand, &adjreq, &column); + + op = *spinand->op_templates.write_cache; + op.addr.val = column; + + /* + * Some controllers are limited in term of max TX data size. In this + * case, split the operation into one LOAD CACHE and one or more + * LOAD RANDOM CACHE. + */ + while (nbytes) { + op.data.buf.out = buf; + op.data.nbytes = nbytes; + + ret = spi_mem_adjust_op_size(spinand->spimem, &op); + if (ret) + return ret; + + ret = spi_mem_exec_op(spinand->spimem, &op); + if (ret) + return ret; + + buf += op.data.nbytes; + nbytes -= op.data.nbytes; + op.addr.val += op.data.nbytes; + + /* + * We need to use the RANDOM LOAD CACHE operation if there's + * more than one iteration, because the LOAD operation resets + * the cache to 0xff. + */ + if (nbytes) { + column = op.addr.val; + op = *spinand->op_templates.update_cache; + op.addr.val = column; + } + } + + return 0; +} + +static int spinand_program_op(struct spinand_device *spinand, + const struct nand_page_io_req *req) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int row = nanddev_pos_to_row(nand, &req->pos); + struct spi_mem_op op = SPINAND_PROG_EXEC_OP(row); + + return spi_mem_exec_op(spinand->spimem, &op); +} + +static int spinand_erase_op(struct spinand_device *spinand, + const struct nand_pos *pos) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int row = nanddev_pos_to_row(nand, pos); + struct spi_mem_op op = SPINAND_BLK_ERASE_OP(row); + + return spi_mem_exec_op(spinand->spimem, &op); +} + +static int spinand_wait(struct spinand_device *spinand, u8 *s) +{ + unsigned long timeo = jiffies + msecs_to_jiffies(400); + u8 status; + int ret; + + do { + ret = spinand_read_status(spinand, &status); + if (ret) + return ret; + + if (!(status & STATUS_BUSY)) + goto out; + } while (time_before(jiffies, timeo)); + + /* + * Extra read, just in case the STATUS_READY bit has changed + * since our last check + */ + ret = spinand_read_status(spinand, &status); + if (ret) + return ret; + +out: + if (s) + *s = status; + + return status & STATUS_BUSY ? -ETIMEDOUT : 0; +} + +static int spinand_read_id_op(struct spinand_device *spinand, u8 *buf) +{ + struct spi_mem_op op = SPINAND_READID_OP(0, spinand->scratchbuf, + SPINAND_MAX_ID_LEN); + int ret; + + ret = spi_mem_exec_op(spinand->spimem, &op); + if (!ret) + memcpy(buf, spinand->scratchbuf, SPINAND_MAX_ID_LEN); + + return ret; +} + +static int spinand_reset_op(struct spinand_device *spinand) +{ + struct spi_mem_op op = SPINAND_RESET_OP; + int ret; + + ret = spi_mem_exec_op(spinand->spimem, &op); + if (ret) + return ret; + + return spinand_wait(spinand, NULL); +} + +static int spinand_lock_block(struct spinand_device *spinand, u8 lock) +{ + return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, lock); +} + +static int spinand_check_ecc_status(struct spinand_device *spinand, u8 status) +{ + struct nand_device *nand = spinand_to_nand(spinand); + + if (spinand->eccinfo.get_status) + return spinand->eccinfo.get_status(spinand, status); + + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case STATUS_ECC_HAS_BITFLIPS: + /* + * We have no way to know exactly how many bitflips have been + * fixed, so let's return the maximum possible value so that + * wear-leveling layers move the data immediately. + */ + return nand->eccreq.strength; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + default: + break; + } + + return -EINVAL; +} + +static int spinand_read_page(struct spinand_device *spinand, + const struct nand_page_io_req *req, + bool ecc_enabled) +{ + u8 status; + int ret; + + ret = spinand_load_page_op(spinand, req); + if (ret) + return ret; + + ret = spinand_wait(spinand, &status); + if (ret < 0) + return ret; + + ret = spinand_read_from_cache_op(spinand, req); + if (ret) + return ret; + + if (!ecc_enabled) + return 0; + + return spinand_check_ecc_status(spinand, status); +} + +static int spinand_write_page(struct spinand_device *spinand, + const struct nand_page_io_req *req) +{ + u8 status; + int ret; + + ret = spinand_write_enable_op(spinand); + if (ret) + return ret; + + ret = spinand_write_to_cache_op(spinand, req); + if (ret) + return ret; + + ret = spinand_program_op(spinand, req); + if (ret) + return ret; + + ret = spinand_wait(spinand, &status); + if (!ret && (status & STATUS_PROG_FAILED)) + ret = -EIO; + + return ret; +} + +static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + struct nand_device *nand = mtd_to_nanddev(mtd); + unsigned int max_bitflips = 0; + struct nand_io_iter iter; + bool enable_ecc = false; + bool ecc_failed = false; + int ret = 0; + + if (ops->mode != MTD_OPS_RAW && spinand->eccinfo.ooblayout) + enable_ecc = true; + + mutex_lock(&spinand->lock); + + nanddev_io_for_each_page(nand, from, ops, &iter) { + ret = spinand_select_target(spinand, iter.req.pos.target); + if (ret) + break; + + ret = spinand_ecc_enable(spinand, enable_ecc); + if (ret) + break; + + ret = spinand_read_page(spinand, &iter.req, enable_ecc); + if (ret < 0 && ret != -EBADMSG) + break; + + if (ret == -EBADMSG) { + ecc_failed = true; + mtd->ecc_stats.failed++; + ret = 0; + } else { + mtd->ecc_stats.corrected += ret; + max_bitflips = max_t(unsigned int, max_bitflips, ret); + } + + ops->retlen += iter.req.datalen; + ops->oobretlen += iter.req.ooblen; + } + + mutex_unlock(&spinand->lock); + + if (ecc_failed && !ret) + ret = -EBADMSG; + + return ret ? ret : max_bitflips; +} + +static int spinand_mtd_write(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + struct nand_device *nand = mtd_to_nanddev(mtd); + struct nand_io_iter iter; + bool enable_ecc = false; + int ret = 0; + + if (ops->mode != MTD_OPS_RAW && mtd->ooblayout) + enable_ecc = true; + + mutex_lock(&spinand->lock); + + nanddev_io_for_each_page(nand, to, ops, &iter) { + ret = spinand_select_target(spinand, iter.req.pos.target); + if (ret) + break; + + ret = spinand_ecc_enable(spinand, enable_ecc); + if (ret) + break; + + ret = spinand_write_page(spinand, &iter.req); + if (ret) + break; + + ops->retlen += iter.req.datalen; + ops->oobretlen += iter.req.ooblen; + } + + mutex_unlock(&spinand->lock); + + return ret; +} + +static bool spinand_isbad(struct nand_device *nand, const struct nand_pos *pos) +{ + struct spinand_device *spinand = nand_to_spinand(nand); + struct nand_page_io_req req = { + .pos = *pos, + .ooblen = 2, + .ooboffs = 0, + .oobbuf.in = spinand->oobbuf, + .mode = MTD_OPS_RAW, + }; + + memset(spinand->oobbuf, 0, 2); + spinand_select_target(spinand, pos->target); + spinand_read_page(spinand, &req, false); + if (spinand->oobbuf[0] != 0xff || spinand->oobbuf[1] != 0xff) + return true; + + return false; +} + +static int spinand_mtd_block_isbad(struct mtd_info *mtd, loff_t offs) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + struct spinand_device *spinand = nand_to_spinand(nand); + struct nand_pos pos; + int ret; + + nanddev_offs_to_pos(nand, offs, &pos); + mutex_lock(&spinand->lock); + ret = nanddev_isbad(nand, &pos); + mutex_unlock(&spinand->lock); + + return ret; +} + +static int spinand_markbad(struct nand_device *nand, const struct nand_pos *pos) +{ + struct spinand_device *spinand = nand_to_spinand(nand); + struct nand_page_io_req req = { + .pos = *pos, + .ooboffs = 0, + .ooblen = 2, + .oobbuf.out = spinand->oobbuf, + }; + int ret; + + /* Erase block before marking it bad. */ + ret = spinand_select_target(spinand, pos->target); + if (ret) + return ret; + + ret = spinand_write_enable_op(spinand); + if (ret) + return ret; + + spinand_erase_op(spinand, pos); + + memset(spinand->oobbuf, 0, 2); + return spinand_write_page(spinand, &req); +} + +static int spinand_mtd_block_markbad(struct mtd_info *mtd, loff_t offs) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + struct spinand_device *spinand = nand_to_spinand(nand); + struct nand_pos pos; + int ret; + + nanddev_offs_to_pos(nand, offs, &pos); + mutex_lock(&spinand->lock); + ret = nanddev_markbad(nand, &pos); + mutex_unlock(&spinand->lock); + + return ret; +} + +static int spinand_erase(struct nand_device *nand, const struct nand_pos *pos) +{ + struct spinand_device *spinand = nand_to_spinand(nand); + u8 status; + int ret; + + ret = spinand_select_target(spinand, pos->target); + if (ret) + return ret; + + ret = spinand_write_enable_op(spinand); + if (ret) + return ret; + + ret = spinand_erase_op(spinand, pos); + if (ret) + return ret; + + ret = spinand_wait(spinand, &status); + if (!ret && (status & STATUS_ERASE_FAILED)) + ret = -EIO; + + return ret; +} + +static int spinand_mtd_erase(struct mtd_info *mtd, + struct erase_info *einfo) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + int ret; + + mutex_lock(&spinand->lock); + ret = nanddev_mtd_erase(mtd, einfo); + mutex_unlock(&spinand->lock); + + return ret; +} + +static int spinand_mtd_block_isreserved(struct mtd_info *mtd, loff_t offs) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + struct nand_device *nand = mtd_to_nanddev(mtd); + struct nand_pos pos; + int ret; + + nanddev_offs_to_pos(nand, offs, &pos); + mutex_lock(&spinand->lock); + ret = nanddev_isreserved(nand, &pos); + mutex_unlock(&spinand->lock); + + return ret; +} + +static const struct nand_ops spinand_ops = { + .erase = spinand_erase, + .markbad = spinand_markbad, + .isbad = spinand_isbad, +}; + +static int spinand_manufacturer_detect(struct spinand_device *spinand) +{ + return -ENOTSUPP; +} + +static int spinand_manufacturer_init(struct spinand_device *spinand) +{ + if (spinand->manufacturer->ops->init) + return spinand->manufacturer->ops->init(spinand); + + return 0; +} + +static void spinand_manufacturer_cleanup(struct spinand_device *spinand) +{ + /* Release manufacturer private data */ + if (spinand->manufacturer->ops->cleanup) + return spinand->manufacturer->ops->cleanup(spinand); +} + +static const struct spi_mem_op * +spinand_select_op_variant(struct spinand_device *spinand, + const struct spinand_op_variants *variants) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int i; + + for (i = 0; i < variants->nops; i++) { + struct spi_mem_op op = variants->ops[i]; + unsigned int nbytes; + int ret; + + nbytes = nanddev_per_page_oobsize(nand) + + nanddev_page_size(nand); + + while (nbytes) { + op.data.nbytes = nbytes; + ret = spi_mem_adjust_op_size(spinand->spimem, &op); + if (ret) + break; + + if (!spi_mem_supports_op(spinand->spimem, &op)) + break; + + nbytes -= op.data.nbytes; + } + + if (!nbytes) + return &variants->ops[i]; + } + + return NULL; +} + +/** + * spinand_match_and_init() - Try to find a match between a device ID and an + * entry in a spinand_info table + * @spinand: SPI NAND object + * @table: SPI NAND device description table + * @table_size: size of the device description table + * + * Should be used by SPI NAND manufacturer drivers when they want to find a + * match between a device ID retrieved through the READ_ID command and an + * entry in the SPI NAND description table. If a match is found, the spinand + * object will be initialized with information provided by the matching + * spinand_info entry. + * + * Return: 0 on success, a negative error code otherwise. + */ +int spinand_match_and_init(struct spinand_device *spinand, + const struct spinand_info *table, + unsigned int table_size, u8 devid) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int i; + + for (i = 0; i < table_size; i++) { + const struct spinand_info *info = &table[i]; + const struct spi_mem_op *op; + + if (devid != info->devid) + continue; + + nand->memorg = table[i].memorg; + nand->eccreq = table[i].eccreq; + spinand->eccinfo = table[i].eccinfo; + spinand->flags = table[i].flags; + spinand->select_target = table[i].select_target; + + op = spinand_select_op_variant(spinand, + info->op_variants.read_cache); + if (!op) + return -ENOTSUPP; + + spinand->op_templates.read_cache = op; + + op = spinand_select_op_variant(spinand, + info->op_variants.write_cache); + if (!op) + return -ENOTSUPP; + + spinand->op_templates.write_cache = op; + + op = spinand_select_op_variant(spinand, + info->op_variants.update_cache); + spinand->op_templates.update_cache = op; + + return 0; + } + + return -ENOTSUPP; +} + +static int spinand_detect(struct spinand_device *spinand) +{ + struct device *dev = &spinand->spimem->spi->dev; + struct nand_device *nand = spinand_to_nand(spinand); + int ret; + + ret = spinand_reset_op(spinand); + if (ret) + return ret; + + ret = spinand_read_id_op(spinand, spinand->id.data); + if (ret) + return ret; + + spinand->id.len = SPINAND_MAX_ID_LEN; + + ret = spinand_manufacturer_detect(spinand); + if (ret) { + dev_err(dev, "unknown raw ID %*phN\n", SPINAND_MAX_ID_LEN, + spinand->id.data); + return ret; + } + + if (nand->memorg.ntargets > 1 && !spinand->select_target) { + dev_err(dev, + "SPI NANDs with more than one die must implement ->select_target()\n"); + return -EINVAL; + } + + dev_info(&spinand->spimem->spi->dev, + "%s SPI NAND was found.\n", spinand->manufacturer->name); + dev_info(&spinand->spimem->spi->dev, + "%llu MiB, block size: %zu KiB, page size: %zu, OOB size: %u\n", + nanddev_size(nand) >> 20, nanddev_eraseblock_size(nand) >> 10, + nanddev_page_size(nand), nanddev_per_page_oobsize(nand)); + + return 0; +} + +static int spinand_noecc_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + return -ERANGE; +} + +static int spinand_noecc_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + /* Reserve 2 bytes for the BBM. */ + region->offset = 2; + region->length = 62; + + return 0; +} + +static const struct mtd_ooblayout_ops spinand_noecc_ooblayout = { + .ecc = spinand_noecc_ooblayout_ecc, + .free = spinand_noecc_ooblayout_free, +}; + +static int spinand_init(struct spinand_device *spinand) +{ + struct device *dev = &spinand->spimem->spi->dev; + struct mtd_info *mtd = spinand_to_mtd(spinand); + struct nand_device *nand = mtd_to_nanddev(mtd); + int ret, i; + + /* + * We need a scratch buffer because the spi_mem interface requires that + * buf passed in spi_mem_op->data.buf be DMA-able. + */ + spinand->scratchbuf = kzalloc(SPINAND_MAX_ID_LEN, GFP_KERNEL); + if (!spinand->scratchbuf) + return -ENOMEM; + + ret = spinand_detect(spinand); + if (ret) + goto err_free_bufs; + + /* + * Use kzalloc() instead of devm_kzalloc() here, because some drivers + * may use this buffer for DMA access. + * Memory allocated by devm_ does not guarantee DMA-safe alignment. + */ + spinand->databuf = kzalloc(nanddev_page_size(nand) + + nanddev_per_page_oobsize(nand), + GFP_KERNEL); + if (!spinand->databuf) { + ret = -ENOMEM; + goto err_free_bufs; + } + + spinand->oobbuf = spinand->databuf + nanddev_page_size(nand); + + ret = spinand_init_cfg_cache(spinand); + if (ret) + goto err_free_bufs; + + ret = spinand_init_quad_enable(spinand); + if (ret) + goto err_free_bufs; + + ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0); + if (ret) + goto err_free_bufs; + + ret = spinand_manufacturer_init(spinand); + if (ret) { + dev_err(dev, + "Failed to initialize the SPI NAND chip (err = %d)\n", + ret); + goto err_free_bufs; + } + + /* After power up, all blocks are locked, so unlock them here. */ + for (i = 0; i < nand->memorg.ntargets; i++) { + ret = spinand_select_target(spinand, i); + if (ret) + goto err_free_bufs; + + ret = spinand_lock_block(spinand, BL_ALL_UNLOCKED); + if (ret) + goto err_free_bufs; + } + + ret = nanddev_init(nand, &spinand_ops, THIS_MODULE); + if (ret) + goto err_manuf_cleanup; + + /* + * Right now, we don't support ECC, so let the whole oob + * area is available for user. + */ + mtd->_read_oob = spinand_mtd_read; + mtd->_write_oob = spinand_mtd_write; + mtd->_block_isbad = spinand_mtd_block_isbad; + mtd->_block_markbad = spinand_mtd_block_markbad; + mtd->_block_isreserved = spinand_mtd_block_isreserved; + mtd->_erase = spinand_mtd_erase; + + if (spinand->eccinfo.ooblayout) + mtd_set_ooblayout(mtd, spinand->eccinfo.ooblayout); + else + mtd_set_ooblayout(mtd, &spinand_noecc_ooblayout); + + ret = mtd_ooblayout_count_freebytes(mtd); + if (ret < 0) + goto err_cleanup_nanddev; + + mtd->oobavail = ret; + + return 0; + +err_cleanup_nanddev: + nanddev_cleanup(nand); + +err_manuf_cleanup: + spinand_manufacturer_cleanup(spinand); + +err_free_bufs: + kfree(spinand->databuf); + kfree(spinand->scratchbuf); + return ret; +} + +static void spinand_cleanup(struct spinand_device *spinand) +{ + struct nand_device *nand = spinand_to_nand(spinand); + + nanddev_cleanup(nand); + spinand_manufacturer_cleanup(spinand); + kfree(spinand->databuf); + kfree(spinand->scratchbuf); +} + +static int spinand_probe(struct spi_mem *mem) +{ + struct spinand_device *spinand; + struct mtd_info *mtd; + int ret; + + spinand = devm_kzalloc(&mem->spi->dev, sizeof(*spinand), + GFP_KERNEL); + if (!spinand) + return -ENOMEM; + + spinand->spimem = mem; + spi_mem_set_drvdata(mem, spinand); + spinand_set_of_node(spinand, mem->spi->dev.of_node); + mutex_init(&spinand->lock); + mtd = spinand_to_mtd(spinand); + mtd->dev.parent = &mem->spi->dev; + + ret = spinand_init(spinand); + if (ret) + return ret; + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) + goto err_spinand_cleanup; + + return 0; + +err_spinand_cleanup: + spinand_cleanup(spinand); + + return ret; +} + +static int spinand_remove(struct spi_mem *mem) +{ + struct spinand_device *spinand; + struct mtd_info *mtd; + int ret; + + spinand = spi_mem_get_drvdata(mem); + mtd = spinand_to_mtd(spinand); + + ret = mtd_device_unregister(mtd); + if (ret) + return ret; + + spinand_cleanup(spinand); + + return 0; +} + +static const struct spi_device_id spinand_ids[] = { + { .name = "spi-nand" }, + { /* sentinel */ }, +}; + +#ifdef CONFIG_OF +static const struct of_device_id spinand_of_ids[] = { + { .compatible = "spi-nand" }, + { /* sentinel */ }, +}; +#endif + +static struct spi_mem_driver spinand_drv = { + .spidrv = { + .id_table = spinand_ids, + .driver = { + .name = "spi-nand", + .of_match_table = of_match_ptr(spinand_of_ids), + }, + }, + .probe = spinand_probe, + .remove = spinand_remove, +}; +module_spi_mem_driver(spinand_drv); + +MODULE_DESCRIPTION("SPI NAND framework"); +MODULE_AUTHOR("Peter Pan"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h new file mode 100644 index 000000000000..d3efe62dc9de --- /dev/null +++ b/include/linux/mtd/spinand.h @@ -0,0 +1,416 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016-2017 Micron Technology, Inc. + * + * Authors: + * Peter Pan + */ +#ifndef __LINUX_MTD_SPINAND_H +#define __LINUX_MTD_SPINAND_H + +#include +#include +#include +#include +#include +#include +#include + +/** + * Standard SPI NAND flash operations + */ + +#define SPINAND_RESET_OP \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xff, 1), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_WR_EN_DIS_OP(enable) \ + SPI_MEM_OP(SPI_MEM_OP_CMD((enable) ? 0x06 : 0x04, 1), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_READID_OP(ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x9f, 1), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 1)) + +#define SPINAND_SET_FEATURE_OP(reg, valptr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x1f, 1), \ + SPI_MEM_OP_ADDR(1, reg, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(1, valptr, 1)) + +#define SPINAND_GET_FEATURE_OP(reg, valptr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x0f, 1), \ + SPI_MEM_OP_ADDR(1, reg, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_IN(1, valptr, 1)) + +#define SPINAND_BLK_ERASE_OP(addr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xd8, 1), \ + SPI_MEM_OP_ADDR(3, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_PAGE_READ_OP(addr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x13, 1), \ + SPI_MEM_OP_ADDR(3, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_PAGE_READ_FROM_CACHE_OP(fast, addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 1)) + +#define SPINAND_PAGE_READ_FROM_CACHE_X2_OP(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 2)) + +#define SPINAND_PAGE_READ_FROM_CACHE_X4_OP(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 4)) + +#define SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1), \ + SPI_MEM_OP_ADDR(2, addr, 2), \ + SPI_MEM_OP_DUMMY(ndummy, 2), \ + SPI_MEM_OP_DATA_IN(len, buf, 2)) + +#define SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1), \ + SPI_MEM_OP_ADDR(2, addr, 4), \ + SPI_MEM_OP_DUMMY(ndummy, 4), \ + SPI_MEM_OP_DATA_IN(len, buf, 4)) + +#define SPINAND_PROG_EXEC_OP(addr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x10, 1), \ + SPI_MEM_OP_ADDR(3, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_PROG_LOAD(reset, addr, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x02 : 0x84, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(len, buf, 1)) + +#define SPINAND_PROG_LOAD_X4(reset, addr, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x32 : 0x34, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(len, buf, 4)) + +/** + * Standard SPI NAND flash commands + */ +#define SPINAND_CMD_PROG_LOAD_X4 0x32 +#define SPINAND_CMD_PROG_LOAD_RDM_DATA_X4 0x34 + +/* feature register */ +#define REG_BLOCK_LOCK 0xa0 +#define BL_ALL_UNLOCKED 0x00 + +/* configuration register */ +#define REG_CFG 0xb0 +#define CFG_OTP_ENABLE BIT(6) +#define CFG_ECC_ENABLE BIT(4) +#define CFG_QUAD_ENABLE BIT(0) + +/* status register */ +#define REG_STATUS 0xc0 +#define STATUS_BUSY BIT(0) +#define STATUS_ERASE_FAILED BIT(2) +#define STATUS_PROG_FAILED BIT(3) +#define STATUS_ECC_MASK GENMASK(5, 4) +#define STATUS_ECC_NO_BITFLIPS (0 << 4) +#define STATUS_ECC_HAS_BITFLIPS (1 << 4) +#define STATUS_ECC_UNCOR_ERROR (2 << 4) + +struct spinand_op; +struct spinand_device; + +#define SPINAND_MAX_ID_LEN 4 + +/** + * struct spinand_id - SPI NAND id structure + * @data: buffer containing the id bytes. Currently 4 bytes large, but can + * be extended if required + * @len: ID length + * + * struct_spinand_id->data contains all bytes returned after a READ_ID command, + * including dummy bytes if the chip does not emit ID bytes right after the + * READ_ID command. The responsibility to extract real ID bytes is left to + * struct_manufacurer_ops->detect(). + */ +struct spinand_id { + u8 data[SPINAND_MAX_ID_LEN]; + int len; +}; + +/** + * struct manufacurer_ops - SPI NAND manufacturer specific operations + * @detect: detect a SPI NAND device. Every time a SPI NAND device is probed + * the core calls the struct_manufacurer_ops->detect() hook of each + * registered manufacturer until one of them return 1. Note that + * the first thing to check in this hook is that the manufacturer ID + * in struct_spinand_device->id matches the manufacturer whose + * ->detect() hook has been called. Should return 1 if there's a + * match, 0 if the manufacturer ID does not match and a negative + * error code otherwise. When true is returned, the core assumes + * that properties of the NAND chip (spinand->base.memorg and + * spinand->base.eccreq) have been filled + * @init: initialize a SPI NAND device + * @cleanup: cleanup a SPI NAND device + * + * Each SPI NAND manufacturer driver should implement this interface so that + * NAND chips coming from this vendor can be detected and initialized properly. + */ +struct spinand_manufacturer_ops { + int (*detect)(struct spinand_device *spinand); + int (*init)(struct spinand_device *spinand); + void (*cleanup)(struct spinand_device *spinand); +}; + +/** + * struct spinand_manufacturer - SPI NAND manufacturer instance + * @id: manufacturer ID + * @name: manufacturer name + * @ops: manufacturer operations + */ +struct spinand_manufacturer { + u8 id; + char *name; + const struct spinand_manufacturer_ops *ops; +}; + +/** + * struct spinand_op_variants - SPI NAND operation variants + * @ops: the list of variants for a given operation + * @nops: the number of variants + * + * Some operations like read-from-cache/write-to-cache have several variants + * depending on the number of IO lines you use to transfer data or address + * cycles. This structure is a way to describe the different variants supported + * by a chip and let the core pick the best one based on the SPI mem controller + * capabilities. + */ +struct spinand_op_variants { + const struct spi_mem_op *ops; + unsigned int nops; +}; + +#define SPINAND_OP_VARIANTS(name, ...) \ + const struct spinand_op_variants name = { \ + .ops = (struct spi_mem_op[]) { __VA_ARGS__ }, \ + .nops = sizeof((struct spi_mem_op[]){ __VA_ARGS__ }) / \ + sizeof(struct spi_mem_op), \ + } + +/** + * spinand_ecc_info - description of the on-die ECC implemented by a SPI NAND + * chip + * @get_status: get the ECC status. Should return a positive number encoding + * the number of corrected bitflips if correction was possible or + * -EBADMSG if there are uncorrectable errors. I can also return + * other negative error codes if the error is not caused by + * uncorrectable bitflips + * @ooblayout: the OOB layout used by the on-die ECC implementation + */ +struct spinand_ecc_info { + int (*get_status)(struct spinand_device *spinand, u8 status); + const struct mtd_ooblayout_ops *ooblayout; +}; + +#define SPINAND_HAS_QE_BIT BIT(0) + +/** + * struct spinand_info - Structure used to describe SPI NAND chips + * @model: model name + * @devid: device ID + * @flags: OR-ing of the SPINAND_XXX flags + * @memorg: memory organization + * @eccreq: ECC requirements + * @eccinfo: on-die ECC info + * @op_variants: operations variants + * @op_variants.read_cache: variants of the read-cache operation + * @op_variants.write_cache: variants of the write-cache operation + * @op_variants.update_cache: variants of the update-cache operation + * @select_target: function used to select a target/die. Required only for + * multi-die chips + * + * Each SPI NAND manufacturer driver should have a spinand_info table + * describing all the chips supported by the driver. + */ +struct spinand_info { + const char *model; + u8 devid; + u32 flags; + struct nand_memory_organization memorg; + struct nand_ecc_req eccreq; + struct spinand_ecc_info eccinfo; + struct { + const struct spinand_op_variants *read_cache; + const struct spinand_op_variants *write_cache; + const struct spinand_op_variants *update_cache; + } op_variants; + int (*select_target)(struct spinand_device *spinand, + unsigned int target); +}; + +#define SPINAND_INFO_OP_VARIANTS(__read, __write, __update) \ + { \ + .read_cache = __read, \ + .write_cache = __write, \ + .update_cache = __update, \ + } + +#define SPINAND_ECCINFO(__ooblayout, __get_status) \ + .eccinfo = { \ + .ooblayout = __ooblayout, \ + .get_status = __get_status, \ + } + +#define SPINAND_SELECT_TARGET(__func) \ + .select_target = __func, + +#define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants, \ + __flags, ...) \ + { \ + .model = __model, \ + .devid = __id, \ + .memorg = __memorg, \ + .eccreq = __eccreq, \ + .op_variants = __op_variants, \ + .flags = __flags, \ + __VA_ARGS__ \ + } + +/** + * struct spinand_device - SPI NAND device instance + * @base: NAND device instance + * @spimem: pointer to the SPI mem object + * @lock: lock used to serialize accesses to the NAND + * @id: NAND ID as returned by READ_ID + * @flags: NAND flags + * @op_templates: various SPI mem op templates + * @op_templates.read_cache: read cache op template + * @op_templates.write_cache: write cache op template + * @op_templates.update_cache: update cache op template + * @select_target: select a specific target/die. Usually called before sending + * a command addressing a page or an eraseblock embedded in + * this die. Only required if your chip exposes several dies + * @cur_target: currently selected target/die + * @eccinfo: on-die ECC information + * @cfg_cache: config register cache. One entry per die + * @databuf: bounce buffer for data + * @oobbuf: bounce buffer for OOB data + * @scratchbuf: buffer used for everything but page accesses. This is needed + * because the spi-mem interface explicitly requests that buffers + * passed in spi_mem_op be DMA-able, so we can't based the bufs on + * the stack + * @manufacturer: SPI NAND manufacturer information + * @priv: manufacturer private data + */ +struct spinand_device { + struct nand_device base; + struct spi_mem *spimem; + struct mutex lock; + struct spinand_id id; + u32 flags; + + struct { + const struct spi_mem_op *read_cache; + const struct spi_mem_op *write_cache; + const struct spi_mem_op *update_cache; + } op_templates; + + int (*select_target)(struct spinand_device *spinand, + unsigned int target); + unsigned int cur_target; + + struct spinand_ecc_info eccinfo; + + u8 *cfg_cache; + u8 *databuf; + u8 *oobbuf; + u8 *scratchbuf; + const struct spinand_manufacturer *manufacturer; + void *priv; +}; + +/** + * mtd_to_spinand() - Get the SPI NAND device attached to an MTD instance + * @mtd: MTD instance + * + * Return: the SPI NAND device attached to @mtd. + */ +static inline struct spinand_device *mtd_to_spinand(struct mtd_info *mtd) +{ + return container_of(mtd_to_nanddev(mtd), struct spinand_device, base); +} + +/** + * spinand_to_mtd() - Get the MTD device embedded in a SPI NAND device + * @spinand: SPI NAND device + * + * Return: the MTD device embedded in @spinand. + */ +static inline struct mtd_info *spinand_to_mtd(struct spinand_device *spinand) +{ + return nanddev_to_mtd(&spinand->base); +} + +/** + * nand_to_spinand() - Get the SPI NAND device embedding an NAND object + * @nand: NAND object + * + * Return: the SPI NAND device embedding @nand. + */ +static inline struct spinand_device *nand_to_spinand(struct nand_device *nand) +{ + return container_of(nand, struct spinand_device, base); +} + +/** + * spinand_to_nand() - Get the NAND device embedded in a SPI NAND object + * @spinand: SPI NAND device + * + * Return: the NAND device embedded in @spinand. + */ +static inline struct nand_device * +spinand_to_nand(struct spinand_device *spinand) +{ + return &spinand->base; +} + +/** + * spinand_set_of_node - Attach a DT node to a SPI NAND device + * @spinand: SPI NAND device + * @np: DT node + * + * Attach a DT node to a SPI NAND device. + */ +static inline void spinand_set_of_node(struct spinand_device *spinand, + struct device_node *np) +{ + nanddev_set_of_node(&spinand->base, np); +} + +int spinand_match_and_init(struct spinand_device *dev, + const struct spinand_info *table, + unsigned int table_size, u8 devid); + +int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val); +int spinand_select_target(struct spinand_device *spinand, unsigned int target); + +#endif /* __LINUX_MTD_SPINAND_H */ diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h index bb4bd15ae1f6..4fa34a227a0f 100644 --- a/include/linux/spi/spi-mem.h +++ b/include/linux/spi/spi-mem.h @@ -3,7 +3,9 @@ * Copyright (C) 2018 Exceet Electronics GmbH * Copyright (C) 2018 Bootlin * - * Author: Boris Brezillon + * Author: + * Peter Pan + * Boris Brezillon */ #ifndef __LINUX_SPI_MEM_H -- cgit v1.2.3 From a508e8875e135d7a1df26d8131b5443cb07005ff Mon Sep 17 00:00:00 2001 From: Peter Pan Date: Fri, 22 Jun 2018 14:28:25 +0200 Subject: mtd: spinand: Add initial support for Micron MT29F2G01ABAGD Add a basic driver for Micron SPI NANDs. Only one device is supported right now, but the driver will be extended to support more devices afterwards. Signed-off-by: Peter Pan Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/core.c | 17 ++++++ drivers/mtd/nand/spi/micron.c | 133 ++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 3 + 4 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/micron.c (limited to 'drivers') diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index 2c473b765027..a1df25398f20 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o +spinand-objs := core.o micron.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 4efbeb0d85b4..9cc502d7e745 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -763,8 +763,25 @@ static const struct nand_ops spinand_ops = { .isbad = spinand_isbad, }; +static const struct spinand_manufacturer *spinand_manufacturers[] = { + µn_spinand_manufacturer, +}; + static int spinand_manufacturer_detect(struct spinand_device *spinand) { + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(spinand_manufacturers); i++) { + ret = spinand_manufacturers[i]->ops->detect(spinand); + if (ret > 0) { + spinand->manufacturer = spinand_manufacturers[i]; + return 0; + } else if (ret < 0) { + return ret; + } + } + return -ENOTSUPP; } diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c new file mode 100644 index 000000000000..9c4381d6847b --- /dev/null +++ b/drivers/mtd/nand/spi/micron.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016-2017 Micron Technology, Inc. + * + * Authors: + * Peter Pan + */ + +#include +#include +#include + +#define SPINAND_MFR_MICRON 0x2c + +#define MICRON_STATUS_ECC_MASK GENMASK(7, 4) +#define MICRON_STATUS_ECC_NO_BITFLIPS (0 << 4) +#define MICRON_STATUS_ECC_1TO3_BITFLIPS (1 << 4) +#define MICRON_STATUS_ECC_4TO6_BITFLIPS (3 << 4) +#define MICRON_STATUS_ECC_7TO8_BITFLIPS (5 << 4) + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static int mt29f2g01abagd_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + region->offset = 64; + region->length = 64; + + return 0; +} + +static int mt29f2g01abagd_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + /* Reserve 2 bytes for the BBM. */ + region->offset = 2; + region->length = 62; + + return 0; +} + +static const struct mtd_ooblayout_ops mt29f2g01abagd_ooblayout = { + .ecc = mt29f2g01abagd_ooblayout_ecc, + .free = mt29f2g01abagd_ooblayout_free, +}; + +static int mt29f2g01abagd_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + switch (status & MICRON_STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + case MICRON_STATUS_ECC_1TO3_BITFLIPS: + return 3; + + case MICRON_STATUS_ECC_4TO6_BITFLIPS: + return 6; + + case MICRON_STATUS_ECC_7TO8_BITFLIPS: + return 8; + + default: + break; + } + + return -EINVAL; +} + +static const struct spinand_info micron_spinand_table[] = { + SPINAND_INFO("MT29F2G01ABAGD", 0x24, + NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&mt29f2g01abagd_ooblayout, + mt29f2g01abagd_ecc_get_status)), +}; + +static int micron_spinand_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + /* + * Micron SPI NAND read ID need a dummy byte, + * so the first byte in raw_id is dummy. + */ + if (id[1] != SPINAND_MFR_MICRON) + return 0; + + ret = spinand_match_and_init(spinand, micron_spinand_table, + ARRAY_SIZE(micron_spinand_table), id[2]); + if (ret) + return ret; + + return 1; +} + +static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { + .detect = micron_spinand_detect, +}; + +const struct spinand_manufacturer micron_spinand_manufacturer = { + .id = SPINAND_MFR_MICRON, + .name = "Micron", + .ops = µn_spinand_manuf_ops, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index d3efe62dc9de..717b272940f1 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -193,6 +193,9 @@ struct spinand_manufacturer { const struct spinand_manufacturer_ops *ops; }; +/* SPI NAND manufacturers */ +extern const struct spinand_manufacturer micron_spinand_manufacturer; + /** * struct spinand_op_variants - SPI NAND operation variants * @ops: the list of variants for a given operation -- cgit v1.2.3 From 1075492bb9e26312bc8ddeec1a93e2de5f9c76b4 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Fri, 22 Jun 2018 14:28:26 +0200 Subject: mtd: spinand: Add initial support for Winbond W25M02GV Add support for the W25M02GV chip. Signed-off-by: Frieder Schrempf Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/core.c | 1 + drivers/mtd/nand/spi/winbond.c | 141 +++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 1 + 4 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/winbond.c (limited to 'drivers') diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index a1df25398f20..100008d202ed 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o micron.o +spinand-objs := core.o micron.o winbond.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 9cc502d7e745..4803a6bfe8ec 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -765,6 +765,7 @@ static const struct nand_ops spinand_ops = { static const struct spinand_manufacturer *spinand_manufacturers[] = { µn_spinand_manufacturer, + &winbond_spinand_manufacturer, }; static int spinand_manufacturer_detect(struct spinand_device *spinand) diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c new file mode 100644 index 000000000000..67baa1b32c00 --- /dev/null +++ b/drivers/mtd/nand/spi/winbond.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 exceet electronics GmbH + * + * Authors: + * Frieder Schrempf + * Boris Brezillon + */ + +#include +#include +#include + +#define SPINAND_MFR_WINBOND 0xEF + +#define WINBOND_CFG_BUF_READ BIT(3) + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section) + 8; + region->length = 8; + + return 0; +} + +static int w25m02gv_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section) + 2; + region->length = 6; + + return 0; +} + +static const struct mtd_ooblayout_ops w25m02gv_ooblayout = { + .ecc = w25m02gv_ooblayout_ecc, + .free = w25m02gv_ooblayout_free, +}; + +static int w25m02gv_select_target(struct spinand_device *spinand, + unsigned int target) +{ + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, + spinand->scratchbuf, + 1)); + + *spinand->scratchbuf = target; + return spi_mem_exec_op(spinand->spimem, &op); +} + +static const struct spinand_info winbond_spinand_table[] = { + SPINAND_INFO("W25M02GV", 0xAB, + NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 2), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), + SPINAND_SELECT_TARGET(w25m02gv_select_target)), +}; + +/** + * winbond_spinand_detect - initialize device related part in spinand_device + * struct if it is a Winbond device. + * @spinand: SPI NAND device structure + */ +static int winbond_spinand_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + /* + * Winbond SPI NAND read ID need a dummy byte, + * so the first byte in raw_id is dummy. + */ + if (id[1] != SPINAND_MFR_WINBOND) + return 0; + + ret = spinand_match_and_init(spinand, winbond_spinand_table, + ARRAY_SIZE(winbond_spinand_table), id[2]); + if (ret) + return ret; + + return 1; +} + +static int winbond_spinand_init(struct spinand_device *spinand) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int i; + + /* + * Make sure all dies are in buffer read mode and not continuous read + * mode. + */ + for (i = 0; i < nand->memorg.ntargets; i++) { + spinand_select_target(spinand, i); + spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ, + WINBOND_CFG_BUF_READ); + } + + return 0; +} + +static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { + .detect = winbond_spinand_detect, + .init = winbond_spinand_init, +}; + +const struct spinand_manufacturer winbond_spinand_manufacturer = { + .id = SPINAND_MFR_WINBOND, + .name = "Winbond", + .ops = &winbond_spinand_manuf_ops, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 717b272940f1..f0f16b9029e7 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -195,6 +195,7 @@ struct spinand_manufacturer { /* SPI NAND manufacturers */ extern const struct spinand_manufacturer micron_spinand_manufacturer; +extern const struct spinand_manufacturer winbond_spinand_manufacturer; /** * struct spinand_op_variants - SPI NAND operation variants -- cgit v1.2.3 From b02308af05e62c7d995f4fc75b0bc2ae3c3026f7 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 22 Jun 2018 14:28:27 +0200 Subject: mtd: spinand: Add initial support for the MX35LF1GE4AB chip Add minimal support for the MX35LF1GE4AB SPI NAND chip. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/core.c | 1 + drivers/mtd/nand/spi/macronix.c | 136 ++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 1 + 4 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/macronix.c (limited to 'drivers') diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index 100008d202ed..b74e074b363a 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o micron.o winbond.o +spinand-objs := core.o macronix.o micron.o winbond.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 4803a6bfe8ec..30f83649c481 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -764,6 +764,7 @@ static const struct nand_ops spinand_ops = { }; static const struct spinand_manufacturer *spinand_manufacturers[] = { + ¯onix_spinand_manufacturer, µn_spinand_manufacturer, &winbond_spinand_manufacturer, }; diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c new file mode 100644 index 000000000000..8ba3489d332a --- /dev/null +++ b/drivers/mtd/nand/spi/macronix.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 Macronix + * + * Author: Boris Brezillon + */ + +#include +#include +#include + +#define SPINAND_MFR_MACRONIX 0xC2 + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static int mx35lfxge4ab_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + return -ERANGE; +} + +static int mx35lfxge4ab_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + region->offset = 2; + region->length = mtd->oobsize - 2; + + return 0; +} + +static const struct mtd_ooblayout_ops mx35lfxge4ab_ooblayout = { + .ecc = mx35lfxge4ab_ooblayout_ecc, + .free = mx35lfxge4ab_ooblayout_free, +}; + +static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr) +{ + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_DUMMY(1, 1), + SPI_MEM_OP_DATA_IN(1, eccsr, 1)); + + return spi_mem_exec_op(spinand->spimem, &op); +} + +static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + struct nand_device *nand = spinand_to_nand(spinand); + u8 eccsr; + + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + case STATUS_ECC_HAS_BITFLIPS: + /* + * Let's try to retrieve the real maximum number of bitflips + * in order to avoid forcing the wear-leveling layer to move + * data around if it's not necessary. + */ + if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr)) + return nand->eccreq.strength; + + if (WARN_ON(eccsr > nand->eccreq.strength || !eccsr)) + return nand->eccreq.strength; + + return eccsr; + + default: + break; + } + + return -EINVAL; +} + +static const struct spinand_info macronix_spinand_table[] = { + SPINAND_INFO("MX35LF1GE4AB", 0x12, + NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), +}; + +static int macronix_spinand_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + /* + * Macronix SPI NAND read ID needs a dummy byte, so the first byte in + * raw_id is garbage. + */ + if (id[1] != SPINAND_MFR_MACRONIX) + return 0; + + ret = spinand_match_and_init(spinand, macronix_spinand_table, + ARRAY_SIZE(macronix_spinand_table), + id[2]); + if (ret) + return ret; + + return 1; +} + +static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { + .detect = macronix_spinand_detect, +}; + +const struct spinand_manufacturer macronix_spinand_manufacturer = { + .id = SPINAND_MFR_MACRONIX, + .name = "Macronix", + .ops = ¯onix_spinand_manuf_ops, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index f0f16b9029e7..088ff96c3eb6 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -194,6 +194,7 @@ struct spinand_manufacturer { }; /* SPI NAND manufacturers */ +extern const struct spinand_manufacturer macronix_spinand_manufacturer; extern const struct spinand_manufacturer micron_spinand_manufacturer; extern const struct spinand_manufacturer winbond_spinand_manufacturer; -- cgit v1.2.3 From 3dfa025f890c4568adc9ba06ecc0da35718f4799 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 22 Jun 2018 14:28:28 +0200 Subject: mtd: spinand: macronix: Add support for MX35LF2GE4AB MX35LF2GE4AB is almost identical to MX35LF1GE4AB except it has 2 times more eraseblocks per LUN and exposes 2 planes instead of 1. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/spi/macronix.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index 8ba3489d332a..98f6b9c4b684 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -102,6 +102,14 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35LF2GE4AB", 0x22, + NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), }; static int macronix_spinand_detect(struct spinand_device *spinand) -- cgit v1.2.3 From 24f0ae995deb728076e3ea93fea1949a9775debf Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Wed, 27 Jun 2018 22:47:44 +0200 Subject: mtd: rawnand: mxc: remove __init qualifier from mxcnd_probe_dt Using the sysfs unbind, bind nodes, mxcnd_probe and mxcnd_probe_dt can potentially be called at any time. After the __init functions are cleaned, mxcnd_probe_dt is no longer available. Calling it anyway causes a crash. mxcnd_probe used to be marked as __init, this was removed years ago. Remove the __init qualifier from from mxcnd_probe_dt as well. Fixes: 06f255106923 ("mtd: remove use of __devinit") Signed-off-by: Martin Kaiser Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/mxc_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index 26cef218bb43..90cfb5e730aa 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -1686,7 +1686,7 @@ static const struct of_device_id mxcnd_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, mxcnd_dt_ids); -static int __init mxcnd_probe_dt(struct mxc_nand_host *host) +static int mxcnd_probe_dt(struct mxc_nand_host *host) { struct device_node *np = host->dev->of_node; const struct of_device_id *of_id = @@ -1700,7 +1700,7 @@ static int __init mxcnd_probe_dt(struct mxc_nand_host *host) return 0; } #else -static int __init mxcnd_probe_dt(struct mxc_nand_host *host) +static int mxcnd_probe_dt(struct mxc_nand_host *host) { return 1; } -- cgit v1.2.3 From cd1beffa83f680f3cb33be4e7cadfdf8399cef7e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 23 Jun 2018 01:06:35 +0900 Subject: mtd: rawnand: denali_dt: use dev as a shorthand of &pdev->dev The probe function references &pdev->dev many times, and I will add more soon. Add 'dev' as a shorthand. Signed-off-by: Masahiro Yamada Reviewed-by: Richard Weinberger Tested-by: Richard Weinberger Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/denali_dt.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c index 5869e90cc14b..6b4bd16e8bee 100644 --- a/drivers/mtd/nand/raw/denali_dt.c +++ b/drivers/mtd/nand/raw/denali_dt.c @@ -79,44 +79,45 @@ MODULE_DEVICE_TABLE(of, denali_nand_dt_ids); static int denali_dt_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct resource *res; struct denali_dt *dt; const struct denali_dt_data *data; struct denali_nand_info *denali; int ret; - dt = devm_kzalloc(&pdev->dev, sizeof(*dt), GFP_KERNEL); + dt = devm_kzalloc(dev, sizeof(*dt), GFP_KERNEL); if (!dt) return -ENOMEM; denali = &dt->denali; - data = of_device_get_match_data(&pdev->dev); + data = of_device_get_match_data(dev); if (data) { denali->revision = data->revision; denali->caps = data->caps; denali->ecc_caps = data->ecc_caps; } - denali->dev = &pdev->dev; + denali->dev = dev; denali->irq = platform_get_irq(pdev, 0); if (denali->irq < 0) { - dev_err(&pdev->dev, "no irq defined\n"); + dev_err(dev, "no irq defined\n"); return denali->irq; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "denali_reg"); - denali->reg = devm_ioremap_resource(&pdev->dev, res); + denali->reg = devm_ioremap_resource(dev, res); if (IS_ERR(denali->reg)) return PTR_ERR(denali->reg); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); - denali->host = devm_ioremap_resource(&pdev->dev, res); + denali->host = devm_ioremap_resource(dev, res); if (IS_ERR(denali->host)) return PTR_ERR(denali->host); - dt->clk = devm_clk_get(&pdev->dev, NULL); + dt->clk = devm_clk_get(dev, NULL); if (IS_ERR(dt->clk)) { - dev_err(&pdev->dev, "no clk available\n"); + dev_err(dev, "no clk available\n"); return PTR_ERR(dt->clk); } ret = clk_prepare_enable(dt->clk); -- cgit v1.2.3 From 6f1fe97bec349a1fd6c5a8c7c5998d759fe721d5 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 23 Jun 2018 01:06:37 +0900 Subject: mtd: rawnand: denali_dt: add more clocks based on IP datasheet Currently, denali_dt.c requires a single anonymous clock, but the Denali User's Guide requires three clocks for this IP: - clk: controller core clock - clk_x: bus interface clock - ecc_clk: clock at which ECC circuitry is run This commit supports these named clocks to represent the real hardware. For the backward compatibility, the driver still accepts a single clock just as before. The clk_x_rate is taken from the clock driver again if the named clock "clk_x" is available. This will happen only for future DT, hence the existing DT files are not affected. Signed-off-by: Masahiro Yamada Reviewed-by: Richard Weinberger Tested-by: Richard Weinberger Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/denali_dt.c | 53 ++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c index 6b4bd16e8bee..afaae378c624 100644 --- a/drivers/mtd/nand/raw/denali_dt.c +++ b/drivers/mtd/nand/raw/denali_dt.c @@ -27,7 +27,9 @@ struct denali_dt { struct denali_nand_info denali; - struct clk *clk; + struct clk *clk; /* core clock */ + struct clk *clk_x; /* bus interface clock */ + struct clk *clk_ecc; /* ECC circuit clock */ }; struct denali_dt_data { @@ -115,28 +117,61 @@ static int denali_dt_probe(struct platform_device *pdev) if (IS_ERR(denali->host)) return PTR_ERR(denali->host); - dt->clk = devm_clk_get(dev, NULL); + /* + * A single anonymous clock is supported for the backward compatibility. + * New platforms should support all the named clocks. + */ + dt->clk = devm_clk_get(dev, "nand"); + if (IS_ERR(dt->clk)) + dt->clk = devm_clk_get(dev, NULL); if (IS_ERR(dt->clk)) { dev_err(dev, "no clk available\n"); return PTR_ERR(dt->clk); } + + dt->clk_x = devm_clk_get(dev, "nand_x"); + if (IS_ERR(dt->clk_x)) + dt->clk_x = NULL; + + dt->clk_ecc = devm_clk_get(dev, "ecc"); + if (IS_ERR(dt->clk_ecc)) + dt->clk_ecc = NULL; + ret = clk_prepare_enable(dt->clk); if (ret) return ret; - /* - * Hardcode the clock rate for the backward compatibility. - * This works for both SOCFPGA and UniPhier. - */ - denali->clk_x_rate = 200000000; + ret = clk_prepare_enable(dt->clk_x); + if (ret) + goto out_disable_clk; + + ret = clk_prepare_enable(dt->clk_ecc); + if (ret) + goto out_disable_clk_x; + + if (dt->clk_x) { + denali->clk_x_rate = clk_get_rate(dt->clk_x); + } else { + /* + * Hardcode the clock rates for the backward compatibility. + * This works for both SOCFPGA and UniPhier. + */ + dev_notice(dev, + "necessary clock is missing. default clock rates are used.\n"); + denali->clk_x_rate = 200000000; + } ret = denali_init(denali); if (ret) - goto out_disable_clk; + goto out_disable_clk_ecc; platform_set_drvdata(pdev, dt); return 0; +out_disable_clk_ecc: + clk_disable_unprepare(dt->clk_ecc); +out_disable_clk_x: + clk_disable_unprepare(dt->clk_x); out_disable_clk: clk_disable_unprepare(dt->clk); @@ -148,6 +183,8 @@ static int denali_dt_remove(struct platform_device *pdev) struct denali_dt *dt = platform_get_drvdata(pdev); denali_remove(&dt->denali); + clk_disable_unprepare(dt->clk_ecc); + clk_disable_unprepare(dt->clk_x); clk_disable_unprepare(dt->clk); return 0; -- cgit v1.2.3 From 1dfac31a5a63ac04a9b5fbc3f5105a586560f191 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 23 Jun 2018 01:06:38 +0900 Subject: mtd: rawnand: denali: optimize timing parameters for data interface This commit improves the ->setup_data_interface() hook. The denali_setup_data_interface() needs the frequency of clk_x and the ratio of clk_x / clk. The latter is currently hardcoded in the driver, like this: #define DENALI_CLK_X_MULT 6 The IP datasheet requires that clk_x / clk be 4, 5, or 6. I just chose 6 because it is the most defensive value, but it is not optimal. By getting the clock rate of both "clk" and "clk_x", the driver can compute the timing values more precisely. To not break the existing platforms, the fallback value, 50 MHz is provided. It is true for all upstreamed platforms. Signed-off-by: Masahiro Yamada Reviewed-by: Richard Weinberger Tested-by: Richard Weinberger Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/denali.c | 49 +++++++++++++++++++-------------------- drivers/mtd/nand/raw/denali.h | 1 + drivers/mtd/nand/raw/denali_dt.c | 2 ++ drivers/mtd/nand/raw/denali_pci.c | 1 + 4 files changed, 28 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c index a586a1d8b997..4d53f41ada08 100644 --- a/drivers/mtd/nand/raw/denali.c +++ b/drivers/mtd/nand/raw/denali.c @@ -51,14 +51,6 @@ MODULE_LICENSE("GPL"); #define DENALI_INVALID_BANK -1 #define DENALI_NR_BANKS 4 -/* - * The bus interface clock, clk_x, is phase aligned with the core clock. The - * clk_x is an integral multiple N of the core clk. The value N is configured - * at IP delivery time, and its available value is 4, 5, or 6. We need to align - * to the largest value to make it work with any possible configuration. - */ -#define DENALI_CLK_X_MULT 6 - static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd) { return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand); @@ -954,7 +946,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, { struct denali_nand_info *denali = mtd_to_denali(mtd); const struct nand_sdr_timings *timings; - unsigned long t_clk; + unsigned long t_x, mult_x; int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data; int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup; int addr_2_data_mask; @@ -965,15 +957,24 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, return PTR_ERR(timings); /* clk_x period in picoseconds */ - t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate); - if (!t_clk) + t_x = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate); + if (!t_x) + return -EINVAL; + + /* + * The bus interface clock, clk_x, is phase aligned with the core clock. + * The clk_x is an integral multiple N of the core clk. The value N is + * configured at IP delivery time, and its available value is 4, 5, 6. + */ + mult_x = DIV_ROUND_CLOSEST_ULL(denali->clk_x_rate, denali->clk_rate); + if (mult_x < 4 || mult_x > 6) return -EINVAL; if (chipnr == NAND_DATA_IFACE_CHECK_ONLY) return 0; /* tREA -> ACC_CLKS */ - acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk); + acc_clks = DIV_ROUND_UP(timings->tREA_max, t_x); acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE); tmp = ioread32(denali->reg + ACC_CLKS); @@ -982,7 +983,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, iowrite32(tmp, denali->reg + ACC_CLKS); /* tRWH -> RE_2_WE */ - re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk); + re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_x); re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE); tmp = ioread32(denali->reg + RE_2_WE); @@ -991,7 +992,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, iowrite32(tmp, denali->reg + RE_2_WE); /* tRHZ -> RE_2_RE */ - re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk); + re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_x); re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE); tmp = ioread32(denali->reg + RE_2_RE); @@ -1005,8 +1006,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, * With WE_2_RE properly set, the Denali controller automatically takes * care of the delay; the driver need not set NAND_WAIT_TCCS. */ - we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min), - t_clk); + we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min), t_x); we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE); tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE); @@ -1021,7 +1021,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, if (denali->revision < 0x0501) addr_2_data_mask >>= 1; - addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk); + addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_x); addr_2_data = min_t(int, addr_2_data, addr_2_data_mask); tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA); @@ -1031,7 +1031,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, /* tREH, tWH -> RDWR_EN_HI_CNT */ rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min), - t_clk); + t_x); rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE); tmp = ioread32(denali->reg + RDWR_EN_HI_CNT); @@ -1040,11 +1040,10 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT); /* tRP, tWP -> RDWR_EN_LO_CNT */ - rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min), - t_clk); + rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min), t_x); rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min), - t_clk); - rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT); + t_x); + rdwr_en_lo_hi = max_t(int, rdwr_en_lo_hi, mult_x); rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi); rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE); @@ -1054,8 +1053,8 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT); /* tCS, tCEA -> CS_SETUP_CNT */ - cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo, - (int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks, + cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_x) - rdwr_en_lo, + (int)DIV_ROUND_UP(timings->tCEA_max, t_x) - acc_clks, 0); cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE); @@ -1255,7 +1254,7 @@ int denali_init(struct denali_nand_info *denali) } /* clk rate info is needed for setup_data_interface */ - if (denali->clk_x_rate) + if (denali->clk_rate && denali->clk_x_rate) chip->setup_data_interface = denali_setup_data_interface; ret = nand_scan_ident(mtd, denali->max_banks, NULL); diff --git a/drivers/mtd/nand/raw/denali.h b/drivers/mtd/nand/raw/denali.h index 9ad33d237378..1f8feaf924eb 100644 --- a/drivers/mtd/nand/raw/denali.h +++ b/drivers/mtd/nand/raw/denali.h @@ -300,6 +300,7 @@ struct denali_nand_info { struct nand_chip nand; + unsigned long clk_rate; /* core clock rate */ unsigned long clk_x_rate; /* bus interface clock rate */ int active_bank; /* currently selected bank */ struct device *dev; diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c index afaae378c624..0faaad032e5f 100644 --- a/drivers/mtd/nand/raw/denali_dt.c +++ b/drivers/mtd/nand/raw/denali_dt.c @@ -150,6 +150,7 @@ static int denali_dt_probe(struct platform_device *pdev) goto out_disable_clk_x; if (dt->clk_x) { + denali->clk_rate = clk_get_rate(dt->clk); denali->clk_x_rate = clk_get_rate(dt->clk_x); } else { /* @@ -158,6 +159,7 @@ static int denali_dt_probe(struct platform_device *pdev) */ dev_notice(dev, "necessary clock is missing. default clock rates are used.\n"); + denali->clk_rate = 50000000; denali->clk_x_rate = 200000000; } diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c index 49cb3e1f8bd0..7c8efc4c7bdf 100644 --- a/drivers/mtd/nand/raw/denali_pci.c +++ b/drivers/mtd/nand/raw/denali_pci.c @@ -73,6 +73,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) denali->irq = dev->irq; denali->ecc_caps = &denali_pci_ecc_caps; denali->nand.ecc.options |= NAND_ECC_MAXIMIZE; + denali->clk_rate = 50000000; /* 50 MHz */ denali->clk_x_rate = 200000000; /* 200 MHz */ ret = pci_request_regions(dev, DENALI_NAND_NAME); -- cgit v1.2.3 From 8604e6346e8db6dece741881d647606511ddafde Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sun, 25 Feb 2018 23:09:14 +0100 Subject: mtd: rawnand: docg4: fix the probe function error path nand_release() should not be called on an MTD device that has not been registered. While it should work thanks to the checks done in mtd_device_unregister() it's a bad practice to cleanup/release something that has not previously been initialized/allocated. Rework the error path to follow this rule. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/docg4.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/docg4.c b/drivers/mtd/nand/raw/docg4.c index 1314aa99b9ab..bb96cb33cd6b 100644 --- a/drivers/mtd/nand/raw/docg4.c +++ b/drivers/mtd/nand/raw/docg4.c @@ -1341,7 +1341,7 @@ static int __init probe_docg4(struct platform_device *pdev) nand = kzalloc(len, GFP_KERNEL); if (nand == NULL) { retval = -ENOMEM; - goto fail_unmap; + goto unmap; } mtd = nand_to_mtd(nand); @@ -1357,7 +1357,7 @@ static int __init probe_docg4(struct platform_device *pdev) doc->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY); if (doc->bch == NULL) { retval = -EINVAL; - goto fail; + goto free_nand; } platform_set_drvdata(pdev, doc); @@ -1366,30 +1366,32 @@ static int __init probe_docg4(struct platform_device *pdev) retval = read_id_reg(mtd); if (retval == -ENODEV) { dev_warn(dev, "No diskonchip G4 device found.\n"); - goto fail; + goto free_bch; } retval = nand_scan_tail(mtd); if (retval) - goto fail; + goto free_bch; retval = read_factory_bbt(mtd); if (retval) - goto fail; + goto cleanup_nand; retval = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); if (retval) - goto fail; + goto cleanup_nand; doc->mtd = mtd; + return 0; -fail: - nand_release(mtd); /* deletes partitions and mtd devices */ +cleanup_nand: + nand_cleanup(nand); +free_bch: free_bch(doc->bch); +free_nand: kfree(nand); - -fail_unmap: +unmap: iounmap(virtadr); return retval; -- cgit v1.2.3 From da8eb7d227c560a1557a16caee20d7be07b074f8 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 3 Jul 2018 09:12:14 +0100 Subject: mtd: rawnand: gpmi: remove redundant variable payload_virt Variable payload_virt is being assigned but is never used hence it is redundant and can be removed. Cleans up clang warning: warning: variable 'payload_virt' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index f6aa358a3452..58d4e4a93a93 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -957,7 +957,6 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, struct gpmi_nand_data *this = nand_get_controller_data(chip); struct bch_geometry *nfc_geo = &this->bch_geometry; struct mtd_info *mtd = nand_to_mtd(chip); - void *payload_virt; dma_addr_t payload_phys; unsigned int i; unsigned char *status; @@ -967,7 +966,6 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, dev_dbg(this->dev, "page number is : %d\n", page); - payload_virt = this->payload_virt; payload_phys = this->payload_phys; if (virt_addr_valid(buf)) { @@ -976,7 +974,6 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, dest_phys = dma_map_single(this->dev, buf, nfc_geo->payload_size, DMA_FROM_DEVICE); if (!dma_mapping_error(this->dev, dest_phys)) { - payload_virt = buf; payload_phys = dest_phys; direct = true; } -- cgit v1.2.3 From 9f43deee43511c2d70a8220f76854aab89f4e673 Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Tue, 3 Jul 2018 17:36:03 +0530 Subject: mtd: rawnand: qcom: erased page bitflips detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NAND parts can have bitflips in an erased page due to the process technology used. In this case, QCOM NAND controller is not able to identify that page as an erased page. Currently the driver calls nand_check_erased_ecc_chunk() for identifying the erased pages but this won’t work always since the checking is being with ECC engine returned data. In case of bitflips, the ECC engine tries to correct the data and then it generates the uncorrectable error. Now, this data is not equal to original raw data. For erased CW identification, the raw data should be read again from NAND device and this nand_check_erased_ecc_chunk function() should be called for raw data only. Now following logic is being added to identify the erased codeword bitflips. 1. In most of the cases, not all the codewords will have bitflips and only single CW will have bitflips. So, there is no need to read the complete raw page data. The NAND raw read can be scheduled for any CW in page. The NAND controller works on CW basis and it will update the status register after each CW read. Maintain the bitmask for the CW which generated the uncorrectable error. 2. Do raw read for all the CW's which generated the uncorrectable error. 3. Both DATA and OOB need to be checked for number of 0. The top-level API can be called with only data buf or OOB buf so use chip->databuf if data buf is null and chip->oob_poi if OOB buf is null for copying the raw bytes temporarily. 4. For each CW, check the number of 0 in cw_data and usable oob bytes, The bbm and spare (unused) bytes bit flip won’t affect the ECC so don’t check the number of bitflips in this area. Signed-off-by: Abhishek Sahu Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/qcom_nandc.c | 126 +++++++++++++++++++++++++++----------- 1 file changed, 89 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 160acdff7d86..994f980c6d86 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1655,21 +1655,95 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, return check_flash_errors(host, 1); } +/* + * Bitflips can happen in erased codewords also so this function counts the + * number of 0 in each CW for which ECC engine returns the uncorrectable + * error. The page will be assumed as erased if this count is less than or + * equal to the ecc->strength for each CW. + * + * 1. Both DATA and OOB need to be checked for number of 0. The + * top-level API can be called with only data buf or OOB buf so use + * chip->data_buf if data buf is null and chip->oob_poi if oob buf + * is null for copying the raw bytes. + * 2. Perform raw read for all the CW which has uncorrectable errors. + * 3. For each CW, check the number of 0 in cw_data and usable OOB bytes. + * The BBM and spare bytes bit flip won’t affect the ECC so don’t check + * the number of bitflips in this area. + */ +static int +check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf, + u8 *oob_buf, unsigned long uncorrectable_cws, + int page, unsigned int max_bitflips) +{ + struct nand_chip *chip = &host->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + u8 *cw_data_buf, *cw_oob_buf; + int cw, data_size, oob_size, ret = 0; + + if (!data_buf) { + data_buf = chip->data_buf; + chip->pagebuf = -1; + } + + if (!oob_buf) { + oob_buf = chip->oob_poi; + chip->pagebuf = -1; + } + + for_each_set_bit(cw, &uncorrectable_cws, ecc->steps) { + if (cw == (ecc->steps - 1)) { + data_size = ecc->size - ((ecc->steps - 1) * 4); + oob_size = (ecc->steps * 4) + host->ecc_bytes_hw; + } else { + data_size = host->cw_data; + oob_size = host->ecc_bytes_hw; + } + + /* determine starting buffer address for current CW */ + cw_data_buf = data_buf + (cw * host->cw_data); + cw_oob_buf = oob_buf + (cw * ecc->bytes); + + ret = qcom_nandc_read_cw_raw(mtd, chip, cw_data_buf, + cw_oob_buf, page, cw); + if (ret) + return ret; + + /* + * make sure it isn't an erased page reported + * as not-erased by HW because of a few bitflips + */ + ret = nand_check_erased_ecc_chunk(cw_data_buf, data_size, + cw_oob_buf + host->bbm_size, + oob_size, NULL, + 0, ecc->strength); + if (ret < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += ret; + max_bitflips = max_t(unsigned int, max_bitflips, ret); + } + } + + return max_bitflips; +} + /* * reads back status registers set by the controller to notify page read * errors. this is equivalent to what 'ecc->correct()' would do. */ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, - u8 *oob_buf) + u8 *oob_buf, int page) { struct nand_chip *chip = &host->chip; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; - unsigned int max_bitflips = 0; + unsigned int max_bitflips = 0, uncorrectable_cws = 0; struct read_stats *buf; - bool flash_op_err = false; + bool flash_op_err = false, erased; int i; + u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; buf = (struct read_stats *)nandc->reg_read_buf; nandc_read_buffer_sync(nandc, true); @@ -1699,10 +1773,6 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, * codeword detection check will be done. */ if ((flash & FS_OP_ERR) && (buffer & BS_UNCORRECTABLE_BIT)) { - bool erased; - int ret, ecclen, extraooblen; - void *eccbuf; - /* * For BCH ECC, ignore erased codeword errors, if * ERASED_CW bits are set. @@ -1723,31 +1793,8 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, erased = false; } - if (erased) { - data_buf += data_len; - if (oob_buf) - oob_buf += oob_len + ecc->bytes; - continue; - } - - eccbuf = oob_buf ? oob_buf + oob_len : NULL; - ecclen = oob_buf ? host->ecc_bytes_hw : 0; - extraooblen = oob_buf ? oob_len : 0; - - /* - * make sure it isn't an erased page reported - * as not-erased by HW because of a few bitflips - */ - ret = nand_check_erased_ecc_chunk(data_buf, - data_len, eccbuf, ecclen, oob_buf, - extraooblen, ecc->strength); - if (ret < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += ret; - max_bitflips = - max_t(unsigned int, max_bitflips, ret); - } + if (!erased) + uncorrectable_cws |= BIT(i); /* * Check if MPU or any other operational error (timeout, * device failure, etc.) happened for this codeword and @@ -1777,7 +1824,12 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, if (flash_op_err) return -EIO; - return max_bitflips; + if (!uncorrectable_cws) + return max_bitflips; + + return check_for_erased_page(host, data_buf_start, oob_buf_start, + uncorrectable_cws, page, + max_bitflips); } /* @@ -1785,7 +1837,7 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, * ecc->read_oob() */ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, - u8 *oob_buf) + u8 *oob_buf, int page) { struct nand_chip *chip = &host->chip; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); @@ -1858,7 +1910,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, return ret; } - return parse_read_errors(host, data_buf_start, oob_buf_start); + return parse_read_errors(host, data_buf_start, oob_buf_start, page); } /* @@ -1910,7 +1962,7 @@ static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, clear_bam_transaction(nandc); - return read_page_ecc(host, data_buf, oob_buf); + return read_page_ecc(host, data_buf, oob_buf, page); } /* implements ecc->read_page_raw() */ @@ -1951,7 +2003,7 @@ static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, set_address(host, 0, page); update_rw_regs(host, ecc->steps, true); - return read_page_ecc(host, NULL, chip->oob_poi); + return read_page_ecc(host, NULL, chip->oob_poi, page); } /* implements ecc->write_page() */ -- cgit v1.2.3 From 4d54df433558683236298cc15dbdfee9aa928dee Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 11:44:56 +0200 Subject: mtd: rawnand: gpmi: Remove useless dependency on MTD_NAND The MTD_NAND_GPMI_NAND entry is already defined in an 'if MTD_NAND' block, no need to add an extra "depends on MTD_NAND". Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 6074a946708a..58dad80eb174 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -340,7 +340,7 @@ config MTD_NAND_NANDSIM config MTD_NAND_GPMI_NAND tristate "GPMI NAND Flash Controller driver" - depends on MTD_NAND && MXS_DMA + depends on MXS_DMA help Enables NAND Flash support for IMX23, IMX28 or IMX6. The GPMI controller is very powerful, with the help of BCH -- cgit v1.2.3 From 45e9f40f466955c9880d59683531f6d1366b97cd Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 11:44:57 +0200 Subject: mtd: rawnand: Add 'depends on HAS_IOMEM' where missing When COMPILE_TEST is allowed and the platform needs uses the iomem API we need to add an explicit dependency on HAS_IOMEM to avoid selection of these drivers when building for an arch that has no iomem support (this is the case of arch/um). Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 58dad80eb174..bfd28c1b72a3 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -152,6 +152,7 @@ config MTD_NAND_S3C2410_CLKSTOP config MTD_NAND_TANGO tristate "NAND Flash support for Tango chips" depends on ARCH_TANGO || COMPILE_TEST + depends on HAS_IOMEM help Enables the NAND Flash controller on Tango chips. @@ -513,6 +514,7 @@ config MTD_NAND_SUNXI config MTD_NAND_HISI504 tristate "Support for NAND controller on Hisilicon SoC Hip04" depends on ARCH_HISI || COMPILE_TEST + depends on HAS_IOMEM help Enables support for NAND controller on Hisilicon SoC Hip04. @@ -526,6 +528,7 @@ config MTD_NAND_QCOM config MTD_NAND_MTK tristate "Support for NAND controller on MTK SoCs" depends on ARCH_MEDIATEK || COMPILE_TEST + depends on HAS_IOMEM help Enables support for NAND controller on MTK SoCs. This controller is found on mt27xx, mt81xx, mt65xx SoCs. @@ -533,6 +536,7 @@ config MTD_NAND_MTK config MTD_NAND_TEGRA tristate "Support for NAND controller on NVIDIA Tegra" depends on ARCH_TEGRA || COMPILE_TEST + depends on HAS_IOMEM help Enables support for NAND flash controller on NVIDIA Tegra SoC. The driver has been developed and tested on a Tegra 2 SoC. DMA -- cgit v1.2.3 From 31ac1a53eb7f4407decbbbe65d8232311705029e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 11:44:59 +0200 Subject: mtd: rawnand: omap2: Allow selection of this driver when COMPILE_TEST=y It just makes NAND maintainers' life easier by allowing them to compile-test this driver without having ARCH_OMAP2PLUS or ARCH_KEYSTONE enabled. We also need to add a dependency on HAS_IOMEM to make sure the driver compiles correctly. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index bfd28c1b72a3..6a406f5b6b7f 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -77,7 +77,8 @@ config MTD_NAND_AMS_DELTA config MTD_NAND_OMAP2 tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone" - depends on (ARCH_OMAP2PLUS || ARCH_KEYSTONE) + depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST + depends on HAS_IOMEM help Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4 and Keystone platforms. -- cgit v1.2.3 From b22a8b075dbc755dd483e1d318c8f2933464583e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 11:45:01 +0200 Subject: mtd: rawnand: sharpsl: Remove inclusion of mach and asm headers We don't need mach/hardware.h and sm/mach-types.h, and asm/io.h can be replaced by linux/io.h. Now that we removed those inclusions, we're ready to allow selection of this driver when COMPILE_TEST=y. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/sharpsl.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/sharpsl.c b/drivers/mtd/nand/raw/sharpsl.c index e93df02c825e..fc171b17a39b 100644 --- a/drivers/mtd/nand/raw/sharpsl.c +++ b/drivers/mtd/nand/raw/sharpsl.c @@ -21,10 +21,7 @@ #include #include #include - -#include -#include -#include +#include struct sharpsl_nand { struct nand_chip chip; -- cgit v1.2.3 From 18331b98102925f17be13421e153b050b8df01d3 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 11:45:02 +0200 Subject: mtd: rawnand: sharpsl: Allow selection of this driver when COMPILE_TEST=y It just makes NAND maintainers' life easier by allowing them to compile-test this driver without having ARCH_PXA enabled. We also need to add a dependency on HAS_IOMEM to make sure the driver compiles correctly. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 6a406f5b6b7f..59bafed9da17 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -249,7 +249,8 @@ config MTD_NAND_DOCG4 config MTD_NAND_SHARPSL tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" - depends on ARCH_PXA + depends on ARCH_PXA || COMPILE_TEST + depends on HAS_IOMEM config MTD_NAND_CAFE tristate "NAND support for OLPC CAFÉ chip" -- cgit v1.2.3 From ee70e5e796ff30e486bef4800a633df8e14d6245 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 11:45:03 +0200 Subject: mtd: rawnand: lpc32xx: Allow selection of these drivers when COMPILE_TEST=y It just makes NAND maintainers' life easier by allowing them to compile-test these drivers without having ARCH_LPC32XX enabled. We also need to add a dependency on HAS_IOMEM to make sure the driver compiles correctly. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 59bafed9da17..22cc19e7586c 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -297,7 +297,8 @@ config MTD_NAND_MARVELL config MTD_NAND_SLC_LPC32XX tristate "NXP LPC32xx SLC Controller" - depends on ARCH_LPC32XX + depends on ARCH_LPC32XX || COMPILE_TEST + depends on HAS_IOMEM help Enables support for NXP's LPC32XX SLC (i.e. for Single Level Cell chips) NAND controller. This is the default for the PHYTEC 3250 @@ -308,7 +309,8 @@ config MTD_NAND_SLC_LPC32XX config MTD_NAND_MLC_LPC32XX tristate "NXP LPC32xx MLC Controller" - depends on ARCH_LPC32XX + depends on ARCH_LPC32XX || COMPILE_TEST + depends on HAS_IOMEM help Uses the LPC32XX MLC (i.e. for Multi Level Cell chips) NAND controller. This is the default for the WORK92105 controller -- cgit v1.2.3 From 0beb48724120ae13fe87d344a78fb430e077fed9 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 11:45:04 +0200 Subject: mtd: rawnand: brcmnand: Allow selection of this driver when COMPILE_TEST=y It just makes NAND maintainers' life easier by allowing them to compile-test this driver without having ARM, ARM64 or MIPS enabled. We also need to add a dependency on HAS_IOMEM to make sure the driver compiles correctly. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 22cc19e7586c..af0b3cd51c82 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -354,7 +354,8 @@ config MTD_NAND_GPMI_NAND config MTD_NAND_BRCMNAND tristate "Broadcom STB NAND controller" - depends on ARM || ARM64 || MIPS + depends on ARM || ARM64 || MIPS || COMPILE_TEST + depends on HAS_IOMEM help Enables the Broadcom NAND controller driver. The controller was originally designed for Set-Top Box but is used on various BCM7xxx, -- cgit v1.2.3 From c03f3cf37e54fee8c04c531dae1c81ebc8f58723 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 11:45:07 +0200 Subject: mtd: rawnand: mxc: Avoid inclusion of asm/mach headers asm/mach/flash.h does not seem to be needed, drop this #include to make the code completely machine and arch independent and allow one to compile it when COMPILE_TEST=y. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/mxc_nand.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index 90cfb5e730aa..14ec7d975593 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -34,8 +34,6 @@ #include #include #include - -#include #include #define DRIVER_NAME "mxc_nand" -- cgit v1.2.3 From 434bc2e148aa5a6052cbc038f485fa3284a477d0 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 11:45:08 +0200 Subject: mtd: rawnand: mxc: Allow selection of this driver when COMPILE_TEST=y It just makes NAND maintainers' life easier by allowing them to compile-test this driver without having ARCH_MXC enabled. We also need to add a dependency on HAS_IOMEM to make sure the driver compiles correctly. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index af0b3cd51c82..3565dc06593f 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -443,7 +443,8 @@ config MTD_NAND_VF610_NFC config MTD_NAND_MXC tristate "MXC NAND support" - depends on ARCH_MXC + depends on ARCH_MXC || COMPILE_TEST + depends on HAS_IOMEM help This enables the driver for the NAND flash controller on the MXC processors. -- cgit v1.2.3 From b2d55fe2ad5a47676292b1ef211fe4f9f14133ec Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 11:45:11 +0200 Subject: mtd: rawnand: qcom: Allow selection of this driver when COMPILE_TEST=y It just makes NAND maintainers' life easier by allowing them to compile-test this driver without having ARCH_QCOM enabled. We also need to add a dependency on HAS_IOMEM to make sure the driver compiles correctly. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 3565dc06593f..dd2fc7105ea7 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -526,7 +526,8 @@ config MTD_NAND_HISI504 config MTD_NAND_QCOM tristate "Support for NAND on QCOM SoCs" - depends on ARCH_QCOM + depends on ARCH_QCOM || COMPILE_TEST + depends on HAS_IOMEM help Enables support for NAND flash chips on SoCs containing the EBI2 NAND controller. This controller is found on IPQ806x SoC. -- cgit v1.2.3 From d4de09e5bb08138f06b6d27cd0246d32866b0c68 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 11:45:13 +0200 Subject: mtd: rawnand: nuc900: Allow selection of this driver when COMPILE_TEST=y It just makes NAND maintainers' life easier by allowing them to compile-test this driver without having ARCH_W90X900 enabled. We also need to add a dependency on HAS_IOMEM to make sure the driver compiles correctly. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index dd2fc7105ea7..230311fa2cc0 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -478,7 +478,8 @@ config MTD_NAND_SOCRATES config MTD_NAND_NUC900 tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards." - depends on ARCH_W90X900 + depends on ARCH_W90X900 || COMPILE_TEST + depends on HAS_IOMEM help This enables the driver for the NAND Flash on evaluation board based on w90p910 / NUC9xx. -- cgit v1.2.3 From a58d0b4d512aceb06d8a968c1d19318c77faa6b0 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 11:45:16 +0200 Subject: bcma: Allow selection of this driver when COMPILE_TEST=y This allows us to increase compile-test coverage without having to build a kernel for MIPS. That's particularly interesting for subsystem maintainers that want to test as many drivers as possible in a single build. We also add a dependency on HAS_IOMEM in BCMA_HOST_SOC to make sure the driver is not selected when the arch does not implement IO accessors. Signed-off-by: Boris Brezillon Acked-by: Kalle Valo Signed-off-by: Miquel Raynal --- drivers/bcma/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig index cb0f1aad20b7..b9558ff20830 100644 --- a/drivers/bcma/Kconfig +++ b/drivers/bcma/Kconfig @@ -30,6 +30,7 @@ config BCMA_HOST_PCI config BCMA_HOST_SOC bool "Support for BCMA in a SoC" + depends on HAS_IOMEM help Host interface for a Broadcom AIX bus directly mapped into the memory. This only works with the Broadcom SoCs from the @@ -61,7 +62,7 @@ config BCMA_DRIVER_PCI_HOSTMODE config BCMA_DRIVER_MIPS bool "BCMA Broadcom MIPS core driver" - depends on MIPS + depends on MIPS || COMPILE_TEST help Driver for the Broadcom MIPS core attached to Broadcom specific Advanced Microcontroller Bus. -- cgit v1.2.3 From 5dcddebb1b1f7638a2547d6729ca1ea501fb6834 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 12:27:26 +0200 Subject: mtd: rawnand: Kill cafe_nand_bug() Leaving a function pointer to NULL should be enough to trigger a NULL pointer exception, and anyway, if we want to BUG() when some missing hooks are called, this should be done in the core, so let's drop the cafe_nand_bug() dummy function. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/cafe_nand.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c index d721f489b38b..ac0be933a490 100644 --- a/drivers/mtd/nand/raw/cafe_nand.c +++ b/drivers/mtd/nand/raw/cafe_nand.c @@ -345,11 +345,6 @@ static irqreturn_t cafe_nand_interrupt(int irq, void *id) return IRQ_HANDLED; } -static void cafe_nand_bug(struct mtd_info *mtd) -{ - BUG(); -} - static int cafe_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { @@ -760,9 +755,6 @@ static int cafe_nand_probe(struct pci_dev *pdev, cafe->nand.ecc.size = mtd->writesize; cafe->nand.ecc.bytes = 14; cafe->nand.ecc.strength = 4; - cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; - cafe->nand.ecc.calculate = (void *)cafe_nand_bug; - cafe->nand.ecc.correct = (void *)cafe_nand_bug; cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel; cafe->nand.ecc.write_oob = cafe_nand_write_oob; cafe->nand.ecc.read_page = cafe_nand_read_page; -- cgit v1.2.3 From 44b07b921dea5ec997fb929ceaa82582a7415816 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 12:27:30 +0200 Subject: mtd: rawnand: Rename nand_default_bbt() into nand_create_bbt() Rename nand_default_bbt() into nand_create_bbt() and pass it a nand_chip object to prepare removal of the chip->scan_bbt() hook. We add a temporary nand_default_bbt() wrapper which will be dropped after the removal of ->scan_bbt(). Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_base.c | 5 +++++ drivers/mtd/nand/raw/nand_bbt.c | 10 +++++----- include/linux/mtd/rawnand.h | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index faac82b1e058..0476e13d47b1 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4924,6 +4924,11 @@ static void nand_shutdown(struct mtd_info *mtd) nand_get_device(mtd, FL_PM_SUSPENDED); } +static int nand_default_bbt(struct mtd_info *mtd) +{ + return nand_create_bbt(mtd_to_nand(mtd)); +} + /* Set default functions */ static void nand_set_defaults(struct nand_chip *chip) { diff --git a/drivers/mtd/nand/raw/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c index d9f4ceff2568..39db352f8757 100644 --- a/drivers/mtd/nand/raw/nand_bbt.c +++ b/drivers/mtd/nand/raw/nand_bbt.c @@ -1349,15 +1349,14 @@ static int nand_create_badblock_pattern(struct nand_chip *this) } /** - * nand_default_bbt - [NAND Interface] Select a default bad block table for the device - * @mtd: MTD device structure + * nand_create_bbt - [NAND Interface] Select a default bad block table for the device + * @this: NAND chip object * * This function selects the default bad block table support for the device and * calls the nand_scan_bbt function. */ -int nand_default_bbt(struct mtd_info *mtd) +int nand_create_bbt(struct nand_chip *this) { - struct nand_chip *this = mtd_to_nand(mtd); int ret; /* Is a flash based bad block table requested? */ @@ -1383,8 +1382,9 @@ int nand_default_bbt(struct mtd_info *mtd) return ret; } - return nand_scan_bbt(mtd, this->badblock_pattern); + return nand_scan_bbt(nand_to_mtd(this), this->badblock_pattern); } +EXPORT_SYMBOL(nand_create_bbt); /** * nand_isreserved_bbt - [NAND Interface] Check if a block is reserved diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 83ab6779144e..186f9fb1e7eb 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1538,7 +1538,7 @@ extern const struct nand_manufacturer_ops micron_nand_manuf_ops; extern const struct nand_manufacturer_ops amd_nand_manuf_ops; extern const struct nand_manufacturer_ops macronix_nand_manuf_ops; -int nand_default_bbt(struct mtd_info *mtd); +int nand_create_bbt(struct nand_chip *chip); int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs); int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs); int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt); -- cgit v1.2.3 From e80eba7581512625083ba540f120479b935425de Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 12:27:31 +0200 Subject: mtd: rawnand: Kill the chip->scan_bbt() hook None of the existing drivers are overloading the ->scan_bbt() method, let's get rid of it and replace calls to ->scan_bbt() by nand_create_bbt() ones. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/diskonchip.c | 4 ++-- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 2 +- drivers/mtd/nand/raw/nand_base.c | 9 +-------- drivers/mtd/nand/raw/nandsim.c | 2 +- include/linux/mtd/rawnand.h | 2 -- 5 files changed, 5 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c index 8d10061abb4b..3c46188dd6d2 100644 --- a/drivers/mtd/nand/raw/diskonchip.c +++ b/drivers/mtd/nand/raw/diskonchip.c @@ -1291,7 +1291,7 @@ static int __init nftl_scan_bbt(struct mtd_info *mtd) this->bbt_md = NULL; } - ret = this->scan_bbt(mtd); + ret = nand_create_bbt(this); if (ret) return ret; @@ -1338,7 +1338,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd) this->bbt_md->pattern = "TBB_SYSM"; } - ret = this->scan_bbt(mtd); + ret = nand_create_bbt(this); if (ret) return ret; diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 58d4e4a93a93..599513321e71 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -1944,7 +1944,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) ret = nand_boot_init(this); if (ret) goto err_nand_cleanup; - ret = chip->scan_bbt(mtd); + ret = nand_create_bbt(chip); if (ret) goto err_nand_cleanup; diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 0476e13d47b1..4fa5e20d9690 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4924,11 +4924,6 @@ static void nand_shutdown(struct mtd_info *mtd) nand_get_device(mtd, FL_PM_SUSPENDED); } -static int nand_default_bbt(struct mtd_info *mtd) -{ - return nand_create_bbt(mtd_to_nand(mtd)); -} - /* Set default functions */ static void nand_set_defaults(struct nand_chip *chip) { @@ -4970,8 +4965,6 @@ static void nand_set_defaults(struct nand_chip *chip) chip->write_byte = busw ? nand_write_byte16 : nand_write_byte; if (!chip->read_buf || chip->read_buf == nand_read_buf) chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; - if (!chip->scan_bbt) - chip->scan_bbt = nand_default_bbt; if (!chip->controller) { chip->controller = &chip->hwcontrol; @@ -6673,7 +6666,7 @@ int nand_scan_tail(struct mtd_info *mtd) return 0; /* Build bad block table */ - ret = chip->scan_bbt(mtd); + ret = nand_create_bbt(chip); if (ret) goto err_nand_manuf_cleanup; diff --git a/drivers/mtd/nand/raw/nandsim.c b/drivers/mtd/nand/raw/nandsim.c index f8edacde49ab..8a3b36cfe5ea 100644 --- a/drivers/mtd/nand/raw/nandsim.c +++ b/drivers/mtd/nand/raw/nandsim.c @@ -2337,7 +2337,7 @@ static int __init ns_init_module(void) if ((retval = init_nandsim(nsmtd)) != 0) goto err_exit; - if ((retval = chip->scan_bbt(nsmtd)) != 0) + if ((retval = nand_create_bbt(chip)) != 0) goto err_exit; if ((retval = parse_badblocks(nand, nsmtd)) != 0) diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 186f9fb1e7eb..ac0007ddadf2 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1199,7 +1199,6 @@ int nand_op_parser_exec_op(struct nand_chip *chip, * @buf_align: minimum buffer alignment required by a platform * @hwcontrol: platform-specific hardware control structure * @erase: [REPLACEABLE] erase function - * @scan_bbt: [REPLACEABLE] function to scan bad block table * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transferring * data from array to read regs (tR). * @state: [INTERN] the current state of the NAND device @@ -1292,7 +1291,6 @@ struct nand_chip { const struct nand_operation *op, bool check_only); int (*erase)(struct mtd_info *mtd, int page); - int (*scan_bbt)(struct mtd_info *mtd); int (*set_features)(struct mtd_info *mtd, struct nand_chip *chip, int feature_addr, uint8_t *subfeature_para); int (*get_features)(struct mtd_info *mtd, struct nand_chip *chip, -- cgit v1.2.3 From f0f01838f76420988b50b23f315704c9a5cd2fa9 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 12:27:32 +0200 Subject: mtd: rawnand: orion_nand: Kill orion_nand_data.dev_ready() None of the boards seem to overload the ->dev_ready() hook, just drop this field from orion_nand_data. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/orion_nand.c | 3 --- include/linux/platform_data/mtd-orion_nand.h | 1 - 2 files changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/orion_nand.c b/drivers/mtd/nand/raw/orion_nand.c index 7825fd3ce66b..1a4828442675 100644 --- a/drivers/mtd/nand/raw/orion_nand.c +++ b/drivers/mtd/nand/raw/orion_nand.c @@ -153,9 +153,6 @@ static int __init orion_nand_probe(struct platform_device *pdev) if (board->width == 16) nc->options |= NAND_BUSWIDTH_16; - if (board->dev_ready) - nc->dev_ready = board->dev_ready; - platform_set_drvdata(pdev, info); /* Not all platforms can gate the clock, so it is not diff --git a/include/linux/platform_data/mtd-orion_nand.h b/include/linux/platform_data/mtd-orion_nand.h index a7ce77c7c1a8..34828eb85982 100644 --- a/include/linux/platform_data/mtd-orion_nand.h +++ b/include/linux/platform_data/mtd-orion_nand.h @@ -12,7 +12,6 @@ */ struct orion_nand_data { struct mtd_partition *parts; - int (*dev_ready)(struct mtd_info *mtd); u32 nr_parts; u8 ale; /* address line number connected to ALE */ u8 cle; /* address line number connected to CLE */ -- cgit v1.2.3 From dc2d8856a758627d4f126d17bc113eedd72b2d60 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 5 Jul 2018 12:27:33 +0200 Subject: mtd: rawnand: plat_nand: Kill pdata->ctrl.{hwcontrol, read_byte}() None of the board files are overloading those hooks, so let's drop them from struct platform_nand_ctrl. Signed-off-by: Boris Brezillon Acked-by: Robert Jarzmik Signed-off-by: Miquel Raynal --- arch/arm/mach-pxa/balloon3.c | 1 - arch/arm/mach-pxa/em-x270.c | 1 - drivers/mtd/nand/raw/plat_nand.c | 2 -- include/linux/mtd/rawnand.h | 4 ---- 4 files changed, 8 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-pxa/balloon3.c b/arch/arm/mach-pxa/balloon3.c index f4f8f23bda8c..af46d2182533 100644 --- a/arch/arm/mach-pxa/balloon3.c +++ b/arch/arm/mach-pxa/balloon3.c @@ -688,7 +688,6 @@ struct platform_nand_data balloon3_nand_pdata = { .chip_delay = 50, }, .ctrl = { - .hwcontrol = 0, .dev_ready = balloon3_nand_dev_ready, .select_chip = balloon3_nand_select_chip, .cmd_ctrl = balloon3_nand_cmd_ctl, diff --git a/arch/arm/mach-pxa/em-x270.c b/arch/arm/mach-pxa/em-x270.c index 49022ad338e9..29be04c6cc48 100644 --- a/arch/arm/mach-pxa/em-x270.c +++ b/arch/arm/mach-pxa/em-x270.c @@ -346,7 +346,6 @@ struct platform_nand_data em_x270_nand_platdata = { .chip_delay = 20, }, .ctrl = { - .hwcontrol = 0, .dev_ready = em_x270_nand_device_ready, .select_chip = 0, .cmd_ctrl = em_x270_nand_cmd_ctl, diff --git a/drivers/mtd/nand/raw/plat_nand.c b/drivers/mtd/nand/raw/plat_nand.c index 925a1323604d..222626df4b96 100644 --- a/drivers/mtd/nand/raw/plat_nand.c +++ b/drivers/mtd/nand/raw/plat_nand.c @@ -67,12 +67,10 @@ static int plat_nand_probe(struct platform_device *pdev) data->chip.select_chip = pdata->ctrl.select_chip; data->chip.write_buf = pdata->ctrl.write_buf; data->chip.read_buf = pdata->ctrl.read_buf; - data->chip.read_byte = pdata->ctrl.read_byte; data->chip.chip_delay = pdata->chip.chip_delay; data->chip.options |= pdata->chip.options; data->chip.bbt_options |= pdata->chip.bbt_options; - data->chip.ecc.hwctl = pdata->ctrl.hwcontrol; data->chip.ecc.mode = NAND_ECC_SOFT; data->chip.ecc.algo = NAND_ECC_HAMMING; diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index ac0007ddadf2..11c2426fc363 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1572,14 +1572,12 @@ struct platform_device; * struct platform_nand_ctrl - controller level device structure * @probe: platform specific function to probe/setup hardware * @remove: platform specific function to remove/teardown hardware - * @hwcontrol: platform specific hardware control structure * @dev_ready: platform specific function to read ready/busy pin * @select_chip: platform specific chip select function * @cmd_ctrl: platform specific function for controlling * ALE/CLE/nCE. Also used to write command and address * @write_buf: platform specific function for write buffer * @read_buf: platform specific function for read buffer - * @read_byte: platform specific function to read one byte from chip * @priv: private data to transport driver specific settings * * All fields are optional and depend on the hardware driver requirements @@ -1587,13 +1585,11 @@ struct platform_device; struct platform_nand_ctrl { int (*probe)(struct platform_device *pdev); void (*remove)(struct platform_device *pdev); - void (*hwcontrol)(struct mtd_info *mtd, int cmd); int (*dev_ready)(struct mtd_info *mtd); void (*select_chip)(struct mtd_info *mtd, int chip); void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); - unsigned char (*read_byte)(struct mtd_info *mtd); void *priv; }; -- cgit v1.2.3 From b04a3fe3143d23e8e450fe8b36868c6e99d5bdd0 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 7 Jul 2018 14:25:20 -0300 Subject: mtd: rawnand: gpmi: Switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Signed-off-by: Fabio Estevam Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c | 15 +-------------- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 15 +-------------- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h | 11 +---------- 3 files changed, 3 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c index 83697b8df871..88ea2203e263 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c @@ -1,22 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Freescale GPMI NAND Flash Driver * * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. * Copyright (C) 2008 Embedded Alley Solutions, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 599513321e71..6294d018df65 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -1,22 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Freescale GPMI NAND Flash Driver * * Copyright (C) 2010-2015 Freescale Semiconductor, Inc. * Copyright (C) 2008 Embedded Alley Solutions, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h index 6aa10d6962d6..69cd0cbde4f2 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h @@ -1,18 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Freescale GPMI NAND Flash Driver * * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. * Copyright (C) 2008 Embedded Alley Solutions, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __DRIVERS_MTD_NAND_GPMI_NAND_H #define __DRIVERS_MTD_NAND_GPMI_NAND_H -- cgit v1.2.3 From c786bbcc1a2c15db29a2acb33d8a53e19a57d20d Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 7 Jul 2018 14:25:21 -0300 Subject: mtd: rawnand: mxc: Switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Signed-off-by: Fabio Estevam Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/mxc_nand.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index 14ec7d975593..c6eff61e909d 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -1,20 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright 2008 Sascha Hauer, kernel@pengutronix.de - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. */ #include -- cgit v1.2.3 From bd9c3f9b3c00da322b5e784e820533f1598f690a Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 8 Jul 2018 02:10:06 +0200 Subject: mtd: rawnand: marvell: add suspend and resume hooks This patch restores the suspend and resume hooks that the old driver used to have. Apart from stopping and starting the clocks, the resume callback also nullifies the selected_chip pointer, so the next command that is issued will re-select the chip and thereby restore the timing registers. Factor out some code from marvell_nfc_init() into a new function marvell_nfc_reset() and also call it at resume time to reset some registers that don't retain their contents during low-power mode. Without this patch, a PXA3xx based system would cough up an error similar to the one below after resume. [ 44.660162] marvell-nfc 43100000.nand-controller: Timeout waiting for RB signal [ 44.671492] ubi0 error: ubi_io_write: error -110 while writing 2048 bytes to PEB 102:38912, written 0 bytes [ 44.682887] CPU: 0 PID: 1417 Comm: remote-control Not tainted 4.18.0-rc2+ #344 [ 44.691197] Hardware name: Marvell PXA3xx (Device Tree Support) [ 44.697111] Backtrace: [ 44.699593] [] (dump_backtrace) from [] (show_stack+0x18/0x1c) [ 44.708931] r7:00000800 r6:00009800 r5:00000066 r4:c6139000 [ 44.715833] [] (show_stack) from [] (dump_stack+0x20/0x28) [ 44.724206] [] (dump_stack) from [] (ubi_io_write+0x3d4/0x630) [ 44.732925] [] (ubi_io_write) from [] (ubi_eba_write_leb+0x690/0x6fc) ... Fixes: 02f26ecf8c77 ("mtd: nand: add reworked Marvell NAND controller driver") Cc: stable@vger.kernel.org Signed-off-by: Daniel Mack Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/marvell_nand.c | 73 +++++++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index ba6889bbe802..96c74bfc5a39 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2678,6 +2678,21 @@ static int marvell_nfc_init_dma(struct marvell_nfc *nfc) return 0; } +static void marvell_nfc_reset(struct marvell_nfc *nfc) +{ + /* + * ECC operations and interruptions are only enabled when specifically + * needed. ECC shall not be activated in the early stages (fails probe). + * Arbiter flag, even if marked as "reserved", must be set (empirical). + * SPARE_EN bit must always be set or ECC bytes will not be at the same + * offset in the read page and this will fail the protection. + */ + writel_relaxed(NDCR_ALL_INT | NDCR_ND_ARB_EN | NDCR_SPARE_EN | + NDCR_RD_ID_CNT(NFCV1_READID_LEN), nfc->regs + NDCR); + writel_relaxed(0xFFFFFFFF, nfc->regs + NDSR); + writel_relaxed(0, nfc->regs + NDECCCTRL); +} + static int marvell_nfc_init(struct marvell_nfc *nfc) { struct device_node *np = nfc->dev->of_node; @@ -2716,17 +2731,7 @@ static int marvell_nfc_init(struct marvell_nfc *nfc) if (!nfc->caps->is_nfcv2) marvell_nfc_init_dma(nfc); - /* - * ECC operations and interruptions are only enabled when specifically - * needed. ECC shall not be activated in the early stages (fails probe). - * Arbiter flag, even if marked as "reserved", must be set (empirical). - * SPARE_EN bit must always be set or ECC bytes will not be at the same - * offset in the read page and this will fail the protection. - */ - writel_relaxed(NDCR_ALL_INT | NDCR_ND_ARB_EN | NDCR_SPARE_EN | - NDCR_RD_ID_CNT(NFCV1_READID_LEN), nfc->regs + NDCR); - writel_relaxed(0xFFFFFFFF, nfc->regs + NDSR); - writel_relaxed(0, nfc->regs + NDECCCTRL); + marvell_nfc_reset(nfc); return 0; } @@ -2841,6 +2846,51 @@ static int marvell_nfc_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused marvell_nfc_suspend(struct device *dev) +{ + struct marvell_nfc *nfc = dev_get_drvdata(dev); + struct marvell_nand_chip *chip; + + list_for_each_entry(chip, &nfc->chips, node) + marvell_nfc_wait_ndrun(&chip->chip); + + clk_disable_unprepare(nfc->reg_clk); + clk_disable_unprepare(nfc->core_clk); + + return 0; +} + +static int __maybe_unused marvell_nfc_resume(struct device *dev) +{ + struct marvell_nfc *nfc = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(nfc->core_clk); + if (ret < 0) + return ret; + + if (!IS_ERR(nfc->reg_clk)) { + ret = clk_prepare_enable(nfc->reg_clk); + if (ret < 0) + return ret; + } + + /* + * Reset nfc->selected_chip so the next command will cause the timing + * registers to be restored in marvell_nfc_select_chip(). + */ + nfc->selected_chip = NULL; + + /* Reset registers that have lost their contents */ + marvell_nfc_reset(nfc); + + return 0; +} + +static const struct dev_pm_ops marvell_nfc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(marvell_nfc_suspend, marvell_nfc_resume) +}; + static const struct marvell_nfc_caps marvell_armada_8k_nfc_caps = { .max_cs_nb = 4, .max_rb_nb = 2, @@ -2925,6 +2975,7 @@ static struct platform_driver marvell_nfc_driver = { .driver = { .name = "marvell-nfc", .of_match_table = marvell_nfc_of_ids, + .pm = &marvell_nfc_pm_ops, }, .id_table = marvell_nfc_platform_ids, .probe = marvell_nfc_probe, -- cgit v1.2.3 From 7734a275a7eebf11878a923dc102908eff0f0657 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 8 Jul 2018 02:10:07 +0200 Subject: mtd: rawnand: marvell: remove bogus comment in marvell_nfc_select_chip() The comment in marvell_nfc_select_chip() about ndtr0 and ndtr1 didn't reflect what the driver was doing. The values of NDTR0 and NDTR1 are read from the registers at probe time and a copy is retained in 'struct marvell_nand_chip'. If keep-config is set in the DT properties, there are no other writers of these timing variables so they can safely be used when the chip is selected. As suggested by Miquel Raynal, simply remove the comment. Signed-off-by: Daniel Mack Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/marvell_nand.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index 96c74bfc5a39..638a0c8b44b8 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -650,11 +650,6 @@ static void marvell_nfc_select_chip(struct mtd_info *mtd, int die_nr) return; } - /* - * Do not change the timing registers when using the DT property - * marvell,nand-keep-config; in that case ->ndtr0 and ->ndtr1 from the - * marvell_nand structure are supposedly empty. - */ writel_relaxed(marvell_nand->ndtr0, nfc->regs + NDTR0); writel_relaxed(marvell_nand->ndtr1, nfc->regs + NDTR1); -- cgit v1.2.3 From f9e64d61042575960ce868fa974d9dbe4937c59a Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 8 Jul 2018 02:10:08 +0200 Subject: mtd: rawnand: marvell: set reg_clk to NULL if it can't be obtained Don't keep an error-pointer around in the private struct. If this optional clock can't be obtained, simply set the pointer to NULL instead so we can use clk_prepare_enable() on it without further checks, Signed-off-by: Daniel Mack Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/marvell_nand.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index 638a0c8b44b8..80a074cccb82 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2773,17 +2773,19 @@ static int marvell_nfc_probe(struct platform_device *pdev) return ret; nfc->reg_clk = devm_clk_get(&pdev->dev, "reg"); - if (PTR_ERR(nfc->reg_clk) != -ENOENT) { - if (!IS_ERR(nfc->reg_clk)) { - ret = clk_prepare_enable(nfc->reg_clk); - if (ret) - goto unprepare_core_clk; - } else { + if (IS_ERR(nfc->reg_clk)) { + if (PTR_ERR(nfc->reg_clk) != -ENOENT) { ret = PTR_ERR(nfc->reg_clk); goto unprepare_core_clk; } + + nfc->reg_clk = NULL; } + ret = clk_prepare_enable(nfc->reg_clk); + if (ret) + goto unprepare_core_clk; + marvell_nfc_disable_int(nfc, NDCR_ALL_INT); marvell_nfc_clear_int(nfc, NDCR_ALL_INT); ret = devm_request_irq(dev, irq, marvell_nfc_isr, @@ -2864,11 +2866,9 @@ static int __maybe_unused marvell_nfc_resume(struct device *dev) if (ret < 0) return ret; - if (!IS_ERR(nfc->reg_clk)) { - ret = clk_prepare_enable(nfc->reg_clk); - if (ret < 0) - return ret; - } + ret = clk_prepare_enable(nfc->reg_clk); + if (ret < 0) + return ret; /* * Reset nfc->selected_chip so the next command will cause the timing -- cgit v1.2.3 From 20366e19e28f9954b25580c020d7a4e0db6055c4 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 4 Jul 2018 16:08:58 +0200 Subject: mtd: rawnand: hynix: Use ->exec_op() in hynix_nand_reg_write_op() Modern NAND controller drivers implement ->exec_op() instead of ->cmdfunc(), make sure we don't end up with a NULL pointer dereference when hynix_nand_reg_write_op() is called. Fixes: 8878b126df76 ("mtd: nand: add ->exec_op() implementation") Cc: Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_hynix.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_hynix.c b/drivers/mtd/nand/raw/nand_hynix.c index 8cbe77f447c7..4ffbb26e76d6 100644 --- a/drivers/mtd/nand/raw/nand_hynix.c +++ b/drivers/mtd/nand/raw/nand_hynix.c @@ -100,6 +100,16 @@ static int hynix_nand_reg_write_op(struct nand_chip *chip, u8 addr, u8 val) struct mtd_info *mtd = nand_to_mtd(chip); u16 column = ((u16)addr << 8) | addr; + if (chip->exec_op) { + struct nand_op_instr instrs[] = { + NAND_OP_ADDR(1, &addr, 0), + NAND_OP_8BIT_DATA_OUT(1, &val, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + return nand_exec_op(chip, &op); + } + chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1); chip->write_byte(mtd, val); -- cgit v1.2.3 From e6848511d06189cd28f83e5aef580510699fb2b4 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:22 +0200 Subject: mtd: rawnand: atmel: Use uintptr_t casts instead of unsigned int When casting a pointer to an unsigned int, uintptr_t should be used to cope with the pointer size differences between 32-bit and 64-bit architectures. This is needed if we want to allow compilation of this driver when COMPILE_TEST=y. Reported-by: Stephen Rothwell Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/atmel/nand-controller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index e686fe73159e..e8f7549d0354 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -2050,7 +2050,7 @@ atmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc) return ret; } - nc->ebi_csa_offs = (unsigned int)match->data; + nc->ebi_csa_offs = (uintptr_t)match->data; /* * The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1 -- cgit v1.2.3 From d28395c9083de4ee03e51b804d7acc591191bb00 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:23 +0200 Subject: mtd: rawnand: atmel: Add an __iomem cast on gen_pool_dma_alloc() call gen_pool_dma_alloc() return type is void *, while internally, the memory region exposed by the sram driver has been mapped with ioremap(). Add a void * to void __iomem * cast to make sparse happy. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/atmel/nand-controller.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index e8f7549d0354..30dae4c9d439 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -2219,9 +2219,9 @@ atmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc) return -ENOMEM; } - nc->sram.virt = gen_pool_dma_alloc(nc->sram.pool, - ATMEL_NFC_SRAM_SIZE, - &nc->sram.dma); + nc->sram.virt = (void __iomem *)gen_pool_dma_alloc(nc->sram.pool, + ATMEL_NFC_SRAM_SIZE, + &nc->sram.dma); if (!nc->sram.virt) { dev_err(nc->base.dev, "Could not allocate memory from the NFC SRAM pool\n"); -- cgit v1.2.3 From 88a40e7dca00fef96e4396c3e8c0077ab96b15a6 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:24 +0200 Subject: mtd: rawnand: atmel: Allow selection of this driver when COMPILE_TEST=y It just makes NAND maintainers' life easier by allowing them to compile-test this driver without having ARCH_AT91 enabled. We also need to add a dependency on HAS_IOMEM to make sure the driver compiles correctly. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 230311fa2cc0..1488ad0a0199 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -277,7 +277,8 @@ config MTD_NAND_CS553X config MTD_NAND_ATMEL tristate "Support for NAND Flash / SmartMedia on AT91" - depends on ARCH_AT91 + depends on ARCH_AT91 || COMPILE_TEST + depends on HAS_IOMEM select MFD_ATMEL_SMC help Enables support for NAND Flash / Smart Media Card interface -- cgit v1.2.3 From c5b76d8dd2bf318d9e6b4b3a480b9e96c01b31c8 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:28 +0200 Subject: mtd: rawnand: davinci: Stop doing iomem pointer <-> u32 conversions There is no point in doing this sort of conversion since we can replace |= by += operations which are perfectly valid on pointers. This is done in preparation of COMPILE_TEST addition to the NAND_DAVINCI Kconfig entry, since building for x86 generates several warnings because of inappropriate u32 <-> void * conversions (pointers are 64-bit large on x86_64). Reported-by: Stephen Rothwell Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/davinci_nand.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index cd12e5abafde..9cd36a750965 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -60,8 +60,7 @@ struct davinci_nand_info { void __iomem *base; void __iomem *vaddr; - uint32_t ioaddr; - uint32_t current_cs; + void __iomem *current_cs; uint32_t mask_chipsel; uint32_t mask_ale; @@ -102,17 +101,17 @@ static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct davinci_nand_info *info = to_davinci_nand(mtd); - uint32_t addr = info->current_cs; + void __iomem *addr = info->current_cs; struct nand_chip *nand = mtd_to_nand(mtd); /* Did the control lines change? */ if (ctrl & NAND_CTRL_CHANGE) { if ((ctrl & NAND_CTRL_CLE) == NAND_CTRL_CLE) - addr |= info->mask_cle; + addr += info->mask_cle; else if ((ctrl & NAND_CTRL_ALE) == NAND_CTRL_ALE) - addr |= info->mask_ale; + addr += info->mask_ale; - nand->IO_ADDR_W = (void __iomem __force *)addr; + nand->IO_ADDR_W = addr; } if (cmd != NAND_CMD_NONE) @@ -122,14 +121,14 @@ static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd, static void nand_davinci_select_chip(struct mtd_info *mtd, int chip) { struct davinci_nand_info *info = to_davinci_nand(mtd); - uint32_t addr = info->ioaddr; + + info->current_cs = info->vaddr; /* maybe kick in a second chipselect */ if (chip > 0) - addr |= info->mask_chipsel; - info->current_cs = addr; + info->current_cs += info->mask_chipsel; - info->chip.IO_ADDR_W = (void __iomem __force *)addr; + info->chip.IO_ADDR_W = info->current_cs; info->chip.IO_ADDR_R = info->chip.IO_ADDR_W; } @@ -680,9 +679,7 @@ static int nand_davinci_probe(struct platform_device *pdev) info->chip.bbt_md = pdata->bbt_md; info->timing = pdata->timing; - info->ioaddr = (uint32_t __force) vaddr; - - info->current_cs = info->ioaddr; + info->current_cs = info->vaddr; info->core_chipsel = pdata->core_chipsel; info->mask_chipsel = pdata->mask_chipsel; -- cgit v1.2.3 From cc53d5ca38c02e05efd20ba8af1759d1005aa454 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:29 +0200 Subject: mtd: rawnand: davinci: Use uintptr_t casts instead of unsigned ones uintptr_t should be used when casting a pointer to an unsigned int so that the code compiles without warnings even on 64-bit architectures. This is needed if we want to allow selection of this driver when COMPILE_TEST=y. Reported-by: Stephen Rothwell Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/davinci_nand.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index 9cd36a750965..e79ed0f60ade 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -318,7 +318,7 @@ static int nand_davinci_correct_4bit(struct mtd_info *mtd, /* Unpack ten bytes into eight 10 bit values. We know we're * little-endian, and use type punning for less shifting/masking. */ - if (WARN_ON(0x01 & (unsigned) ecc_code)) + if (WARN_ON(0x01 & (uintptr_t)ecc_code)) return -EINVAL; ecc16 = (unsigned short *)ecc_code; @@ -440,9 +440,9 @@ static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { struct nand_chip *chip = mtd_to_nand(mtd); - if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0) + if ((0x03 & ((uintptr_t)buf)) == 0 && (0x03 & len) == 0) ioread32_rep(chip->IO_ADDR_R, buf, len >> 2); - else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0) + else if ((0x01 & ((uintptr_t)buf)) == 0 && (0x01 & len) == 0) ioread16_rep(chip->IO_ADDR_R, buf, len >> 1); else ioread8_rep(chip->IO_ADDR_R, buf, len); @@ -453,9 +453,9 @@ static void nand_davinci_write_buf(struct mtd_info *mtd, { struct nand_chip *chip = mtd_to_nand(mtd); - if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0) + if ((0x03 & ((uintptr_t)buf)) == 0 && (0x03 & len) == 0) iowrite32_rep(chip->IO_ADDR_R, buf, len >> 2); - else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0) + else if ((0x01 & ((uintptr_t)buf)) == 0 && (0x01 & len) == 0) iowrite16_rep(chip->IO_ADDR_R, buf, len >> 1); else iowrite8_rep(chip->IO_ADDR_R, buf, len); -- cgit v1.2.3 From d3691813b05c654342fd01dfe3fff9a72828ebe7 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 18 Jul 2018 09:04:12 +0200 Subject: mtd: rawnand: fix indentation in Kconfig Rules about Kconfig are simple but in the raw NAND directory indentation is somehow archaic. Fix the indentation in the whole file. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 94 ++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 1488ad0a0199..2460fc19bfa9 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -44,12 +44,12 @@ config MTD_NAND_DENALI tristate config MTD_NAND_DENALI_PCI - tristate "Support Denali NAND controller on Intel Moorestown" + tristate "Support Denali NAND controller on Intel Moorestown" select MTD_NAND_DENALI depends on PCI - help - Enable the driver for NAND flash on Intel Moorestown, using the - Denali NAND controller core. + help + Enable the driver for NAND flash on Intel Moorestown, using the + Denali NAND controller core. config MTD_NAND_DENALI_DT tristate "Support Denali NAND controller as a DT device" @@ -80,7 +80,7 @@ config MTD_NAND_OMAP2 depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST depends on HAS_IOMEM help - Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4 + Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4 and Keystone platforms. config MTD_NAND_OMAP_BCH @@ -138,7 +138,7 @@ config MTD_NAND_NDFC depends on 4xx select MTD_NAND_ECC_SMC help - NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs + NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs config MTD_NAND_S3C2410_CLKSTOP bool "Samsung S3C NAND IDLE clock stop" @@ -170,40 +170,40 @@ config MTD_NAND_DISKONCHIP these devices. config MTD_NAND_DISKONCHIP_PROBE_ADVANCED - bool "Advanced detection options for DiskOnChip" - depends on MTD_NAND_DISKONCHIP - help - This option allows you to specify nonstandard address at which to - probe for a DiskOnChip, or to change the detection options. You - are unlikely to need any of this unless you are using LinuxBIOS. - Say 'N'. + bool "Advanced detection options for DiskOnChip" + depends on MTD_NAND_DISKONCHIP + help + This option allows you to specify nonstandard address at which to + probe for a DiskOnChip, or to change the detection options. You + are unlikely to need any of this unless you are using LinuxBIOS. + Say 'N'. config MTD_NAND_DISKONCHIP_PROBE_ADDRESS - hex "Physical address of DiskOnChip" if MTD_NAND_DISKONCHIP_PROBE_ADVANCED - depends on MTD_NAND_DISKONCHIP - default "0" - ---help--- - By default, the probe for DiskOnChip devices will look for a - DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. - This option allows you to specify a single address at which to probe - for the device, which is useful if you have other devices in that - range which get upset when they are probed. - - (Note that on PowerPC, the normal probe will only check at - 0xE4000000.) - - Normally, you should leave this set to zero, to allow the probe at - the normal addresses. + hex "Physical address of DiskOnChip" if MTD_NAND_DISKONCHIP_PROBE_ADVANCED + depends on MTD_NAND_DISKONCHIP + default "0" + help + By default, the probe for DiskOnChip devices will look for a + DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. + This option allows you to specify a single address at which to probe + for the device, which is useful if you have other devices in that + range which get upset when they are probed. + + (Note that on PowerPC, the normal probe will only check at + 0xE4000000.) + + Normally, you should leave this set to zero, to allow the probe at + the normal addresses. config MTD_NAND_DISKONCHIP_PROBE_HIGH - bool "Probe high addresses" - depends on MTD_NAND_DISKONCHIP_PROBE_ADVANCED - help - By default, the probe for DiskOnChip devices will look for a - DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. - This option changes to make it probe between 0xFFFC8000 and - 0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be - useful to you. Say 'N'. + bool "Probe high addresses" + depends on MTD_NAND_DISKONCHIP_PROBE_ADVANCED + help + By default, the probe for DiskOnChip devices will look for a + DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. + This option changes to make it probe between 0xFFFC8000 and + 0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be + useful to you. Say 'N'. config MTD_NAND_DISKONCHIP_BBTWRITE bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP" @@ -345,13 +345,13 @@ config MTD_NAND_NANDSIM MTD nand layer. config MTD_NAND_GPMI_NAND - tristate "GPMI NAND Flash Controller driver" - depends on MXS_DMA - help - Enables NAND Flash support for IMX23, IMX28 or IMX6. - The GPMI controller is very powerful, with the help of BCH - module, it can do the hardware ECC. The GPMI supports several - NAND flashs at the same time. + tristate "GPMI NAND Flash Controller driver" + depends on MXS_DMA + help + Enables NAND Flash support for IMX23, IMX28 or IMX6. + The GPMI controller is very powerful, with the help of BCH + module, it can do the hardware ECC. The GPMI supports several + NAND flashs at the same time. config MTD_NAND_BRCMNAND tristate "Broadcom STB NAND controller" @@ -459,9 +459,9 @@ config MTD_NAND_SH_FLCTL for NAND Flash using FLCTL. config MTD_NAND_DAVINCI - tristate "Support NAND on DaVinci/Keystone SoC" - depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF) - help + tristate "Support NAND on DaVinci/Keystone SoC" + depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF) + help Enable the driver for NAND flash chips on Texas Instruments DaVinci/Keystone processors. @@ -489,7 +489,7 @@ config MTD_NAND_JZ4740 tristate "Support for JZ4740 SoC NAND controller" depends on MACH_JZ4740 help - Enables support for NAND Flash on JZ4740 SoC based boards. + Enables support for NAND Flash on JZ4740 SoC based boards. config MTD_NAND_JZ4780 tristate "Support for NAND on JZ4780 SoC" -- cgit v1.2.3 From b9221470b081c4dc52de859d47e2431e76b1fac2 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:30 +0200 Subject: mtd: rawnand: davinci: Allow selection of this driver when COMPILE_TEST=y It just makes NAND maintainers' life easier by allowing them to compile-test this driver without having ARCH_DAVINCI or ARCH_KEYSTONE enabled. We also need to add a dependency on HAS_IOMEM to make sure the driver compiles correctly. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 2460fc19bfa9..524684239fdb 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -460,7 +460,8 @@ config MTD_NAND_SH_FLCTL config MTD_NAND_DAVINCI tristate "Support NAND on DaVinci/Keystone SoC" - depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF) + depends on ARCH_DAVINCI || (ARCH_KEYSTONE && TI_AEMIF) || COMPILE_TEST + depends on HAS_IOMEM help Enable the driver for NAND flash chips on Texas Instruments DaVinci/Keystone processors. -- cgit v1.2.3 From cf3e3fd2e94f4648f17fbd5e0e26409d5d1face9 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:31 +0200 Subject: mtd: rawnand: sunxi: Add an U suffix to NFC_PAGE_OP definition Fixes the "warning: large integer implicitly truncated to unsigned type [-Woverflow]" warning when compiled for x86. This is needed in order to allow compiling this driver when COMPILE_TEST=y. Reported-by: Stephen Rothwell Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/sunxi_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index d831a141a196..99043c3a4fa7 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -127,7 +127,7 @@ #define NFC_CMD_TYPE_MSK GENMASK(31, 30) #define NFC_NORMAL_OP (0 << 30) #define NFC_ECC_OP (1 << 30) -#define NFC_PAGE_OP (2 << 30) +#define NFC_PAGE_OP (2U << 30) /* define bit use in NFC_RCMD_SET */ #define NFC_READ_CMD_MSK GENMASK(7, 0) -- cgit v1.2.3 From 06c8b5dc938117866460ee95b79318847dbde209 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:32 +0200 Subject: mtd: rawnand: sunxi: Make sure ret is initialized in sunxi_nfc_read_byte() Fixes the following smatch warning: drivers/mtd/nand/raw/sunxi_nand.c:551 sunxi_nfc_read_byte() error: uninitialized symbol 'ret'. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/sunxi_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 99043c3a4fa7..4b11cd4a79be 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -544,7 +544,7 @@ static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd) { - uint8_t ret; + uint8_t ret = 0; sunxi_nfc_read_buf(mtd, &ret, 1); -- cgit v1.2.3 From e37db6df41f5d45e43d5544b2cfadeed1710f086 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:33 +0200 Subject: mtd: rawnand: sunxi: Allow selection of this driver when COMPILE_TEST=y It just makes NAND maintainers' life easier by allowing them to compile-test this driver without having ARCH_SUNXI enabled. We also need to add a dependency on HAS_IOMEM to make sure the driver compiles correctly. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 524684239fdb..cd9eb60faff6 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -516,7 +516,8 @@ config MTD_NAND_XWAY config MTD_NAND_SUNXI tristate "Support for NAND on Allwinner SoCs" - depends on ARCH_SUNXI + depends on ARCH_SUNXI || COMPILE_TEST + depends on HAS_IOMEM help Enables support for NAND Flash chips on Allwinner SoCs. -- cgit v1.2.3 From 8f3931ed975e1d775b87ce85d65ecacd54138359 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:34 +0200 Subject: mtd: rawnand: fscm: Avoid collision on PC def when compiling for MIPS We want to allow this driver to be selected when COMPILE_TEST=y, this means the driver can be compiled for any arch, including MIPS. When compiling this driver for MIPS, we end up with a collision on the 'PC' macro definition (also defined in arch/mips/include/asm/ptrace.h). Prefix the fsmc one with FSMC_ to avoid this problem. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/fsmc_nand.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index f4a5a317d4ae..d71c49f50e77 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -62,7 +62,7 @@ reg) /* fsmc controller registers for NAND flash */ -#define PC 0x00 +#define FSMC_PC 0x00 /* pc register definitions */ #define FSMC_RESET (1 << 0) #define FSMC_WAITON (1 << 1) @@ -273,12 +273,13 @@ static void fsmc_nand_setup(struct fsmc_nand_data *host, tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT; if (host->nand.options & NAND_BUSWIDTH_16) - writel_relaxed(value | FSMC_DEVWID_16, host->regs_va + PC); + writel_relaxed(value | FSMC_DEVWID_16, + host->regs_va + FSMC_PC); else - writel_relaxed(value | FSMC_DEVWID_8, host->regs_va + PC); + writel_relaxed(value | FSMC_DEVWID_8, host->regs_va + FSMC_PC); - writel_relaxed(readl(host->regs_va + PC) | tclr | tar, - host->regs_va + PC); + writel_relaxed(readl(host->regs_va + FSMC_PC) | tclr | tar, + host->regs_va + FSMC_PC); writel_relaxed(thiz | thold | twait | tset, host->regs_va + COMM); writel_relaxed(thiz | thold | twait | tset, host->regs_va + ATTRIB); } @@ -371,12 +372,12 @@ static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode) { struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - writel_relaxed(readl(host->regs_va + PC) & ~FSMC_ECCPLEN_256, - host->regs_va + PC); - writel_relaxed(readl(host->regs_va + PC) & ~FSMC_ECCEN, - host->regs_va + PC); - writel_relaxed(readl(host->regs_va + PC) | FSMC_ECCEN, - host->regs_va + PC); + writel_relaxed(readl(host->regs_va + FSMC_PC) & ~FSMC_ECCPLEN_256, + host->regs_va + FSMC_PC); + writel_relaxed(readl(host->regs_va + FSMC_PC) & ~FSMC_ECCEN, + host->regs_va + FSMC_PC); + writel_relaxed(readl(host->regs_va + FSMC_PC) | FSMC_ECCEN, + host->regs_va + FSMC_PC); } /* @@ -618,11 +619,11 @@ static void fsmc_select_chip(struct mtd_info *mtd, int chipnr) if (chipnr > 0) return; - pc = readl(host->regs_va + PC); + pc = readl(host->regs_va + FSMC_PC); if (chipnr < 0) - writel_relaxed(pc & ~FSMC_ENABLE, host->regs_va + PC); + writel_relaxed(pc & ~FSMC_ENABLE, host->regs_va + FSMC_PC); else - writel_relaxed(pc | FSMC_ENABLE, host->regs_va + PC); + writel_relaxed(pc | FSMC_ENABLE, host->regs_va + FSMC_PC); /* nCE line must be asserted before starting any operation */ mb(); -- cgit v1.2.3 From f55824c6faef1df97439094a30b9f21fd2ce60a8 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:35 +0200 Subject: mtd: rawnand: fsmc: Use uintptr_t casts instead of unsigned ones uintptr_t should be used when casting a pointer to an unsigned int so that the code compiles without warnings even on 64-bit architectures. This is needed if we want to allow selection of this driver when COMPILE_TEST=y. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/fsmc_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index d71c49f50e77..527bebc7e6c9 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -547,7 +547,7 @@ static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) struct fsmc_nand_data *host = mtd_to_fsmc(mtd); int i; - if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) && + if (IS_ALIGNED((uintptr_t)buf, sizeof(uint32_t)) && IS_ALIGNED(len, sizeof(uint32_t))) { uint32_t *p = (uint32_t *)buf; len = len >> 2; @@ -570,7 +570,7 @@ static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) struct fsmc_nand_data *host = mtd_to_fsmc(mtd); int i; - if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) && + if (IS_ALIGNED((uintptr_t)buf, sizeof(uint32_t)) && IS_ALIGNED(len, sizeof(uint32_t))) { uint32_t *p = (uint32_t *)buf; len = len >> 2; -- cgit v1.2.3 From 17c09ed706de451b7b449fd57198675349b58105 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:36 +0200 Subject: mtd: rawnand: fsmc: Allow selection of this driver when COMPILE_TEST=y It just makes NAND maintainers' life easier by allowing them to compile-test this driver without having PLAT_SPEAR, ARCH_NOMADIK, ARCH_U8500 or MACH_U300 enabled. We also need to add a dependency on HAS_IOMEM to make sure the driver compiles correctly. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index cd9eb60faff6..a47ed653afe5 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -501,8 +501,9 @@ config MTD_NAND_JZ4780 config MTD_NAND_FSMC tristate "Support for NAND on ST Micros FSMC" - depends on OF - depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300 + depends on OF && HAS_IOMEM + depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300 || \ + COMPILE_TEST help Enables support for NAND Flash chips on the ST Microelectronics Flexible Static Memory Controller (FSMC) -- cgit v1.2.3 From b30a2bd4ee9d3933ce397ab83a0b713e9423ec99 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:37 +0200 Subject: memory: fsl_ifc: Allow selection of this driver when COMPILE_TEST=y It just makes maintainers' life easier by allowing them to compile-test this driver without having FSL_SOC, ARCH_LAYERSCAPE or SOC_LS1021A enabled. We also need to add a dependency on HAS_IOMEM to make sure the driver compiles correctly. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/memory/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 8d731d6c3e54..78457ab2cbc4 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -116,7 +116,8 @@ config FSL_CORENET_CF config FSL_IFC bool - depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A + depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A || COMPILE_TEST + depends on HAS_IOMEM config JZ4780_NEMC bool "Ingenic JZ4780 SoC NEMC driver" -- cgit v1.2.3 From 62a316088dd07b31587e1874f2f9e4d096ef0da1 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:38 +0200 Subject: mtd: rawnand: fsl_ifc: Add an __iomem specifier on eccstat_regs The local eccstat_regs variable in fsl_ifc_run_command() is missing an __iomem specifier, and sparce complains about that. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/fsl_ifc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index 382b67e97174..75d3c951f61a 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -225,7 +225,7 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) int bufnum = nctrl->page & priv->bufnum_mask; int sector_start = bufnum * chip->ecc.steps; int sector_end = sector_start + chip->ecc.steps - 1; - __be32 *eccstat_regs; + __be32 __iomem *eccstat_regs; eccstat_regs = ifc->ifc_nand.nand_eccstat; eccstat = ifc_in32(&eccstat_regs[sector_start / 4]); -- cgit v1.2.3 From a6be5051cdca8069abfe016a70ce9243ba1a2c0d Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:39 +0200 Subject: mtd: rawnand: fsl_ifc: Allow selection of this driver when COMPILE_TEST=y It just makes maintainers' life easier by allowing them to compile-test this driver without having FSL_SOC, ARCH_LAYERSCAPE or SOC_LS1021A enabled. We also need to add a dependency on HAS_IOMEM to make sure the driver compiles correctly. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index a47ed653afe5..30a953aaba3a 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -406,7 +406,8 @@ config MTD_NAND_FSL_ELBC config MTD_NAND_FSL_IFC tristate "NAND support for Freescale IFC controller" - depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A + depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A || COMPILE_TEST + depends on HAS_IOMEM select FSL_IFC select MEMORY help -- cgit v1.2.3 From dc2865ac3527d76fe04ee1f1a3ffc101a60faba0 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:40 +0200 Subject: MIPS: txx9: Move the ndfc.h header to include/linux/platform_data/txx9 This way we will be able to compile the ndfmc driver when COMPILE_TEST=y. Signed-off-by: Boris Brezillon Acked-by: Paul Burton Signed-off-by: Miquel Raynal --- arch/mips/include/asm/txx9/ndfmc.h | 30 ------------------------------ arch/mips/txx9/generic/setup.c | 2 +- arch/mips/txx9/generic/setup_tx4938.c | 2 +- arch/mips/txx9/generic/setup_tx4939.c | 2 +- drivers/mtd/nand/raw/txx9ndfmc.c | 2 +- include/linux/platform_data/txx9/ndfmc.h | 30 ++++++++++++++++++++++++++++++ 6 files changed, 34 insertions(+), 34 deletions(-) delete mode 100644 arch/mips/include/asm/txx9/ndfmc.h create mode 100644 include/linux/platform_data/txx9/ndfmc.h (limited to 'drivers') diff --git a/arch/mips/include/asm/txx9/ndfmc.h b/arch/mips/include/asm/txx9/ndfmc.h deleted file mode 100644 index fa67f3df78fc..000000000000 --- a/arch/mips/include/asm/txx9/ndfmc.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * (C) Copyright TOSHIBA CORPORATION 2007 - */ -#ifndef __ASM_TXX9_NDFMC_H -#define __ASM_TXX9_NDFMC_H - -#define NDFMC_PLAT_FLAG_USE_BSPRT 0x01 -#define NDFMC_PLAT_FLAG_NO_RSTR 0x02 -#define NDFMC_PLAT_FLAG_HOLDADD 0x04 -#define NDFMC_PLAT_FLAG_DUMMYWRITE 0x08 - -struct txx9ndfmc_platform_data { - unsigned int shift; - unsigned int gbus_clock; - unsigned int hold; /* hold time in nanosecond */ - unsigned int spw; /* strobe pulse width in nanosecond */ - unsigned int flags; - unsigned char ch_mask; /* available channel bitmask */ - unsigned char wp_mask; /* write-protect bitmask */ - unsigned char wide_mask; /* 16bit-nand bitmask */ -}; - -void txx9_ndfmc_init(unsigned long baseaddr, - const struct txx9ndfmc_platform_data *plat_data); - -#endif /* __ASM_TXX9_NDFMC_H */ diff --git a/arch/mips/txx9/generic/setup.c b/arch/mips/txx9/generic/setup.c index 1791a44ee570..aa47932abd28 100644 --- a/arch/mips/txx9/generic/setup.c +++ b/arch/mips/txx9/generic/setup.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -35,7 +36,6 @@ #include #include #include -#include #include #ifdef CONFIG_CPU_TX49XX #include diff --git a/arch/mips/txx9/generic/setup_tx4938.c b/arch/mips/txx9/generic/setup_tx4938.c index 85d1795652da..17395d5d15ca 100644 --- a/arch/mips/txx9/generic/setup_tx4938.c +++ b/arch/mips/txx9/generic/setup_tx4938.c @@ -17,13 +17,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include diff --git a/arch/mips/txx9/generic/setup_tx4939.c b/arch/mips/txx9/generic/setup_tx4939.c index 274928987a21..360c388f4c82 100644 --- a/arch/mips/txx9/generic/setup_tx4939.c +++ b/arch/mips/txx9/generic/setup_tx4939.c @@ -21,13 +21,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c index b567d212fe7d..04d57474ef97 100644 --- a/drivers/mtd/nand/raw/txx9ndfmc.c +++ b/drivers/mtd/nand/raw/txx9ndfmc.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include /* TXX9 NDFMC Registers */ #define TXX9_NDFDTR 0x00 diff --git a/include/linux/platform_data/txx9/ndfmc.h b/include/linux/platform_data/txx9/ndfmc.h new file mode 100644 index 000000000000..fc172627d54e --- /dev/null +++ b/include/linux/platform_data/txx9/ndfmc.h @@ -0,0 +1,30 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * (C) Copyright TOSHIBA CORPORATION 2007 + */ +#ifndef __TXX9_NDFMC_H +#define __TXX9_NDFMC_H + +#define NDFMC_PLAT_FLAG_USE_BSPRT 0x01 +#define NDFMC_PLAT_FLAG_NO_RSTR 0x02 +#define NDFMC_PLAT_FLAG_HOLDADD 0x04 +#define NDFMC_PLAT_FLAG_DUMMYWRITE 0x08 + +struct txx9ndfmc_platform_data { + unsigned int shift; + unsigned int gbus_clock; + unsigned int hold; /* hold time in nanosecond */ + unsigned int spw; /* strobe pulse width in nanosecond */ + unsigned int flags; + unsigned char ch_mask; /* available channel bitmask */ + unsigned char wp_mask; /* write-protect bitmask */ + unsigned char wide_mask; /* 16bit-nand bitmask */ +}; + +void txx9_ndfmc_init(unsigned long baseaddr, + const struct txx9ndfmc_platform_data *plat_data); + +#endif /* __TXX9_NDFMC_H */ -- cgit v1.2.3 From eaea2e7300a23e9e40d15ff9b80c7407e3e7802c Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:41 +0200 Subject: mtd: rawnand: txx9ndfmc: Allow selection of this driver when COMPILE_TEST=y It just makes NAND maintainers' life easier by allowing them to compile-test this driver without having SOC_TX4938 or SOC_TX4939 enabled. We also need to add a dependency on HAS_IOMEM to make sure the driver compiles correctly. Signed-off-by: Boris Brezillon Acked-by: Paul Burton Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 30a953aaba3a..2898016809f0 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -469,7 +469,8 @@ config MTD_NAND_DAVINCI config MTD_NAND_TXX9NDFMC tristate "NAND Flash support for TXx9 SoC" - depends on SOC_TX4938 || SOC_TX4939 + depends on SOC_TX4938 || SOC_TX4939 || COMPILE_TEST + depends on HAS_IOMEM help This enables the NAND flash controller on the TXx9 SoCs. -- cgit v1.2.3 From e65e3a50702f4e7a01c0b36ff08d6ed8dde7ee7b Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:42 +0200 Subject: MIPS: jz4740: Move jz4740_nand.h header to include/linux/platform_data/jz4740 This way we will be able to compile the jz4740_nand driver when COMPILE_TEST=y. Signed-off-by: Boris Brezillon Acked-by: Paul Burton Signed-off-by: Miquel Raynal --- arch/mips/include/asm/mach-jz4740/jz4740_nand.h | 34 ------------------------ arch/mips/jz4740/board-qi_lb60.c | 3 ++- drivers/mtd/nand/raw/jz4740_nand.c | 2 +- include/linux/platform_data/jz4740/jz4740_nand.h | 34 ++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 36 deletions(-) delete mode 100644 arch/mips/include/asm/mach-jz4740/jz4740_nand.h create mode 100644 include/linux/platform_data/jz4740/jz4740_nand.h (limited to 'drivers') diff --git a/arch/mips/include/asm/mach-jz4740/jz4740_nand.h b/arch/mips/include/asm/mach-jz4740/jz4740_nand.h deleted file mode 100644 index f381d465e768..000000000000 --- a/arch/mips/include/asm/mach-jz4740/jz4740_nand.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2009-2010, Lars-Peter Clausen - * JZ4740 SoC NAND controller driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#ifndef __ASM_MACH_JZ4740_JZ4740_NAND_H__ -#define __ASM_MACH_JZ4740_JZ4740_NAND_H__ - -#include -#include - -#define JZ_NAND_NUM_BANKS 4 - -struct jz_nand_platform_data { - int num_partitions; - struct mtd_partition *partitions; - - unsigned char banks[JZ_NAND_NUM_BANKS]; - - void (*ident_callback)(struct platform_device *, struct mtd_info *, - struct mtd_partition **, int *num_partitions); -}; - -#endif diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c index 60f0767507c6..af0c8ace0141 100644 --- a/arch/mips/jz4740/board-qi_lb60.c +++ b/arch/mips/jz4740/board-qi_lb60.c @@ -29,10 +29,11 @@ #include #include +#include + #include #include #include -#include #include #include diff --git a/drivers/mtd/nand/raw/jz4740_nand.c b/drivers/mtd/nand/raw/jz4740_nand.c index 613b00a9604b..a0254461812d 100644 --- a/drivers/mtd/nand/raw/jz4740_nand.c +++ b/drivers/mtd/nand/raw/jz4740_nand.c @@ -25,7 +25,7 @@ #include -#include +#include #define JZ_REG_NAND_CTRL 0x50 #define JZ_REG_NAND_ECC_CTRL 0x100 diff --git a/include/linux/platform_data/jz4740/jz4740_nand.h b/include/linux/platform_data/jz4740/jz4740_nand.h new file mode 100644 index 000000000000..bc571f6d5ced --- /dev/null +++ b/include/linux/platform_data/jz4740/jz4740_nand.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2009-2010, Lars-Peter Clausen + * JZ4740 SoC NAND controller driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __JZ4740_NAND_H__ +#define __JZ4740_NAND_H__ + +#include +#include + +#define JZ_NAND_NUM_BANKS 4 + +struct jz_nand_platform_data { + int num_partitions; + struct mtd_partition *partitions; + + unsigned char banks[JZ_NAND_NUM_BANKS]; + + void (*ident_callback)(struct platform_device *, struct mtd_info *, + struct mtd_partition **, int *num_partitions); +}; + +#endif -- cgit v1.2.3 From 6968e07e816906cd4b8150ef2e3af533664f734b Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:43 +0200 Subject: mtd: rawnand: jz4740: Allow selection of this driver when COMPILE_TEST=y It just makes NAND maintainers' life easier by allowing them to compile-test this driver without having MACH_JZ4740 enabled. We also need to add a dependency on HAS_IOMEM to make sure the driver compiles correctly. Signed-off-by: Boris Brezillon Acked-by: Paul Burton Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 2898016809f0..0711bd714cd5 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -490,7 +490,8 @@ config MTD_NAND_NUC900 config MTD_NAND_JZ4740 tristate "Support for JZ4740 SoC NAND controller" - depends on MACH_JZ4740 + depends on MACH_JZ4740 || COMPILE_TEST + depends on HAS_IOMEM help Enables support for NAND Flash on JZ4740 SoC based boards. -- cgit v1.2.3 From b8f0fadab208d87cab715b7599e0c15b4b2cc55f Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:44 +0200 Subject: mtd: rawnand: jz4780: Drop the dependency on MACH_JZ4780 This MACH_JZ4780 dependency is taken care of by JZ4780_NEMC, no need to repeat it here. Signed-off-by: Boris Brezillon Acked-by: Paul Burton Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 0711bd714cd5..d495759c94ac 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -497,7 +497,7 @@ config MTD_NAND_JZ4740 config MTD_NAND_JZ4780 tristate "Support for NAND on JZ4780 SoC" - depends on MACH_JZ4780 && JZ4780_NEMC + depends on JZ4780_NEMC help Enables support for NAND Flash connected to the NEMC on JZ4780 SoC based boards, using the BCH controller for hardware error correction. -- cgit v1.2.3 From 72b77bb018b3e913504fdb0a0612135e5391521e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 19 Jul 2018 09:37:20 +0200 Subject: mtd: rawnand: jz4740: Use the proper format specifier to print chipnr In jz_nand_detect_bank(), chipnr is a size_t argument. Use %zu instead of %i when printing it. Reported-by: Stephen Rothwell Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/jz4740_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/jz4740_nand.c b/drivers/mtd/nand/raw/jz4740_nand.c index a0254461812d..c5dea83b9ea0 100644 --- a/drivers/mtd/nand/raw/jz4740_nand.c +++ b/drivers/mtd/nand/raw/jz4740_nand.c @@ -355,7 +355,7 @@ static int jz_nand_detect_bank(struct platform_device *pdev, mtd->size += chip->chipsize; } - dev_info(&pdev->dev, "Found chip %i on bank %i\n", chipnr, bank); + dev_info(&pdev->dev, "Found chip %zu on bank %i\n", chipnr, bank); return 0; notfound_id: -- cgit v1.2.3 From ab99e11062c187533db08078ccae799faed8873e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 9 Jul 2018 22:09:45 +0200 Subject: memory: jz4780-nemc: Allow selection of this driver when COMPILE_TEST=y It just makes maintainers' life easier by allowing them to compile-test this driver without having MACH_JZ4780 enabled. We also need to add a dependency on HAS_IOMEM to make sure the driver compiles correctly. Signed-off-by: Boris Brezillon Acked-by: Paul Burton Signed-off-by: Miquel Raynal --- drivers/memory/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 78457ab2cbc4..a642552dfdc9 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -122,7 +122,8 @@ config FSL_IFC config JZ4780_NEMC bool "Ingenic JZ4780 SoC NEMC driver" default y - depends on MACH_JZ4780 + depends on MACH_JZ4780 || COMPILE_TEST + depends on HAS_IOMEM help This driver is for the NAND/External Memory Controller (NEMC) in the Ingenic JZ4780. This controller is used to handle external -- cgit v1.2.3 From c2204734bb50899764129ed5568b8d5aa013c8fb Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 11 Jul 2018 10:39:44 +0200 Subject: mtd: rawnand: MTD_NAND_BCM47XXNFLASH needs CONFIG_BCMA We already have a dependency on BCMA_NFLASH, which in turn depends on BCMA, but since BCMA is a tristate option and BCMA_NFLASH is bool, we can run into an invalid configuration with MTD_NAND_BCM47XXNFLASH=y and BCMA=m: drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.o: In function `bcm47xxnflash_ops_bcm4706_init': ops_bcm4706.c:(.text+0x790): undefined reference to `bcma_chipco_pll_read' Adding the dependency here forces MTD_NAND_BCM47XXNFLASH to only be configured =m here so it can link against the BCMA driver. Signed-off-by: Arnd Bergmann Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index d495759c94ac..b6738ece16f1 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -365,6 +365,7 @@ config MTD_NAND_BRCMNAND config MTD_NAND_BCM47XXNFLASH tristate "Support for NAND flash on BCM4706 BCMA bus" depends on BCMA_NFLASH + depends on BCMA help BCMA bus can have various flash memories attached, they are registered by bcma as platform devices. This enables driver for -- cgit v1.2.3 From 29597ca14dee1611eafbc09ae0c6bd756687105f Mon Sep 17 00:00:00 2001 From: RafaÅ‚ MiÅ‚ecki Date: Fri, 13 Jul 2018 11:27:31 +0200 Subject: mtd: rawnand: use mtd_device_register() where applicable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If driver doesn't specify parsers it can use that little helper. Signed-off-by: RafaÅ‚ MiÅ‚ecki Acked-by: Xiaolei Li Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/cmx270_nand.c | 4 ++-- drivers/mtd/nand/raw/cs553x_nand.c | 3 +-- drivers/mtd/nand/raw/davinci_nand.c | 3 +-- drivers/mtd/nand/raw/jz4740_nand.c | 5 ++--- drivers/mtd/nand/raw/mtk_nand.c | 2 +- drivers/mtd/nand/raw/s3c2410.c | 4 ++-- drivers/mtd/nand/raw/txx9ndfmc.c | 2 +- 7 files changed, 10 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/cmx270_nand.c b/drivers/mtd/nand/raw/cmx270_nand.c index 02d6751e9efe..b66e254b6802 100644 --- a/drivers/mtd/nand/raw/cmx270_nand.c +++ b/drivers/mtd/nand/raw/cmx270_nand.c @@ -200,8 +200,8 @@ static int __init cmx270_init(void) } /* Register the partitions */ - ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, NULL, - partition_info, NUM_PARTITIONS); + ret = mtd_device_register(cmx270_nand_mtd, partition_info, + NUM_PARTITIONS); if (ret) goto err_scan; diff --git a/drivers/mtd/nand/raw/cs553x_nand.c b/drivers/mtd/nand/raw/cs553x_nand.c index 82269fde9e66..beafad62e7d5 100644 --- a/drivers/mtd/nand/raw/cs553x_nand.c +++ b/drivers/mtd/nand/raw/cs553x_nand.c @@ -310,8 +310,7 @@ static int __init cs553x_init(void) for (i = 0; i < NR_CS553X_CONTROLLERS; i++) { if (cs553x_mtd[i]) { /* If any devices registered, return success. Else the last error. */ - mtd_device_parse_register(cs553x_mtd[i], NULL, NULL, - NULL, 0); + mtd_device_register(cs553x_mtd[i], NULL, 0); err = 0; } } diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index e79ed0f60ade..626c9363e460 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -800,8 +800,7 @@ static int nand_davinci_probe(struct platform_device *pdev) goto err; if (pdata->parts) - ret = mtd_device_parse_register(mtd, NULL, NULL, - pdata->parts, pdata->nr_parts); + ret = mtd_device_register(mtd, pdata->parts, pdata->nr_parts); else ret = mtd_device_register(mtd, NULL, 0); if (ret < 0) diff --git a/drivers/mtd/nand/raw/jz4740_nand.c b/drivers/mtd/nand/raw/jz4740_nand.c index c5dea83b9ea0..9bb8a89e09f9 100644 --- a/drivers/mtd/nand/raw/jz4740_nand.c +++ b/drivers/mtd/nand/raw/jz4740_nand.c @@ -466,9 +466,8 @@ static int jz_nand_probe(struct platform_device *pdev) goto err_unclaim_banks; } - ret = mtd_device_parse_register(mtd, NULL, NULL, - pdata ? pdata->partitions : NULL, - pdata ? pdata->num_partitions : 0); + ret = mtd_device_register(mtd, pdata ? pdata->partitions : NULL, + pdata ? pdata->num_partitions : 0); if (ret) { dev_err(&pdev->dev, "Failed to add mtd device\n"); diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index 75c845adb050..e6b14b79c8a8 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -1357,7 +1357,7 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, if (ret) return ret; - ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0); + ret = mtd_device_register(mtd, NULL, 0); if (ret) { dev_err(dev, "mtd parse partition error\n"); nand_release(mtd); diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c index 19661c5d3220..10d81f367d26 100644 --- a/drivers/mtd/nand/raw/s3c2410.c +++ b/drivers/mtd/nand/raw/s3c2410.c @@ -802,8 +802,8 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, mtdinfo->name = set->name; - return mtd_device_parse_register(mtdinfo, NULL, NULL, - set->partitions, set->nr_partitions); + return mtd_device_register(mtdinfo, set->partitions, + set->nr_partitions); } return -ENODEV; diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c index 04d57474ef97..5fe9da8b4a0a 100644 --- a/drivers/mtd/nand/raw/txx9ndfmc.c +++ b/drivers/mtd/nand/raw/txx9ndfmc.c @@ -366,7 +366,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) } mtd->name = txx9_priv->mtdname; - mtd_device_parse_register(mtd, NULL, NULL, NULL, 0); + mtd_device_register(mtd, NULL, 0); drvdata->mtds[i] = mtd; } -- cgit v1.2.3 From e8b0ac398f5ec16b249f5243d8011d490e3b91c0 Mon Sep 17 00:00:00 2001 From: RafaÅ‚ MiÅ‚ecki Date: Fri, 13 Jul 2018 11:27:32 +0200 Subject: mtd: onenand: use mtd_device_register() where applicable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If driver doesn't specify parsers it can use that little helper. Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: Miquel Raynal --- drivers/mtd/nand/onenand/generic.c | 5 ++--- drivers/mtd/nand/onenand/samsung.c | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/onenand/generic.c b/drivers/mtd/nand/onenand/generic.c index d5ccaf943b91..acad17ec6581 100644 --- a/drivers/mtd/nand/onenand/generic.c +++ b/drivers/mtd/nand/onenand/generic.c @@ -66,9 +66,8 @@ static int generic_onenand_probe(struct platform_device *pdev) goto out_iounmap; } - err = mtd_device_parse_register(&info->mtd, NULL, NULL, - pdata ? pdata->parts : NULL, - pdata ? pdata->nr_parts : 0); + err = mtd_device_register(&info->mtd, pdata ? pdata->parts : NULL, + pdata ? pdata->nr_parts : 0); platform_set_drvdata(pdev, info); diff --git a/drivers/mtd/nand/onenand/samsung.c b/drivers/mtd/nand/onenand/samsung.c index 4cce4c0311ca..e64d0fdf7eb5 100644 --- a/drivers/mtd/nand/onenand/samsung.c +++ b/drivers/mtd/nand/onenand/samsung.c @@ -933,9 +933,8 @@ static int s3c_onenand_probe(struct platform_device *pdev) if (s3c_read_reg(MEM_CFG_OFFSET) & ONENAND_SYS_CFG1_SYNC_READ) dev_info(&onenand->pdev->dev, "OneNAND Sync. Burst Read enabled\n"); - err = mtd_device_parse_register(mtd, NULL, NULL, - pdata ? pdata->parts : NULL, - pdata ? pdata->nr_parts : 0); + err = mtd_device_register(mtd, pdata ? pdata->parts : NULL, + pdata ? pdata->nr_parts : 0); if (err) { dev_err(&pdev->dev, "failed to parse partitions and register the MTD device\n"); onenand_release(mtd); -- cgit v1.2.3 From 6a943386ee364155727f62711f6f3e898d824cdd Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 14 Jul 2018 12:23:54 +0200 Subject: mtd: rawnand: add default values for dynamic timings Some timings like tBERS (block erase time), tCCs (change column setup time), tPROG (page program time) and tR (page read time) are derived from the ONFI parameter page. They are set in the SDR interface only if the chip is ONFI compliant. It makes these timings unreliable and prevent the driver to use one of these four values with a non-ONFI chip. Fix this situation by taking the highest possible value (or a default one) value for each missing timing (stored as unsigned 16-bit entries in the parameter page). This makes tBERS and tPROG being ~65ms while typical values are at most a few milliseconds. As these are timeouts, it is not impacting at all the performances in nominal use. tR maximum time and tCCS minimum time (delay to wait after a change column) are set, according to the ONFI specification, to default 'slow' values; respectively 200us and 500ns. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_timings.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_timings.c b/drivers/mtd/nand/raw/nand_timings.c index 7c4e4a371bbc..9bb599106a31 100644 --- a/drivers/mtd/nand/raw/nand_timings.c +++ b/drivers/mtd/nand/raw/nand_timings.c @@ -13,6 +13,8 @@ #include #include +#define ONFI_DYN_TIMING_MAX U16_MAX + static const struct nand_data_interface onfi_sdr_timings[] = { /* Mode 0 */ { @@ -303,7 +305,7 @@ int onfi_fill_data_interface(struct nand_chip *chip, /* * Initialize timings that cannot be deduced from timing mode: - * tR, tPROG, tCCS, ... + * tPROG, tBERS, tR and tCCS. * These information are part of the ONFI parameter page. */ if (chip->parameters.onfi.version) { @@ -317,6 +319,22 @@ int onfi_fill_data_interface(struct nand_chip *chip, /* nanoseconds -> picoseconds */ timings->tCCS_min = 1000UL * params->onfi.tCCS; + } else { + struct nand_sdr_timings *timings = &iface->timings.sdr; + /* + * For non-ONFI chips we use the highest possible value for + * tPROG and tBERS. tR and tCCS will take the default values + * precised in the ONFI specification for timing mode 0, + * respectively 200us and 500ns. + */ + + /* microseconds -> picoseconds */ + timings->tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX; + timings->tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX; + timings->tR_max = 1000000ULL * 200000000ULL; + + /* nanoseconds -> picoseconds */ + timings->tCCS_min = 1000UL * 500000; } return 0; -- cgit v1.2.3 From d535934a9ad4dc435884958ac44a7d6ecb6278ce Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 17 Jul 2018 10:46:18 +0200 Subject: mtd: rawnand: tegra: check bounds of die_nr properly The Tegra driver currently only support a single chip select, hence check boundaries accordingly. This fixes a off by one issue catched with Smatch: drivers/mtd/nand/raw/tegra_nand.c:476 tegra_nand_select_chip() warn: array off by one? 'nand->cs[die_nr]' Also warn in case the stack asks for a chip select we currently do not support. Reported-by: Dan Carpenter Signed-off-by: Stefan Agner Signed-off-by: Marcel Ziswiler Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/tegra_nand.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c index 9f7de36be893..56c0aa1bc81f 100644 --- a/drivers/mtd/nand/raw/tegra_nand.c +++ b/drivers/mtd/nand/raw/tegra_nand.c @@ -468,7 +468,9 @@ static void tegra_nand_select_chip(struct mtd_info *mtd, int die_nr) struct tegra_nand_chip *nand = to_tegra_chip(chip); struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); - if (die_nr < 0 || die_nr > 1) { + WARN_ON(die_nr >= (int)ARRAY_SIZE(nand->cs)); + + if (die_nr < 0 || die_nr > 0) { ctrl->cur_cs = -1; return; } -- cgit v1.2.3 From 7330fc505af4af262ef874a4fc4492393106fdce Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 17 Jul 2018 22:27:42 +0200 Subject: mtd: rawnand: qcom: stop using phys_to_dma() Compile-testing this driver on x86 caused a link error: ERROR: "__phys_to_dma" [drivers/mtd/nand/raw/qcom_nandc.ko] undefined! The problem here is that the driver attempts to convert the physical address into the DMA controller as a dma_addr_t and calls phys_to_dma() to do the conversion. The correct way to do the conversion is using the dma mapping interfaces. Fixes: c76b78d8ec05 ("mtd: nand: Qualcomm NAND controller driver") Signed-off-by: Arnd Bergmann Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/qcom_nandc.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 994f980c6d86..645630953f38 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2957,14 +2957,6 @@ static int qcom_nandc_probe(struct platform_device *pdev) nandc->props = dev_data; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nandc->base = devm_ioremap_resource(dev, res); - if (IS_ERR(nandc->base)) - return PTR_ERR(nandc->base); - - nandc->base_phys = res->start; - nandc->base_dma = phys_to_dma(dev, (phys_addr_t)res->start); - nandc->core_clk = devm_clk_get(dev, "core"); if (IS_ERR(nandc->core_clk)) return PTR_ERR(nandc->core_clk); @@ -2977,9 +2969,21 @@ static int qcom_nandc_probe(struct platform_device *pdev) if (ret) return ret; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nandc->base = devm_ioremap_resource(dev, res); + if (IS_ERR(nandc->base)) + return PTR_ERR(nandc->base); + + nandc->base_phys = res->start; + nandc->base_dma = dma_map_resource(dev, res->start, + resource_size(res), + DMA_BIDIRECTIONAL, 0); + if (!nandc->base_dma) + return -ENXIO; + ret = qcom_nandc_alloc(nandc); if (ret) - goto err_core_clk; + goto err_nandc_alloc; ret = clk_prepare_enable(nandc->core_clk); if (ret) @@ -3005,6 +3009,9 @@ err_aon_clk: clk_disable_unprepare(nandc->core_clk); err_core_clk: qcom_nandc_unalloc(nandc); +err_nandc_alloc: + dma_unmap_resource(dev, res->start, resource_size(res), + DMA_BIDIRECTIONAL, 0); return ret; } @@ -3012,16 +3019,21 @@ err_core_clk: static int qcom_nandc_remove(struct platform_device *pdev) { struct qcom_nand_controller *nandc = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct qcom_nand_host *host; list_for_each_entry(host, &nandc->host_list, node) nand_release(nand_to_mtd(&host->chip)); + qcom_nandc_unalloc(nandc); clk_disable_unprepare(nandc->aon_clk); clk_disable_unprepare(nandc->core_clk); + dma_unmap_resource(&pdev->dev, nandc->base_dma, resource_size(res), + DMA_BIDIRECTIONAL, 0); + return 0; } -- cgit v1.2.3 From 79e1ca37cc0c056f224cc1dd4a301b9dc2f94167 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 18 Jul 2018 10:28:14 +0200 Subject: mtd: rawnand: fsmc: Stop using chip->read_buf() chip->read_buf is left unassigned since commit 4da712e70294 ("mtd: nand: fsmc: use ->exec_op()"), leading to a NULL pointer dereference when it's called from fsmc_read_page_hwecc(). Fix that by using the appropriate helper to read data out of the NAND. Fixes: 4da712e70294 ("mtd: nand: fsmc: use ->exec_op()") Cc: Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/fsmc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 527bebc7e6c9..59524d181bfe 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -741,7 +741,7 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { nand_read_page_op(chip, page, s * eccsize, NULL, 0); chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); + nand_read_data_op(chip, p, eccsize, false); for (j = 0; j < eccbytes;) { struct mtd_oob_region oobregion; -- cgit v1.2.3 From dbc44edbf8338d27639214be07fe7ab4f8a903e0 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 18 Jul 2018 10:42:15 +0200 Subject: mtd: rawnand: micron: Fix on-die ECC detection logic Basing the "mandatory on-die" detection on ID byte 2 does not work, because Micron has plenty of NANDs using the same device ID code, and not all of them have forcibly enabled on-die ECC. Since the "Array Operation" feature does not provide the "ECC enabled/disabled" bit when the ECC can't be disabled, let's try to use the "ECC enabled/disabled" bit in the READ_ID bytes. It seems that this bit is dynamically updated on NANDs where on-die ECC can freely be enabled/disabled, so let's hope it stays at one when we have a NAND with on-die ECC forcibly enabled. Fixes: 51f3b3970a8c ("mtd: rawnand: micron: detect forced on-die ECC") Signed-off-by: Boris Brezillon Tested-by: Chris Packham Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_micron.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index d30bd4df9b12..d85bb4d42686 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -196,6 +196,9 @@ enum { MICRON_ON_DIE_MANDATORY, }; +#define MICRON_ID_INTERNAL_ECC_MASK GENMASK(1, 0) +#define MICRON_ID_ECC_ENABLED BIT(7) + /* * Try to detect if the NAND support on-die ECC. To do this, we enable * the feature, and read back if it has been enabled as expected. We @@ -208,7 +211,7 @@ enum { */ static int micron_supports_on_die_ecc(struct nand_chip *chip) { - u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, }; + u8 id[5]; int ret; if (!chip->parameters.onfi.version) @@ -217,26 +220,37 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) if (chip->bits_per_cell != 1) return MICRON_ON_DIE_UNSUPPORTED; + /* + * We only support on-die ECC of 4/512 or 8/512 + */ + if (chip->ecc_strength_ds != 4 && chip->ecc_strength_ds != 8) + return MICRON_ON_DIE_UNSUPPORTED; + + /* 0x2 means on-die ECC is available. */ + if (chip->id.len != 5 || + (chip->id.data[4] & MICRON_ID_INTERNAL_ECC_MASK) != 0x2) + return MICRON_ON_DIE_UNSUPPORTED; + ret = micron_nand_on_die_ecc_setup(chip, true); if (ret) return MICRON_ON_DIE_UNSUPPORTED; - ret = nand_get_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature); - if (ret < 0) - return ret; + ret = nand_readid_op(chip, 0, id, sizeof(id)); + if (ret) + return MICRON_ON_DIE_UNSUPPORTED; - if ((feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) == 0) + if (!(id[4] & MICRON_ID_ECC_ENABLED)) return MICRON_ON_DIE_UNSUPPORTED; ret = micron_nand_on_die_ecc_setup(chip, false); if (ret) return MICRON_ON_DIE_UNSUPPORTED; - ret = nand_get_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature); - if (ret < 0) - return ret; + ret = nand_readid_op(chip, 0, id, sizeof(id)); + if (ret) + return MICRON_ON_DIE_UNSUPPORTED; - if (feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) + if (id[4] & MICRON_ID_ECC_ENABLED) return MICRON_ON_DIE_MANDATORY; /* -- cgit v1.2.3 From 3ec7cb369eb22a5a06f9867e358bc55d40055168 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Wed, 18 Jul 2018 10:42:16 +0200 Subject: mtd: rawnand: micron: support 8/512 on-die ECC Micron MT29F1G08ABAFAWP-ITE:F supports an on-die ECC with 8 bits per 512 bytes. Add support for this combination. Signed-off-by: Chris Packham Reviewed-by: Boris Brezillon Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_micron.c | 161 ++++++++++++++++++++++++++++++------- 1 file changed, 131 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index d85bb4d42686..f8839c7f7464 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -18,10 +18,30 @@ #include /* - * Special Micron status bit that indicates when the block has been - * corrected by on-die ECC and should be rewritten + * Special Micron status bit 3 indicates that the block has been + * corrected by on-die ECC and should be rewritten. */ -#define NAND_STATUS_WRITE_RECOMMENDED BIT(3) +#define NAND_ECC_STATUS_WRITE_RECOMMENDED BIT(3) + +/* + * On chips with 8-bit ECC and additional bit can be used to distinguish + * cases where a errors were corrected without needing a rewrite + * + * Bit 4 Bit 3 Bit 0 Description + * ----- ----- ----- ----------- + * 0 0 0 No Errors + * 0 0 1 Multiple uncorrected errors + * 0 1 0 4 - 6 errors corrected, recommend rewrite + * 0 1 1 Reserved + * 1 0 0 1 - 3 errors corrected + * 1 0 1 Reserved + * 1 1 0 7 - 8 errors corrected, recommend rewrite + */ +#define NAND_ECC_STATUS_MASK (BIT(4) | BIT(3) | BIT(0)) +#define NAND_ECC_STATUS_UNCORRECTABLE BIT(0) +#define NAND_ECC_STATUS_4_6_CORRECTED BIT(3) +#define NAND_ECC_STATUS_1_3_CORRECTED BIT(4) +#define NAND_ECC_STATUS_7_8_CORRECTED (BIT(4) | BIT(3)) struct nand_onfi_vendor_micron { u8 two_plane_read; @@ -74,8 +94,9 @@ static int micron_nand_onfi_init(struct nand_chip *chip) return 0; } -static int micron_nand_on_die_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) +static int micron_nand_on_die_4_ooblayout_ecc(struct mtd_info *mtd, + int section, + struct mtd_oob_region *oobregion) { if (section >= 4) return -ERANGE; @@ -86,8 +107,9 @@ static int micron_nand_on_die_ooblayout_ecc(struct mtd_info *mtd, int section, return 0; } -static int micron_nand_on_die_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) +static int micron_nand_on_die_4_ooblayout_free(struct mtd_info *mtd, + int section, + struct mtd_oob_region *oobregion) { if (section >= 4) return -ERANGE; @@ -98,9 +120,44 @@ static int micron_nand_on_die_ooblayout_free(struct mtd_info *mtd, int section, return 0; } -static const struct mtd_ooblayout_ops micron_nand_on_die_ooblayout_ops = { - .ecc = micron_nand_on_die_ooblayout_ecc, - .free = micron_nand_on_die_ooblayout_free, +static const struct mtd_ooblayout_ops micron_nand_on_die_4_ooblayout_ops = { + .ecc = micron_nand_on_die_4_ooblayout_ecc, + .free = micron_nand_on_die_4_ooblayout_free, +}; + +static int micron_nand_on_die_8_ooblayout_ecc(struct mtd_info *mtd, + int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = mtd->oobsize - chip->ecc.total; + oobregion->length = chip->ecc.total; + + return 0; +} + +static int micron_nand_on_die_8_ooblayout_free(struct mtd_info *mtd, + int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = 2; + oobregion->length = mtd->oobsize - chip->ecc.total - 2; + + return 0; +} + +static const struct mtd_ooblayout_ops micron_nand_on_die_8_ooblayout_ops = { + .ecc = micron_nand_on_die_8_ooblayout_ecc, + .free = micron_nand_on_die_8_ooblayout_free, }; static int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable) @@ -113,6 +170,55 @@ static int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable) return nand_set_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature); } +static int micron_nand_on_die_ecc_status_4(struct nand_chip *chip, u8 status) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + /* + * The internal ECC doesn't tell us the number of bitflips + * that have been corrected, but tells us if it recommends to + * rewrite the block. If it's the case, then we pretend we had + * a number of bitflips equal to the ECC strength, which will + * hint the NAND core to rewrite the block. + */ + if (status & NAND_STATUS_FAIL) { + mtd->ecc_stats.failed++; + } else if (status & NAND_ECC_STATUS_WRITE_RECOMMENDED) { + mtd->ecc_stats.corrected += chip->ecc.strength; + return chip->ecc.strength; + } + + return 0; +} + +static int micron_nand_on_die_ecc_status_8(struct nand_chip *chip, u8 status) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + /* + * With 8/512 we have more information but still don't know precisely + * how many bit-flips were seen. + */ + switch (status & NAND_ECC_STATUS_MASK) { + case NAND_ECC_STATUS_UNCORRECTABLE: + mtd->ecc_stats.failed++; + return 0; + case NAND_ECC_STATUS_1_3_CORRECTED: + mtd->ecc_stats.corrected += 3; + return 3; + case NAND_ECC_STATUS_4_6_CORRECTED: + mtd->ecc_stats.corrected += 6; + /* rewrite recommended */ + return 6; + case NAND_ECC_STATUS_7_8_CORRECTED: + mtd->ecc_stats.corrected += 8; + /* rewrite recommended */ + return 8; + default: + return 0; + } +} + static int micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, @@ -137,19 +243,10 @@ micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, if (ret) goto out; - if (status & NAND_STATUS_FAIL) { - mtd->ecc_stats.failed++; - } else if (status & NAND_STATUS_WRITE_RECOMMENDED) { - /* - * The internal ECC doesn't tell us the number of bitflips - * that have been corrected, but tells us if it recommends to - * rewrite the block. If it's the case, then we pretend we had - * a number of bitflips equal to the ECC strength, which will - * hint the NAND core to rewrite the block. - */ - mtd->ecc_stats.corrected += chip->ecc.strength; - max_bitflips = chip->ecc.strength; - } + if (chip->ecc.strength == 4) + max_bitflips = micron_nand_on_die_ecc_status_4(chip, status); + else + max_bitflips = micron_nand_on_die_ecc_status_8(chip, status); ret = nand_read_data_op(chip, buf, mtd->writesize, false); if (!ret && oob_required) @@ -254,10 +351,9 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) return MICRON_ON_DIE_MANDATORY; /* - * Some Micron NANDs have an on-die ECC of 4/512, some other - * 8/512. We only support the former. + * We only support on-die ECC of 4/512 or 8/512 */ - if (chip->ecc_strength_ds != 4) + if (chip->ecc_strength_ds != 4 && chip->ecc_strength_ds != 8) return MICRON_ON_DIE_UNSUPPORTED; return MICRON_ON_DIE_SUPPORTED; @@ -289,16 +385,21 @@ static int micron_nand_init(struct nand_chip *chip) return -EINVAL; } - chip->ecc.bytes = 8; + if (chip->ecc_strength_ds == 4) + mtd_set_ooblayout(mtd, + µn_nand_on_die_4_ooblayout_ops); + else + mtd_set_ooblayout(mtd, + µn_nand_on_die_8_ooblayout_ops); + + chip->ecc.bytes = chip->ecc_strength_ds * 2; chip->ecc.size = 512; - chip->ecc.strength = 4; + chip->ecc.strength = chip->ecc_strength_ds; chip->ecc.algo = NAND_ECC_BCH; chip->ecc.read_page = micron_nand_read_page_on_die_ecc; chip->ecc.write_page = micron_nand_write_page_on_die_ecc; chip->ecc.read_page_raw = nand_read_page_raw; chip->ecc.write_page_raw = nand_write_page_raw; - - mtd_set_ooblayout(mtd, µn_nand_on_die_ooblayout_ops); } return 0; -- cgit v1.2.3 From 0d6030ac041f6835974deb88a1a9c299b4adc3ad Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 18 Jul 2018 10:42:17 +0200 Subject: mtd: rawnand: Expose _notsupp() helpers for raw page accessors Some implementations simply can't disable their ECC engine. Expose helpers returning -ENOTSUPP so that the caller knows that raw accesses are not supported instead of silently falling back to non-raw accessors. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_base.c | 33 +++++++++++++++++++++++++++++++++ include/linux/mtd/rawnand.h | 4 ++++ 2 files changed, 37 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 4fa5e20d9690..c4b74630f4c5 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -2966,6 +2966,23 @@ int nand_check_erased_ecc_chunk(void *data, int datalen, } EXPORT_SYMBOL(nand_check_erased_ecc_chunk); +/** + * nand_read_page_raw_notsupp - dummy read raw page function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Returns -ENOTSUPP unconditionally. + */ +int nand_read_page_raw_notsupp(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + return -ENOTSUPP; +} +EXPORT_SYMBOL(nand_read_page_raw_notsupp); + /** * nand_read_page_raw - [INTERN] read raw page data without ecc * @mtd: mtd info structure @@ -3960,6 +3977,22 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, return ret; } +/** + * nand_write_page_raw_notsupp - dummy raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * + * Returns -ENOTSUPP unconditionally. + */ +int nand_write_page_raw_notsupp(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, int oob_required, int page) +{ + return -ENOTSUPP; +} +EXPORT_SYMBOL(nand_write_page_raw_notsupp); /** * nand_write_page_raw - [INTERN] raw page write function diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 11c2426fc363..f60fad29eae6 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1681,10 +1681,14 @@ int nand_get_set_features_notsupp(struct mtd_info *mtd, struct nand_chip *chip, /* Default read_page_raw implementation */ int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page); +int nand_read_page_raw_notsupp(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, int oob_required, int page); /* Default write_page_raw implementation */ int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page); +int nand_write_page_raw_notsupp(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, int oob_required, int page); /* Reset and initialize a NAND device */ int nand_reset(struct nand_chip *chip, int chipnr); -- cgit v1.2.3 From cb2bf403a462082a253fe0a94cfec020285ffbd8 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Wed, 18 Jul 2018 10:42:18 +0200 Subject: mtd: rawnand: micron: allow forced on-die ECC Some Micron NAND chips have on-die ECC forceably enabled. Allow such chips to be used as long as the controller has set chip->ecc.mode to NAND_ECC_ON_DIE. Signed-off-by: Chris Packham Reviewed-by: Boris Brezillon Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_micron.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index f8839c7f7464..fd3f68e0909f 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -374,7 +374,8 @@ static int micron_nand_init(struct nand_chip *chip) ondie = micron_supports_on_die_ecc(chip); - if (ondie == MICRON_ON_DIE_MANDATORY) { + if (ondie == MICRON_ON_DIE_MANDATORY && + chip->ecc.mode != NAND_ECC_ON_DIE) { pr_err("On-die ECC forcefully enabled, not supported\n"); return -EINVAL; } @@ -398,8 +399,14 @@ static int micron_nand_init(struct nand_chip *chip) chip->ecc.algo = NAND_ECC_BCH; chip->ecc.read_page = micron_nand_read_page_on_die_ecc; chip->ecc.write_page = micron_nand_write_page_on_die_ecc; - chip->ecc.read_page_raw = nand_read_page_raw; - chip->ecc.write_page_raw = nand_write_page_raw; + + if (ondie == MICRON_ON_DIE_MANDATORY) { + chip->ecc.read_page_raw = nand_read_page_raw_notsupp; + chip->ecc.write_page_raw = nand_write_page_raw_notsupp; + } else { + chip->ecc.read_page_raw = nand_read_page_raw; + chip->ecc.write_page_raw = nand_write_page_raw; + } } return 0; -- cgit v1.2.3 From 2301780711efc514a8bb183e853bda8decce746a Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 18 Jul 2018 10:42:19 +0200 Subject: mtd: rawnand: micron: Get the actual number of bitflips The MT29F2Gxxx chips with 4bits/512byte on-die ECC let us know when some bitflips were corrected by the on-die ECC, but they do not report the actual number of bitflips that were present in the data+ECC chunk. We initially decided to always return ecc->strength to avoid re-reading the page in raw mode + comparing it to the corrected buffer to extract the real number of bitflips, but this forces UBI to move data around as soon as one bitflip is present in a page. This not only wears the NAND out faster, but also degrades performances, since reading a full PEB + writing it back to a different PEB + erasing the old one is much more expensive than re-reading the faulty page in raw mode and comparing it to the corrected buffer. In most cases, the actual number of bitflips does not exceed the bitflips threshold, and UBI won't have to move data around. Otherwise, we can assume the time spent re-reading the page and doing the comparison is negligible compared to the time UBI spends moving a full PEB to another PEB. Note that this logic is not applied to chips with 8bits/512byte on-die ECC, because those chips provide fine-grained information (the maximum error is 1 bit, and it will not force UBI to move blocks around at the first bitflip). Suggested-by: Bean Huo Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_micron.c | 139 +++++++++++++++++++++++++++++++------ 1 file changed, 119 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index fd3f68e0909f..2cff25f7b48b 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -16,6 +16,7 @@ */ #include +#include /* * Special Micron status bit 3 indicates that the block has been @@ -63,6 +64,14 @@ struct nand_onfi_vendor_micron { u8 param_revision; } __packed; +struct micron_on_die_ecc { + void *rawbuf; +}; + +struct micron_nand { + struct micron_on_die_ecc ecc; +}; + static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) { struct nand_chip *chip = mtd_to_nand(mtd); @@ -170,25 +179,71 @@ static int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable) return nand_set_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature); } -static int micron_nand_on_die_ecc_status_4(struct nand_chip *chip, u8 status) +static int micron_nand_on_die_ecc_status_4(struct nand_chip *chip, u8 status, + void *buf, int page, + int oob_required) { + struct micron_nand *micron = nand_get_manufacturer_data(chip); struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int step, max_bitflips = 0; + int ret; + + if (!(status & NAND_ECC_STATUS_WRITE_RECOMMENDED)) { + if (status & NAND_STATUS_FAIL) + mtd->ecc_stats.failed++; + + return 0; + } /* - * The internal ECC doesn't tell us the number of bitflips - * that have been corrected, but tells us if it recommends to - * rewrite the block. If it's the case, then we pretend we had - * a number of bitflips equal to the ECC strength, which will - * hint the NAND core to rewrite the block. + * The internal ECC doesn't tell us the number of bitflips that have + * been corrected, but tells us if it recommends to rewrite the block. + * If it's the case, we need to read the page in raw mode and compare + * its content to the corrected version to extract the actual number of + * bitflips. + * But before we do that, we must make sure we have all OOB bytes read + * in non-raw mode, even if the user did not request those bytes. */ - if (status & NAND_STATUS_FAIL) { - mtd->ecc_stats.failed++; - } else if (status & NAND_ECC_STATUS_WRITE_RECOMMENDED) { - mtd->ecc_stats.corrected += chip->ecc.strength; - return chip->ecc.strength; + if (!oob_required) { + ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, + false); + if (ret) + return ret; } - return 0; + micron_nand_on_die_ecc_setup(chip, false); + + ret = nand_read_page_op(chip, page, 0, micron->ecc.rawbuf, + mtd->writesize + mtd->oobsize); + if (ret) + return ret; + + for (step = 0; step < chip->ecc.steps; step++) { + unsigned int offs, i, nbitflips = 0; + u8 *rawbuf, *corrbuf; + + offs = step * chip->ecc.size; + rawbuf = micron->ecc.rawbuf + offs; + corrbuf = buf + offs; + + for (i = 0; i < chip->ecc.size; i++) + nbitflips += hweight8(corrbuf[i] ^ rawbuf[i]); + + offs = (step * 16) + 4; + rawbuf = micron->ecc.rawbuf + mtd->writesize + offs; + corrbuf = chip->oob_poi + offs; + + for (i = 0; i < chip->ecc.bytes + 4; i++) + nbitflips += hweight8(corrbuf[i] ^ rawbuf[i]); + + if (WARN_ON(nbitflips > chip->ecc.strength)) + return -EINVAL; + + max_bitflips = max(nbitflips, max_bitflips); + mtd->ecc_stats.corrected += nbitflips; + } + + return max_bitflips; } static int micron_nand_on_die_ecc_status_8(struct nand_chip *chip, u8 status) @@ -243,16 +298,18 @@ micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, if (ret) goto out; - if (chip->ecc.strength == 4) - max_bitflips = micron_nand_on_die_ecc_status_4(chip, status); - else - max_bitflips = micron_nand_on_die_ecc_status_8(chip, status); - ret = nand_read_data_op(chip, buf, mtd->writesize, false); if (!ret && oob_required) ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false); + if (chip->ecc.strength == 4) + max_bitflips = micron_nand_on_die_ecc_status_4(chip, status, + buf, page, + oob_required); + else + max_bitflips = micron_nand_on_die_ecc_status_8(chip, status); + out: micron_nand_on_die_ecc_setup(chip, false); @@ -362,12 +419,19 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) static int micron_nand_init(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); + struct micron_nand *micron; int ondie; int ret; + micron = kzalloc(sizeof(*micron), GFP_KERNEL); + if (!micron) + return -ENOMEM; + + nand_set_manufacturer_data(chip, micron); + ret = micron_nand_onfi_init(chip); if (ret) - return ret; + goto err_free_manuf_data; if (mtd->writesize == 2048) chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; @@ -377,13 +441,33 @@ static int micron_nand_init(struct nand_chip *chip) if (ondie == MICRON_ON_DIE_MANDATORY && chip->ecc.mode != NAND_ECC_ON_DIE) { pr_err("On-die ECC forcefully enabled, not supported\n"); - return -EINVAL; + ret = -EINVAL; + goto err_free_manuf_data; } if (chip->ecc.mode == NAND_ECC_ON_DIE) { if (ondie == MICRON_ON_DIE_UNSUPPORTED) { pr_err("On-die ECC selected but not supported\n"); - return -EINVAL; + ret = -EINVAL; + goto err_free_manuf_data; + } + + /* + * In case of 4bit on-die ECC, we need a buffer to store a + * page dumped in raw mode so that we can compare its content + * to the same page after ECC correction happened and extract + * the real number of bitflips from this comparison. + * That's not needed for 8-bit ECC, because the status expose + * a better approximation of the number of bitflips in a page. + */ + if (chip->ecc_strength_ds == 4) { + micron->ecc.rawbuf = kmalloc(mtd->writesize + + mtd->oobsize, + GFP_KERNEL); + if (!micron->ecc.rawbuf) { + ret = -ENOMEM; + goto err_free_manuf_data; + } } if (chip->ecc_strength_ds == 4) @@ -410,6 +494,20 @@ static int micron_nand_init(struct nand_chip *chip) } return 0; + +err_free_manuf_data: + kfree(micron->ecc.rawbuf); + kfree(micron); + + return ret; +} + +static void micron_nand_cleanup(struct nand_chip *chip) +{ + struct micron_nand *micron = nand_get_manufacturer_data(chip); + + kfree(micron->ecc.rawbuf); + kfree(micron); } static void micron_fixup_onfi_param_page(struct nand_chip *chip, @@ -426,5 +524,6 @@ static void micron_fixup_onfi_param_page(struct nand_chip *chip, const struct nand_manufacturer_ops micron_nand_manuf_ops = { .init = micron_nand_init, + .cleanup = micron_nand_cleanup, .fixup_onfi_param_page = micron_fixup_onfi_param_page, }; -- cgit v1.2.3 From ef422e1ecd272d077efce64432907632758cf2e2 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 18 Jul 2018 10:42:20 +0200 Subject: mtd: rawnand: micron: Avoid enabling/disabling ECC when it can't be disabled Some chips have their on-die ECC forcibly enabled, there's no point in trying to enable/disable the ECC engine in that case. Signed-off-by: Boris Brezillon Tested-by: Chris Packham Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_micron.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index 2cff25f7b48b..9d13b701e581 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -65,6 +65,7 @@ struct nand_onfi_vendor_micron { } __packed; struct micron_on_die_ecc { + bool forced; void *rawbuf; }; @@ -171,8 +172,12 @@ static const struct mtd_ooblayout_ops micron_nand_on_die_8_ooblayout_ops = { static int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable) { + struct micron_nand *micron = nand_get_manufacturer_data(chip); u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, }; + if (micron->ecc.forced) + return 0; + if (enable) feature[0] |= ONFI_FEATURE_ON_DIE_ECC_EN; @@ -452,6 +457,9 @@ static int micron_nand_init(struct nand_chip *chip) goto err_free_manuf_data; } + if (ondie == MICRON_ON_DIE_MANDATORY) + micron->ecc.forced = true; + /* * In case of 4bit on-die ECC, we need a buffer to store a * page dumped in raw mode so that we can compare its content -- cgit v1.2.3 From 317c6d9b69eb2dcab2ca4759a2c1c04496ec1591 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 18 Jul 2018 10:42:21 +0200 Subject: mtd: rawnand: micron: Make ECC activation stateful We currently don't store the on-die ECC state (enabled/disabled) which might force us to re-disable the engine even if it's already been disabled after we've read the page in raw mode to count the actual number of bitflips. Add an "enabled" field to struct micron_on_die_ecc to keep track of the ECC state. Signed-off-by: Boris Brezillon Tested-by: Chris Packham Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_micron.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index 9d13b701e581..656947d91841 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -66,6 +66,7 @@ struct nand_onfi_vendor_micron { struct micron_on_die_ecc { bool forced; + bool enabled; void *rawbuf; }; @@ -174,14 +175,22 @@ static int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable) { struct micron_nand *micron = nand_get_manufacturer_data(chip); u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, }; + int ret; if (micron->ecc.forced) return 0; + if (micron->ecc.enabled == enable) + return 0; + if (enable) feature[0] |= ONFI_FEATURE_ON_DIE_ECC_EN; - return nand_set_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature); + ret = nand_set_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature); + if (!ret) + micron->ecc.enabled = enable; + + return ret; } static int micron_nand_on_die_ecc_status_4(struct nand_chip *chip, u8 status, @@ -457,8 +466,10 @@ static int micron_nand_init(struct nand_chip *chip) goto err_free_manuf_data; } - if (ondie == MICRON_ON_DIE_MANDATORY) + if (ondie == MICRON_ON_DIE_MANDATORY) { micron->ecc.forced = true; + micron->ecc.enabled = true; + } /* * In case of 4bit on-die ECC, we need a buffer to store a -- cgit v1.2.3 From c81d28ad4fe8899f3a17b204eeec6d37d698dd5b Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 18 Jul 2018 10:56:50 +0200 Subject: mtd: rawnand: orion: Avoid direct inclusion of asm headers Include linux/sizes.h instead of asm/sizes.h to make code completely arch independent. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/orion_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/orion_nand.c b/drivers/mtd/nand/raw/orion_nand.c index 1a4828442675..27cc902545f3 100644 --- a/drivers/mtd/nand/raw/orion_nand.c +++ b/drivers/mtd/nand/raw/orion_nand.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include struct orion_nand_info { -- cgit v1.2.3 From d166541e9031daff120cac212f1917eff93c07d3 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 18 Jul 2018 10:56:51 +0200 Subject: mtd: rawnand: orion: Handle cases where __LINUX_ARM_ARCH__ is not defined Make sure __LINUX_ARM_ARCH__ is defined before testing its value. This is needed if we want to allow selection of this driver when COMPILE_TEST=y. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/orion_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/orion_nand.c b/drivers/mtd/nand/raw/orion_nand.c index 27cc902545f3..52d435285a3f 100644 --- a/drivers/mtd/nand/raw/orion_nand.c +++ b/drivers/mtd/nand/raw/orion_nand.c @@ -52,7 +52,7 @@ static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { struct nand_chip *chip = mtd_to_nand(mtd); void __iomem *io_base = chip->IO_ADDR_R; -#if __LINUX_ARM_ARCH__ >= 5 +#if defined(__LINUX_ARM_ARCH__) && __LINUX_ARM_ARCH__ >= 5 uint64_t *buf64; #endif int i = 0; @@ -61,7 +61,7 @@ static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) *buf++ = readb(io_base); len--; } -#if __LINUX_ARM_ARCH__ >= 5 +#if defined(__LINUX_ARM_ARCH__) && __LINUX_ARM_ARCH__ >= 5 buf64 = (uint64_t *)buf; while (i < len/8) { /* -- cgit v1.2.3 From 760c435e0f85ed19e48a90d746ce1de2cd02def7 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 19 Jul 2018 00:09:12 +0200 Subject: mtd: rawnand: make subop helpers return unsigned values A report from Colin Ian King pointed a CoverityScan issue where error values on these helpers where not checked in the drivers. These helpers can error out only in case of a software bug in driver code, not because of a runtime/hardware error. Hence, let's WARN_ON() in this case and return 0 which is harmless anyway. Fixes: 8878b126df76 ("mtd: nand: add ->exec_op() implementation") Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_base.c | 44 ++++++++++++++++++++-------------------- include/linux/mtd/rawnand.h | 16 +++++++-------- 2 files changed, 30 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index c4b74630f4c5..ef10beab99f5 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -2668,8 +2668,8 @@ static bool nand_subop_instr_is_valid(const struct nand_subop *subop, return subop && instr_idx < subop->ninstrs; } -static int nand_subop_get_start_off(const struct nand_subop *subop, - unsigned int instr_idx) +static unsigned int nand_subop_get_start_off(const struct nand_subop *subop, + unsigned int instr_idx) { if (instr_idx) return 0; @@ -2688,12 +2688,12 @@ static int nand_subop_get_start_off(const struct nand_subop *subop, * * Given an address instruction, returns the offset of the first cycle to issue. */ -int nand_subop_get_addr_start_off(const struct nand_subop *subop, - unsigned int instr_idx) +unsigned int nand_subop_get_addr_start_off(const struct nand_subop *subop, + unsigned int instr_idx) { - if (!nand_subop_instr_is_valid(subop, instr_idx) || - subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR) - return -EINVAL; + if (WARN_ON(!nand_subop_instr_is_valid(subop, instr_idx) || + subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR)) + return 0; return nand_subop_get_start_off(subop, instr_idx); } @@ -2710,14 +2710,14 @@ EXPORT_SYMBOL_GPL(nand_subop_get_addr_start_off); * * Given an address instruction, returns the number of address cycle to issue. */ -int nand_subop_get_num_addr_cyc(const struct nand_subop *subop, - unsigned int instr_idx) +unsigned int nand_subop_get_num_addr_cyc(const struct nand_subop *subop, + unsigned int instr_idx) { int start_off, end_off; - if (!nand_subop_instr_is_valid(subop, instr_idx) || - subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR) - return -EINVAL; + if (WARN_ON(!nand_subop_instr_is_valid(subop, instr_idx) || + subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR)) + return 0; start_off = nand_subop_get_addr_start_off(subop, instr_idx); @@ -2742,12 +2742,12 @@ EXPORT_SYMBOL_GPL(nand_subop_get_num_addr_cyc); * * Given a data instruction, returns the offset to start from. */ -int nand_subop_get_data_start_off(const struct nand_subop *subop, - unsigned int instr_idx) +unsigned int nand_subop_get_data_start_off(const struct nand_subop *subop, + unsigned int instr_idx) { - if (!nand_subop_instr_is_valid(subop, instr_idx) || - !nand_instr_is_data(&subop->instrs[instr_idx])) - return -EINVAL; + if (WARN_ON(!nand_subop_instr_is_valid(subop, instr_idx) || + !nand_instr_is_data(&subop->instrs[instr_idx]))) + return 0; return nand_subop_get_start_off(subop, instr_idx); } @@ -2764,14 +2764,14 @@ EXPORT_SYMBOL_GPL(nand_subop_get_data_start_off); * * Returns the length of the chunk of data to send/receive. */ -int nand_subop_get_data_len(const struct nand_subop *subop, - unsigned int instr_idx) +unsigned int nand_subop_get_data_len(const struct nand_subop *subop, + unsigned int instr_idx) { int start_off = 0, end_off; - if (!nand_subop_instr_is_valid(subop, instr_idx) || - !nand_instr_is_data(&subop->instrs[instr_idx])) - return -EINVAL; + if (WARN_ON(!nand_subop_instr_is_valid(subop, instr_idx) || + !nand_instr_is_data(&subop->instrs[instr_idx]))) + return 0; start_off = nand_subop_get_data_start_off(subop, instr_idx); diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index f60fad29eae6..598d356de83f 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1007,14 +1007,14 @@ struct nand_subop { unsigned int last_instr_end_off; }; -int nand_subop_get_addr_start_off(const struct nand_subop *subop, - unsigned int op_id); -int nand_subop_get_num_addr_cyc(const struct nand_subop *subop, - unsigned int op_id); -int nand_subop_get_data_start_off(const struct nand_subop *subop, - unsigned int op_id); -int nand_subop_get_data_len(const struct nand_subop *subop, - unsigned int op_id); +unsigned int nand_subop_get_addr_start_off(const struct nand_subop *subop, + unsigned int op_id); +unsigned int nand_subop_get_num_addr_cyc(const struct nand_subop *subop, + unsigned int op_id); +unsigned int nand_subop_get_data_start_off(const struct nand_subop *subop, + unsigned int op_id); +unsigned int nand_subop_get_data_len(const struct nand_subop *subop, + unsigned int op_id); /** * struct nand_op_parser_addr_constraints - Constraints for address instructions -- cgit v1.2.3 From bdc4e58d5395fb45e22a54e01312f1abb22523ef Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 19 Jul 2018 22:53:50 +0200 Subject: mtd: rawnand: s3c2410: Error out when ->nrsets < 0 or ->sets == NULL All of the code in the probe path assumes ->sets != NULL and ->nrsets > 0. Error out if that's not the case to avoid dereferencing a NULL pointer. Reported-by: Dan Carpenter Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/s3c2410.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c index 10d81f367d26..5a4a68790653 100644 --- a/drivers/mtd/nand/raw/s3c2410.c +++ b/drivers/mtd/nand/raw/s3c2410.c @@ -1134,8 +1134,13 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs); - sets = (plat != NULL) ? plat->sets : NULL; - nr_sets = (plat != NULL) ? plat->nr_sets : 1; + if (!plat->sets || plat->nr_sets < 1) { + err = -EINVAL; + goto exit_error; + } + + sets = plat->sets; + nr_sets = plat->nr_sets; info->mtd_count = nr_sets; @@ -1152,7 +1157,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) nmtd = info->mtds; - for (setno = 0; setno < nr_sets; setno++, nmtd++) { + for (setno = 0; setno < nr_sets; setno++, nmtd++, sets++) { struct mtd_info *mtd = nand_to_mtd(&nmtd->chip); pr_debug("initialising set %d (%p, info %p)\n", @@ -1174,9 +1179,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) goto exit_error; s3c2410_nand_add_partition(info, nmtd, sets); - - if (sets != NULL) - sets++; } /* initialise the hardware */ -- cgit v1.2.3 From 110ab1582670392c1d8b805e701f5b7b7efcb120 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 19 Jul 2018 22:47:38 +0200 Subject: mtd: rawnand: Remove unused caller_is_module() definition Commit 260e89a6e0d6 ("mtd: core: tone down suggestion that dev.parent should be set") removed the only user of caller_is_module() but forgot to remove the definition itself. Let's remove it now. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_base.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index ef10beab99f5..e545e03a214e 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -6718,18 +6718,6 @@ err_free_buf: } EXPORT_SYMBOL(nand_scan_tail); -/* - * is_module_text_address() isn't exported, and it's mostly a pointless - * test if this is a module _anyway_ -- they'd have to try _really_ hard - * to call us from in-kernel code if the core NAND support is modular. - */ -#ifdef MODULE -#define caller_is_module() (1) -#else -#define caller_is_module() \ - is_module_text_address((unsigned long)__builtin_return_address(0)) -#endif - /** * nand_scan_with_ids - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure -- cgit v1.2.3 From 7da45139d264f3b7ead04e00ebb29b189cf9826e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 17 Jul 2018 09:08:02 +0200 Subject: mtd: rawnand: better name for the controller structure In the raw NAND core, a NAND chip is described by a nand_chip structure, while a NAND controller is described with a nand_hw_control structure which is not very meaningful. Rename this structure nand_controller. As the structure gets renamed, it is logical to also rename the core function initializing it from nand_hw_control_init() to nand_controller_init(). Lastly, the 'hwcontrol' entry of the nand_chip structure is not meaningful neither while it has the role of fallback when no controller structure is provided by the driver (the controller driver is dumb and can only control a single chip). Thus, it is renamed dummy_controller. Signed-off-by: Miquel Raynal Acked-by: Boris Brezillon --- drivers/mtd/nand/raw/atmel/nand-controller.c | 10 +++++----- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 4 ++-- drivers/mtd/nand/raw/docg4.c | 4 ++-- drivers/mtd/nand/raw/fsl_elbc_nand.c | 4 ++-- drivers/mtd/nand/raw/fsl_ifc_nand.c | 4 ++-- drivers/mtd/nand/raw/jz4780_nand.c | 7 ++++--- drivers/mtd/nand/raw/marvell_nand.c | 6 +++--- drivers/mtd/nand/raw/mtk_nand.c | 2 +- drivers/mtd/nand/raw/nand_base.c | 4 ++-- drivers/mtd/nand/raw/ndfc.c | 4 ++-- drivers/mtd/nand/raw/omap2.c | 2 +- drivers/mtd/nand/raw/oxnas_nand.c | 4 ++-- drivers/mtd/nand/raw/qcom_nandc.c | 4 ++-- drivers/mtd/nand/raw/s3c2410.c | 4 ++-- drivers/mtd/nand/raw/sunxi_nand.c | 6 +++--- drivers/mtd/nand/raw/tango_nand.c | 4 ++-- drivers/mtd/nand/raw/tegra_nand.c | 6 +++--- drivers/mtd/nand/raw/txx9ndfmc.c | 4 ++-- include/linux/mtd/rawnand.h | 14 ++++++++------ 19 files changed, 50 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index 30dae4c9d439..855cc7729c43 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -216,7 +216,7 @@ struct atmel_nand_controller_caps { }; struct atmel_nand_controller { - struct nand_hw_control base; + struct nand_controller base; const struct atmel_nand_controller_caps *caps; struct device *dev; struct regmap *smc; @@ -227,7 +227,7 @@ struct atmel_nand_controller { }; static inline struct atmel_nand_controller * -to_nand_controller(struct nand_hw_control *ctl) +to_nand_controller(struct nand_controller *ctl) { return container_of(ctl, struct atmel_nand_controller, base); } @@ -239,7 +239,7 @@ struct atmel_smc_nand_controller { }; static inline struct atmel_smc_nand_controller * -to_smc_nand_controller(struct nand_hw_control *ctl) +to_smc_nand_controller(struct nand_controller *ctl) { return container_of(to_nand_controller(ctl), struct atmel_smc_nand_controller, base); @@ -263,7 +263,7 @@ struct atmel_hsmc_nand_controller { }; static inline struct atmel_hsmc_nand_controller * -to_hsmc_nand_controller(struct nand_hw_control *ctl) +to_hsmc_nand_controller(struct nand_controller *ctl) { return container_of(to_nand_controller(ctl), struct atmel_hsmc_nand_controller, base); @@ -1966,7 +1966,7 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc, struct device_node *np = dev->of_node; int ret; - nand_hw_control_init(&nc->base); + nand_controller_init(&nc->base); INIT_LIST_HEAD(&nc->chips); nc->dev = dev; nc->caps = caps; diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 1306aaa7a8bf..2e5efa0f9ea2 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -114,7 +114,7 @@ enum { struct brcmnand_controller { struct device *dev; - struct nand_hw_control controller; + struct nand_controller controller; void __iomem *nand_base; void __iomem *nand_fc; /* flash cache */ void __iomem *flash_dma_base; @@ -2433,7 +2433,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) init_completion(&ctrl->done); init_completion(&ctrl->dma_done); - nand_hw_control_init(&ctrl->controller); + nand_controller_init(&ctrl->controller); INIT_LIST_HEAD(&ctrl->host_list); /* NAND register range */ diff --git a/drivers/mtd/nand/raw/docg4.c b/drivers/mtd/nand/raw/docg4.c index bb96cb33cd6b..4dccdfba6140 100644 --- a/drivers/mtd/nand/raw/docg4.c +++ b/drivers/mtd/nand/raw/docg4.c @@ -1257,8 +1257,8 @@ static void __init init_mtd_structs(struct mtd_info *mtd) nand->ecc.strength = DOCG4_T; nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE; nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA; - nand->controller = &nand->hwcontrol; - nand_hw_control_init(nand->controller); + nand->controller = &nand->dummy_controller; + nand_controller_init(nand->controller); /* methods */ nand->cmdfunc = docg4_command; diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index 51f0b340bc0d..0aa54a949653 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -61,7 +61,7 @@ struct fsl_elbc_mtd { /* Freescale eLBC FCM controller information */ struct fsl_elbc_fcm_ctrl { - struct nand_hw_control controller; + struct nand_controller controller; struct fsl_elbc_mtd *chips[MAX_BANKS]; u8 __iomem *addr; /* Address of assigned FCM buffer */ @@ -879,7 +879,7 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev) } elbc_fcm_ctrl->counter++; - nand_hw_control_init(&elbc_fcm_ctrl->controller); + nand_controller_init(&elbc_fcm_ctrl->controller); fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl; } else { elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index 75d3c951f61a..96130d91e32c 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -51,7 +51,7 @@ struct fsl_ifc_mtd { /* overview of the fsl ifc controller */ struct fsl_ifc_nand_ctrl { - struct nand_hw_control controller; + struct nand_controller controller; struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT]; void __iomem *addr; /* Address of assigned IFC buffer */ @@ -1004,7 +1004,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) ifc_nand_ctrl->addr = NULL; fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl; - nand_hw_control_init(&ifc_nand_ctrl->controller); + nand_controller_init(&ifc_nand_ctrl->controller); } else { ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand; } diff --git a/drivers/mtd/nand/raw/jz4780_nand.c b/drivers/mtd/nand/raw/jz4780_nand.c index e69f6ae4c539..49841dad347c 100644 --- a/drivers/mtd/nand/raw/jz4780_nand.c +++ b/drivers/mtd/nand/raw/jz4780_nand.c @@ -44,7 +44,7 @@ struct jz4780_nand_cs { struct jz4780_nand_controller { struct device *dev; struct jz4780_bch *bch; - struct nand_hw_control controller; + struct nand_controller controller; unsigned int num_banks; struct list_head chips; int selected; @@ -65,7 +65,8 @@ static inline struct jz4780_nand_chip *to_jz4780_nand_chip(struct mtd_info *mtd) return container_of(mtd_to_nand(mtd), struct jz4780_nand_chip, chip); } -static inline struct jz4780_nand_controller *to_jz4780_nand_controller(struct nand_hw_control *ctrl) +static inline struct jz4780_nand_controller +*to_jz4780_nand_controller(struct nand_controller *ctrl) { return container_of(ctrl, struct jz4780_nand_controller, controller); } @@ -368,7 +369,7 @@ static int jz4780_nand_probe(struct platform_device *pdev) nfc->dev = dev; nfc->num_banks = num_banks; - nand_hw_control_init(&nfc->controller); + nand_controller_init(&nfc->controller); INIT_LIST_HEAD(&nfc->chips); ret = jz4780_nand_init_chips(nfc, pdev); diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index 80a074cccb82..bd5f9a4b7b16 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -318,7 +318,7 @@ struct marvell_nfc_caps { * @dma_buf: 32-bit aligned buffer for DMA transfers (NFCv1 only) */ struct marvell_nfc { - struct nand_hw_control controller; + struct nand_controller controller; struct device *dev; void __iomem *regs; struct clk *core_clk; @@ -335,7 +335,7 @@ struct marvell_nfc { u8 *dma_buf; }; -static inline struct marvell_nfc *to_marvell_nfc(struct nand_hw_control *ctrl) +static inline struct marvell_nfc *to_marvell_nfc(struct nand_controller *ctrl) { return container_of(ctrl, struct marvell_nfc, controller); } @@ -2745,7 +2745,7 @@ static int marvell_nfc_probe(struct platform_device *pdev) return -ENOMEM; nfc->dev = dev; - nand_hw_control_init(&nfc->controller); + nand_controller_init(&nfc->controller); INIT_LIST_HEAD(&nfc->chips); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index e6b14b79c8a8..7bc6be3f6ec0 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -145,7 +145,7 @@ struct mtk_nfc_clk { }; struct mtk_nfc { - struct nand_hw_control controller; + struct nand_controller controller; struct mtk_ecc_config ecc_cfg; struct mtk_nfc_clk clk; struct mtk_ecc *ecc; diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index e545e03a214e..dcdf0f373100 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5000,8 +5000,8 @@ static void nand_set_defaults(struct nand_chip *chip) chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; if (!chip->controller) { - chip->controller = &chip->hwcontrol; - nand_hw_control_init(chip->controller); + chip->controller = &chip->dummy_controller; + nand_controller_init(chip->controller); } if (!chip->buf_align) diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c index d8a806894937..540fa1a0ea24 100644 --- a/drivers/mtd/nand/raw/ndfc.c +++ b/drivers/mtd/nand/raw/ndfc.c @@ -39,7 +39,7 @@ struct ndfc_controller { void __iomem *ndfcbase; struct nand_chip chip; int chip_select; - struct nand_hw_control ndfc_control; + struct nand_controller ndfc_control; }; static struct ndfc_controller ndfc_ctrl[NDFC_MAX_CS]; @@ -218,7 +218,7 @@ static int ndfc_probe(struct platform_device *ofdev) ndfc = &ndfc_ctrl[cs]; ndfc->chip_select = cs; - nand_hw_control_init(&ndfc->ndfc_control); + nand_controller_init(&ndfc->ndfc_control); ndfc->ofdev = ofdev; dev_set_drvdata(&ofdev->dev, ndfc); diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index e50c64adc3c8..e943b2e5a5e2 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -145,7 +145,7 @@ static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc, static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10}; /* Shared among all NAND instances to synchronize access to the ECC Engine */ -static struct nand_hw_control omap_gpmc_controller = { +static struct nand_controller omap_gpmc_controller = { .lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock), .wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq), }; diff --git a/drivers/mtd/nand/raw/oxnas_nand.c b/drivers/mtd/nand/raw/oxnas_nand.c index d649d5944826..01b00bb69c1e 100644 --- a/drivers/mtd/nand/raw/oxnas_nand.c +++ b/drivers/mtd/nand/raw/oxnas_nand.c @@ -32,7 +32,7 @@ #define OXNAS_NAND_MAX_CHIPS 1 struct oxnas_nand_ctrl { - struct nand_hw_control base; + struct nand_controller base; void __iomem *io_base; struct clk *clk; struct nand_chip *chips[OXNAS_NAND_MAX_CHIPS]; @@ -96,7 +96,7 @@ static int oxnas_nand_probe(struct platform_device *pdev) if (!oxnas) return -ENOMEM; - nand_hw_control_init(&oxnas->base); + nand_controller_init(&oxnas->base); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); oxnas->io_base = devm_ioremap_resource(&pdev->dev, res); diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 645630953f38..aa6c3e026ef1 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -365,7 +365,7 @@ struct nandc_regs { * from all connected NAND devices pagesize */ struct qcom_nand_controller { - struct nand_hw_control controller; + struct nand_controller controller; struct list_head host_list; struct device *dev; @@ -2728,7 +2728,7 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) INIT_LIST_HEAD(&nandc->desc_list); INIT_LIST_HEAD(&nandc->host_list); - nand_hw_control_init(&nandc->controller); + nand_controller_init(&nandc->controller); return 0; } diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c index 5a4a68790653..e8bf64832213 100644 --- a/drivers/mtd/nand/raw/s3c2410.c +++ b/drivers/mtd/nand/raw/s3c2410.c @@ -162,7 +162,7 @@ enum s3c_nand_clk_state { */ struct s3c2410_nand_info { /* mtd info */ - struct nand_hw_control controller; + struct nand_controller controller; struct s3c2410_nand_mtd *mtds; struct s3c2410_platform_nand *platform; @@ -1094,7 +1094,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); - nand_hw_control_init(&info->controller); + nand_controller_init(&info->controller); /* get the clock source and enable it */ diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 4b11cd4a79be..07f3ff9a28f2 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -234,7 +234,7 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) * controller events */ struct sunxi_nfc { - struct nand_hw_control controller; + struct nand_controller controller; struct device *dev; void __iomem *regs; struct clk *ahb_clk; @@ -247,7 +247,7 @@ struct sunxi_nfc { struct dma_chan *dmac; }; -static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl) +static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_controller *ctrl) { return container_of(ctrl, struct sunxi_nfc, controller); } @@ -2012,7 +2012,7 @@ static int sunxi_nfc_probe(struct platform_device *pdev) return -ENOMEM; nfc->dev = dev; - nand_hw_control_init(&nfc->controller); + nand_controller_init(&nfc->controller); INIT_LIST_HEAD(&nfc->chips); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/mtd/nand/raw/tango_nand.c b/drivers/mtd/nand/raw/tango_nand.c index f2052fae21c7..dd7a26efdf4f 100644 --- a/drivers/mtd/nand/raw/tango_nand.c +++ b/drivers/mtd/nand/raw/tango_nand.c @@ -83,7 +83,7 @@ #define MAX_CS 4 struct tango_nfc { - struct nand_hw_control hw; + struct nand_controller hw; void __iomem *reg_base; void __iomem *mem_base; void __iomem *pbus_base; @@ -654,7 +654,7 @@ static int tango_nand_probe(struct platform_device *pdev) return PTR_ERR(nfc->chan); platform_set_drvdata(pdev, nfc); - nand_hw_control_init(&nfc->hw); + nand_controller_init(&nfc->hw); nfc->freq_kHz = clk_get_rate(clk) / 1000; for_each_child_of_node(pdev->dev.of_node, np) { diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c index 56c0aa1bc81f..31c0d9ca9d23 100644 --- a/drivers/mtd/nand/raw/tegra_nand.c +++ b/drivers/mtd/nand/raw/tegra_nand.c @@ -164,7 +164,7 @@ HWSTATUS_RBSY_VALUE(NAND_STATUS_READY)) struct tegra_nand_controller { - struct nand_hw_control controller; + struct nand_controller controller; struct device *dev; void __iomem *regs; int irq; @@ -187,7 +187,7 @@ struct tegra_nand_chip { }; static inline struct tegra_nand_controller * - to_tegra_ctrl(struct nand_hw_control *hw_ctrl) + to_tegra_ctrl(struct nand_controller *hw_ctrl) { return container_of(hw_ctrl, struct tegra_nand_controller, controller); } @@ -1136,7 +1136,7 @@ static int tegra_nand_probe(struct platform_device *pdev) return -ENOMEM; ctrl->dev = &pdev->dev; - nand_hw_control_init(&ctrl->controller); + nand_controller_init(&ctrl->controller); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ctrl->regs = devm_ioremap_resource(&pdev->dev, res); diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c index 5fe9da8b4a0a..8f5bbbac4612 100644 --- a/drivers/mtd/nand/raw/txx9ndfmc.c +++ b/drivers/mtd/nand/raw/txx9ndfmc.c @@ -73,7 +73,7 @@ struct txx9ndfmc_drvdata { void __iomem *base; unsigned char hold; /* in gbusclock */ unsigned char spw; /* in gbusclock */ - struct nand_hw_control hw_control; + struct nand_controller hw_control; }; static struct platform_device *mtd_to_platdev(struct mtd_info *mtd) @@ -303,7 +303,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n", (gbusclk + 500000) / 1000000, hold, spw); - nand_hw_control_init(&drvdata->hw_control); + nand_controller_init(&drvdata->hw_control); platform_set_drvdata(dev, drvdata); txx9ndfmc_initialize(dev); diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 598d356de83f..93a2678e0f0d 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -510,20 +510,21 @@ struct nand_id { }; /** - * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices + * struct nand_controller - Structure used to describe a NAND controller + * * @lock: protection lock * @active: the mtd device which holds the controller currently * @wq: wait queue to sleep on if a NAND operation is in * progress used instead of the per chip wait queue * when a hw controller is available. */ -struct nand_hw_control { +struct nand_controller { spinlock_t lock; struct nand_chip *active; wait_queue_head_t wq; }; -static inline void nand_hw_control_init(struct nand_hw_control *nfc) +static inline void nand_controller_init(struct nand_controller *nfc) { nfc->active = NULL; spin_lock_init(&nfc->lock); @@ -1197,7 +1198,8 @@ int nand_op_parser_exec_op(struct nand_chip *chip, * setting the read-retry mode. Mostly needed for MLC NAND. * @ecc: [BOARDSPECIFIC] ECC control structure * @buf_align: minimum buffer alignment required by a platform - * @hwcontrol: platform-specific hardware control structure + * @dummy_controller: dummy controller implementation for drivers that can + * only control a single chip * @erase: [REPLACEABLE] erase function * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transferring * data from array to read regs (tR). @@ -1333,11 +1335,11 @@ struct nand_chip { flstate_t state; uint8_t *oob_poi; - struct nand_hw_control *controller; + struct nand_controller *controller; struct nand_ecc_ctrl ecc; unsigned long buf_align; - struct nand_hw_control hwcontrol; + struct nand_controller dummy_controller; uint8_t *bbt; struct nand_bbt_descr *bbt_td; -- cgit v1.2.3 From 05b54c7bac906c443fd0b23ab8954e0560b33e5c Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 19 Jul 2018 01:05:46 +0200 Subject: mtd: rawnand: add hooks that may be called during nand_scan() In order to remove the limitation that forbids dynamic allocation in nand_scan_ident(), we must create a path that will be the same for all controller drivers. The idea is to use nand_scan() instead of the widely used nand_scan_ident()/nand_scan_tail() couple. In order to achieve this, controller drivers will need to adjust some parameters between these two functions depending on the NAND chip wired on them. This takes the form of two new hooks (->{attach,detach}_chip()) that are placed in a new nand_controller_ops structure, which is then attached to the nand_controller object at driver initialization time. ->attach_chip() is called between nand_scan_ident() and nand_scan_tail(), and ->detach_chip() is called in the error path of nand_scan() and in nand_cleanup(). Note that some NAND controller drivers don't have a dedicated nand_controller object and instead rely on the default/dummy one embedded in nand_chip. If you're in this case and still want to initialize the controller ops, you'll have to manipulate chip->dummy_controller directly. Last but not least, it's worth mentioning that we plan to move some of the controller related hooks placed in nand_chip into nand_controller_ops to make the separation between NAND chip and NAND controller methods clearer. Signed-off-by: Miquel Raynal Acked-by: Boris Brezillon --- drivers/mtd/nand/raw/nand_base.c | 32 ++++++++++++++++++++++++++++++-- include/linux/mtd/rawnand.h | 21 +++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index dcdf0f373100..dea41fa25be1 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -6718,6 +6718,20 @@ err_free_buf: } EXPORT_SYMBOL(nand_scan_tail); +static int nand_attach(struct nand_chip *chip) +{ + if (chip->controller->ops && chip->controller->ops->attach_chip) + return chip->controller->ops->attach_chip(chip); + + return 0; +} + +static void nand_detach(struct nand_chip *chip) +{ + if (chip->controller->ops && chip->controller->ops->detach_chip) + chip->controller->ops->detach_chip(chip); +} + /** * nand_scan_with_ids - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure @@ -6731,11 +6745,21 @@ EXPORT_SYMBOL(nand_scan_tail); int nand_scan_with_ids(struct mtd_info *mtd, int maxchips, struct nand_flash_dev *ids) { + struct nand_chip *chip = mtd_to_nand(mtd); int ret; ret = nand_scan_ident(mtd, maxchips, ids); - if (!ret) - ret = nand_scan_tail(mtd); + if (ret) + return ret; + + ret = nand_attach(chip); + if (ret) + return ret; + + ret = nand_scan_tail(mtd); + if (ret) + nand_detach(chip); + return ret; } EXPORT_SYMBOL(nand_scan_with_ids); @@ -6763,7 +6787,11 @@ void nand_cleanup(struct nand_chip *chip) /* Free manufacturer priv data. */ nand_manufacturer_cleanup(chip); + + /* Free controller specific allocations after chip identification */ + nand_detach(chip); } + EXPORT_SYMBOL_GPL(nand_cleanup); /** diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 93a2678e0f0d..fbd6b29cf22c 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -509,6 +509,25 @@ struct nand_id { int len; }; +/** + * struct nand_controller_ops - Controller operations + * + * @attach_chip: this method is called after the NAND detection phase after + * flash ID and MTD fields such as erase size, page size and OOB + * size have been set up. ECC requirements are available if + * provided by the NAND chip or device tree. Typically used to + * choose the appropriate ECC configuration and allocate + * associated resources. + * This hook is optional. + * @detach_chip: free all resources allocated/claimed in + * nand_controller_ops->attach_chip(). + * This hook is optional. + */ +struct nand_controller_ops { + int (*attach_chip)(struct nand_chip *chip); + void (*detach_chip)(struct nand_chip *chip); +}; + /** * struct nand_controller - Structure used to describe a NAND controller * @@ -517,11 +536,13 @@ struct nand_id { * @wq: wait queue to sleep on if a NAND operation is in * progress used instead of the per chip wait queue * when a hw controller is available. + * @ops: NAND controller operations. */ struct nand_controller { spinlock_t lock; struct nand_chip *active; wait_queue_head_t wq; + const struct nand_controller_ops *ops; }; static inline void nand_controller_init(struct nand_controller *nfc) -- cgit v1.2.3 From d05a9524d7ec513648ad5e0fbd944e6ff3ddbd84 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 20 Jul 2018 00:38:13 +0200 Subject: mtd: rawnand: jz4740: Include gpio/consumer.h instead of gpio.h GPIO consumers should no longer include , and instead include . Also, explicitly include since it seems to be missing after switching to . This fixes a build error when selecting the driver without selecting GPIOLIB, which can happen when COMPILE_TEST=y. Fixes: 6968e07e8169 ("mtd: rawnand: jz4740: Allow selection of this driver when COMPILE_TEST=y") Reported-by: Miquel Raynal Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/jz4740_nand.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/jz4740_nand.c b/drivers/mtd/nand/raw/jz4740_nand.c index 9bb8a89e09f9..a4052b03249c 100644 --- a/drivers/mtd/nand/raw/jz4740_nand.c +++ b/drivers/mtd/nand/raw/jz4740_nand.c @@ -13,6 +13,7 @@ * */ +#include #include #include #include @@ -23,7 +24,7 @@ #include #include -#include +#include #include -- cgit v1.2.3 From 49919d9c9e960c95712288ff08b96d4980464a5a Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 20 Jul 2018 11:57:38 +0200 Subject: mtd: rawnand: sunxi: Remove gpio.h and of_gpio.h inclusions Commit ddd5ed3a90e7 ("mtd: rawnand: sunxi: Remove support for GPIO-based Ready/Busy polling") removed GPIO-based RB polling. We no longer need to include gpio headers. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/sunxi_nand.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 07f3ff9a28f2..2c0cbe72b449 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -29,14 +29,12 @@ #include #include #include -#include #include #include #include #include #include #include -#include #include #include #include -- cgit v1.2.3 From 89956d118e5a95afc00f51c8f2757a453d8848b9 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 20 Jul 2018 11:57:39 +0200 Subject: mtd: rawnand: atmel: Stop including gpio.h gpio/consumer.h defines everything we need, and it's clearly stated in gpio.h that GPIO consumers should directly stop including gpio.h if they can. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/atmel/nand-controller.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index 855cc7729c43..4d27401f1299 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -52,7 +52,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From c1070db3839fb12513d0717e1e03d04eda1e2d93 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 20 Jul 2018 11:57:40 +0200 Subject: mtd: rawnand: au1550nd: Remove unneeded gpio.h inclusion We don't use the GPIO API in this driver, let's just remove the inclusion. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/au1550nd.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c index df0ef1f1e2f5..35f5c84cd331 100644 --- a/drivers/mtd/nand/raw/au1550nd.c +++ b/drivers/mtd/nand/raw/au1550nd.c @@ -8,7 +8,6 @@ */ #include -#include #include #include #include -- cgit v1.2.3 From b2342c1c80cebee38a2c219437f9382c2a2a1b5d Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:14:55 +0200 Subject: mtd: rawnand: davinci: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Also change the unused "struct device *dev" parameter of the driver structure into a platform device to reuse it in the ->attach_chip() hook. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/davinci_nand.c | 195 +++++++++++++++++++----------------- 1 file changed, 102 insertions(+), 93 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index 626c9363e460..40145e206a6b 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -53,7 +53,7 @@ struct davinci_nand_info { struct nand_chip chip; - struct device *dev; + struct platform_device *pdev; bool is_readmode; @@ -605,6 +605,104 @@ static struct davinci_nand_pdata } #endif +static int davinci_nand_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct davinci_nand_info *info = to_davinci_nand(mtd); + struct davinci_nand_pdata *pdata = nand_davinci_get_pdata(info->pdev); + int ret = 0; + + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + + switch (info->chip.ecc.mode) { + case NAND_ECC_NONE: + pdata->ecc_bits = 0; + break; + case NAND_ECC_SOFT: + pdata->ecc_bits = 0; + /* + * This driver expects Hamming based ECC when ecc_mode is set + * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to + * avoid adding an extra ->ecc_algo field to + * davinci_nand_pdata. + */ + info->chip.ecc.algo = NAND_ECC_HAMMING; + break; + case NAND_ECC_HW: + if (pdata->ecc_bits == 4) { + /* + * No sanity checks: CPUs must support this, + * and the chips may not use NAND_BUSWIDTH_16. + */ + + /* No sharing 4-bit hardware between chipselects yet */ + spin_lock_irq(&davinci_nand_lock); + if (ecc4_busy) + ret = -EBUSY; + else + ecc4_busy = true; + spin_unlock_irq(&davinci_nand_lock); + + if (ret == -EBUSY) + return ret; + + info->chip.ecc.calculate = nand_davinci_calculate_4bit; + info->chip.ecc.correct = nand_davinci_correct_4bit; + info->chip.ecc.hwctl = nand_davinci_hwctl_4bit; + info->chip.ecc.bytes = 10; + info->chip.ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; + info->chip.ecc.algo = NAND_ECC_BCH; + } else { + /* 1bit ecc hamming */ + info->chip.ecc.calculate = nand_davinci_calculate_1bit; + info->chip.ecc.correct = nand_davinci_correct_1bit; + info->chip.ecc.hwctl = nand_davinci_hwctl_1bit; + info->chip.ecc.bytes = 3; + info->chip.ecc.algo = NAND_ECC_HAMMING; + } + info->chip.ecc.size = 512; + info->chip.ecc.strength = pdata->ecc_bits; + break; + default: + return -EINVAL; + } + + /* + * Update ECC layout if needed ... for 1-bit HW ECC, the default + * is OK, but it allocates 6 bytes when only 3 are needed (for + * each 512 bytes). For the 4-bit HW ECC, that default is not + * usable: 10 bytes are needed, not 6. + */ + if (pdata->ecc_bits == 4) { + int chunks = mtd->writesize / 512; + + if (!chunks || mtd->oobsize < 16) { + dev_dbg(&info->pdev->dev, "too small\n"); + return -EINVAL; + } + + /* For small page chips, preserve the manufacturer's + * badblock marking data ... and make sure a flash BBT + * table marker fits in the free bytes. + */ + if (chunks == 1) { + mtd_set_ooblayout(mtd, &hwecc4_small_ooblayout_ops); + } else if (chunks == 4 || chunks == 8) { + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST; + } else { + return -EIO; + } + } + + return ret; +} + +static const struct nand_controller_ops davinci_nand_controller_ops = { + .attach_chip = davinci_nand_attach_chip, +}; + static int nand_davinci_probe(struct platform_device *pdev) { struct davinci_nand_pdata *pdata; @@ -658,7 +756,7 @@ static int nand_davinci_probe(struct platform_device *pdev) return -EADDRNOTAVAIL; } - info->dev = &pdev->dev; + info->pdev = pdev; info->base = base; info->vaddr = vaddr; @@ -708,97 +806,13 @@ static int nand_davinci_probe(struct platform_device *pdev) spin_unlock_irq(&davinci_nand_lock); /* Scan to find existence of the device(s) */ - ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL); + info->chip.dummy_controller.ops = &davinci_nand_controller_ops; + ret = nand_scan(mtd, pdata->mask_chipsel ? 2 : 1); if (ret < 0) { dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); return ret; } - switch (info->chip.ecc.mode) { - case NAND_ECC_NONE: - pdata->ecc_bits = 0; - break; - case NAND_ECC_SOFT: - pdata->ecc_bits = 0; - /* - * This driver expects Hamming based ECC when ecc_mode is set - * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to - * avoid adding an extra ->ecc_algo field to - * davinci_nand_pdata. - */ - info->chip.ecc.algo = NAND_ECC_HAMMING; - break; - case NAND_ECC_HW: - if (pdata->ecc_bits == 4) { - /* No sanity checks: CPUs must support this, - * and the chips may not use NAND_BUSWIDTH_16. - */ - - /* No sharing 4-bit hardware between chipselects yet */ - spin_lock_irq(&davinci_nand_lock); - if (ecc4_busy) - ret = -EBUSY; - else - ecc4_busy = true; - spin_unlock_irq(&davinci_nand_lock); - - if (ret == -EBUSY) - return ret; - - info->chip.ecc.calculate = nand_davinci_calculate_4bit; - info->chip.ecc.correct = nand_davinci_correct_4bit; - info->chip.ecc.hwctl = nand_davinci_hwctl_4bit; - info->chip.ecc.bytes = 10; - info->chip.ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; - info->chip.ecc.algo = NAND_ECC_BCH; - } else { - /* 1bit ecc hamming */ - info->chip.ecc.calculate = nand_davinci_calculate_1bit; - info->chip.ecc.correct = nand_davinci_correct_1bit; - info->chip.ecc.hwctl = nand_davinci_hwctl_1bit; - info->chip.ecc.bytes = 3; - info->chip.ecc.algo = NAND_ECC_HAMMING; - } - info->chip.ecc.size = 512; - info->chip.ecc.strength = pdata->ecc_bits; - break; - default: - return -EINVAL; - } - - /* Update ECC layout if needed ... for 1-bit HW ECC, the default - * is OK, but it allocates 6 bytes when only 3 are needed (for - * each 512 bytes). For the 4-bit HW ECC, that default is not - * usable: 10 bytes are needed, not 6. - */ - if (pdata->ecc_bits == 4) { - int chunks = mtd->writesize / 512; - - if (!chunks || mtd->oobsize < 16) { - dev_dbg(&pdev->dev, "too small\n"); - ret = -EINVAL; - goto err; - } - - /* For small page chips, preserve the manufacturer's - * badblock marking data ... and make sure a flash BBT - * table marker fits in the free bytes. - */ - if (chunks == 1) { - mtd_set_ooblayout(mtd, &hwecc4_small_ooblayout_ops); - } else if (chunks == 4 || chunks == 8) { - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); - info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST; - } else { - ret = -EIO; - goto err; - } - } - - ret = nand_scan_tail(mtd); - if (ret < 0) - goto err; - if (pdata->parts) ret = mtd_device_register(mtd, pdata->parts, pdata->nr_parts); else @@ -815,11 +829,6 @@ static int nand_davinci_probe(struct platform_device *pdev) err_cleanup_nand: nand_cleanup(&info->chip); -err: - spin_lock_irq(&davinci_nand_lock); - if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) - ecc4_busy = false; - spin_unlock_irq(&davinci_nand_lock); return ret; } -- cgit v1.2.3 From d03af162bb09cdd321e422e375c2715f705c094b Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:14:56 +0200 Subject: mtd: rawnand: denali: convert to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon Acked-by: Masahiro Yamada --- drivers/mtd/nand/raw/denali.c | 139 +++++++++++++++++++++++------------------- 1 file changed, 77 insertions(+), 62 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c index 4d53f41ada08..ca18612c4201 100644 --- a/drivers/mtd/nand/raw/denali.c +++ b/drivers/mtd/nand/raw/denali.c @@ -1205,62 +1205,12 @@ static int denali_multidev_fixup(struct denali_nand_info *denali) return 0; } -int denali_init(struct denali_nand_info *denali) +static int denali_attach_chip(struct nand_chip *chip) { - struct nand_chip *chip = &denali->nand; struct mtd_info *mtd = nand_to_mtd(chip); - u32 features = ioread32(denali->reg + FEATURES); + struct denali_nand_info *denali = mtd_to_denali(mtd); int ret; - mtd->dev.parent = denali->dev; - denali_hw_init(denali); - - init_completion(&denali->complete); - spin_lock_init(&denali->irq_lock); - - denali_clear_irq_all(denali); - - ret = devm_request_irq(denali->dev, denali->irq, denali_isr, - IRQF_SHARED, DENALI_NAND_NAME, denali); - if (ret) { - dev_err(denali->dev, "Unable to request IRQ\n"); - return ret; - } - - denali_enable_irq(denali); - denali_reset_banks(denali); - - denali->active_bank = DENALI_INVALID_BANK; - - nand_set_flash_node(chip, denali->dev->of_node); - /* Fallback to the default name if DT did not give "label" property */ - if (!mtd->name) - mtd->name = "denali-nand"; - - chip->select_chip = denali_select_chip; - chip->read_byte = denali_read_byte; - chip->write_byte = denali_write_byte; - chip->read_word = denali_read_word; - chip->cmd_ctrl = denali_cmd_ctrl; - chip->dev_ready = denali_dev_ready; - chip->waitfunc = denali_waitfunc; - - if (features & FEATURES__INDEX_ADDR) { - denali->host_read = denali_indexed_read; - denali->host_write = denali_indexed_write; - } else { - denali->host_read = denali_direct_read; - denali->host_write = denali_direct_write; - } - - /* clk rate info is needed for setup_data_interface */ - if (denali->clk_rate && denali->clk_x_rate) - chip->setup_data_interface = denali_setup_data_interface; - - ret = nand_scan_ident(mtd, denali->max_banks, NULL); - if (ret) - goto disable_irq; - if (ioread32(denali->reg + FEATURES) & FEATURES__DMA) denali->dma_avail = 1; @@ -1293,7 +1243,7 @@ int denali_init(struct denali_nand_info *denali) mtd->oobsize - denali->oob_skip_bytes); if (ret) { dev_err(denali->dev, "Failed to setup ECC settings.\n"); - goto disable_irq; + return ret; } dev_dbg(denali->dev, @@ -1337,7 +1287,7 @@ int denali_init(struct denali_nand_info *denali) ret = denali_multidev_fixup(denali); if (ret) - goto disable_irq; + return ret; /* * This buffer is DMA-mapped by denali_{read,write}_page_raw. Do not @@ -1345,26 +1295,92 @@ int denali_init(struct denali_nand_info *denali) * guarantee DMA-safe alignment. */ denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); - if (!denali->buf) { - ret = -ENOMEM; - goto disable_irq; + if (!denali->buf) + return -ENOMEM; + + return 0; +} + +static void denali_detach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct denali_nand_info *denali = mtd_to_denali(mtd); + + kfree(denali->buf); +} + +static const struct nand_controller_ops denali_controller_ops = { + .attach_chip = denali_attach_chip, + .detach_chip = denali_detach_chip, +}; + +int denali_init(struct denali_nand_info *denali) +{ + struct nand_chip *chip = &denali->nand; + struct mtd_info *mtd = nand_to_mtd(chip); + u32 features = ioread32(denali->reg + FEATURES); + int ret; + + mtd->dev.parent = denali->dev; + denali_hw_init(denali); + + init_completion(&denali->complete); + spin_lock_init(&denali->irq_lock); + + denali_clear_irq_all(denali); + + ret = devm_request_irq(denali->dev, denali->irq, denali_isr, + IRQF_SHARED, DENALI_NAND_NAME, denali); + if (ret) { + dev_err(denali->dev, "Unable to request IRQ\n"); + return ret; + } + + denali_enable_irq(denali); + denali_reset_banks(denali); + + denali->active_bank = DENALI_INVALID_BANK; + + nand_set_flash_node(chip, denali->dev->of_node); + /* Fallback to the default name if DT did not give "label" property */ + if (!mtd->name) + mtd->name = "denali-nand"; + + chip->select_chip = denali_select_chip; + chip->read_byte = denali_read_byte; + chip->write_byte = denali_write_byte; + chip->read_word = denali_read_word; + chip->cmd_ctrl = denali_cmd_ctrl; + chip->dev_ready = denali_dev_ready; + chip->waitfunc = denali_waitfunc; + + if (features & FEATURES__INDEX_ADDR) { + denali->host_read = denali_indexed_read; + denali->host_write = denali_indexed_write; + } else { + denali->host_read = denali_direct_read; + denali->host_write = denali_direct_write; } - ret = nand_scan_tail(mtd); + /* clk rate info is needed for setup_data_interface */ + if (denali->clk_rate && denali->clk_x_rate) + chip->setup_data_interface = denali_setup_data_interface; + + chip->dummy_controller.ops = &denali_controller_ops; + ret = nand_scan(mtd, denali->max_banks); if (ret) - goto free_buf; + goto disable_irq; ret = mtd_device_register(mtd, NULL, 0); if (ret) { dev_err(denali->dev, "Failed to register MTD: %d\n", ret); goto cleanup_nand; } + return 0; cleanup_nand: nand_cleanup(chip); -free_buf: - kfree(denali->buf); disable_irq: denali_disable_irq(denali); @@ -1377,7 +1393,6 @@ void denali_remove(struct denali_nand_info *denali) struct mtd_info *mtd = nand_to_mtd(&denali->nand); nand_release(mtd); - kfree(denali->buf); denali_disable_irq(denali); } EXPORT_SYMBOL(denali_remove); -- cgit v1.2.3 From 99dc9d95ec5a20dddb9f4462d576c63bfc06d8ad Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 10:35:57 +0200 Subject: mtd: rawnand: fsl_elbc: return meaningful values Return -ENOTSUPP instead of -1 from ->chip_init_tail() before migrating this driver to use nand_scan() and transform this function to be a callback run by the core. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/fsl_elbc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index 0aa54a949653..b5637dce6d47 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -700,7 +700,7 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) dev_err(priv->dev, "fsl_elbc_init: page size %d is not supported\n", mtd->writesize); - return -1; + return -ENOTSUPP; } return 0; -- cgit v1.2.3 From 5bf3e76b0abf6f527ae2223a308b7decebcdd23e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 10:37:40 +0200 Subject: mtd: rawnand: fsl_elbc: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon Notes: "pw 947037" --- drivers/mtd/nand/raw/fsl_elbc_nand.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index b5637dce6d47..55f449b711fd 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -637,9 +637,9 @@ static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP; } -static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) +static int fsl_elbc_attach_chip(struct nand_chip *chip) { - struct nand_chip *chip = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(chip); struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); struct fsl_lbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_regs __iomem *lbc = ctrl->regs; @@ -706,6 +706,10 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) return 0; } +static const struct nand_controller_ops fsl_elbc_controller_ops = { + .attach_chip = fsl_elbc_attach_chip, +}; + static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { @@ -910,15 +914,8 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev) if (ret) goto err; - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - goto err; - - ret = fsl_elbc_chip_init_tail(mtd); - if (ret) - goto err; - - ret = nand_scan_tail(mtd); + priv->chip.controller->ops = &fsl_elbc_controller_ops; + ret = nand_scan(mtd, 1); if (ret) goto err; -- cgit v1.2.3 From 37b0375d7398468b61c5e472e2634a4173f36627 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:14:58 +0200 Subject: mtd: rawnand: fsl_ifc: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/fsl_ifc_nand.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index 96130d91e32c..24f59d0066af 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -714,9 +714,9 @@ static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip, return nand_prog_page_end_op(chip); } -static int fsl_ifc_chip_init_tail(struct mtd_info *mtd) +static int fsl_ifc_attach_chip(struct nand_chip *chip) { - struct nand_chip *chip = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(chip); struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); dev_dbg(priv->dev, "%s: nand->numchips = %d\n", __func__, @@ -757,6 +757,10 @@ static int fsl_ifc_chip_init_tail(struct mtd_info *mtd) return 0; } +static const struct nand_controller_ops fsl_ifc_controller_ops = { + .attach_chip = fsl_ifc_attach_chip, +}; + static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) { struct fsl_ifc_ctrl *ctrl = priv->ctrl; @@ -1046,15 +1050,8 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) if (ret) goto err; - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - goto err; - - ret = fsl_ifc_chip_init_tail(mtd); - if (ret) - goto err; - - ret = nand_scan_tail(mtd); + priv->chip.controller->ops = &fsl_ifc_controller_ops; + ret = nand_scan(mtd, 1); if (ret) goto err; -- cgit v1.2.3 From 3bbddfa3d292c5d228049ab09a94076286d9e906 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:14:59 +0200 Subject: mtd: rawnand: fsmc: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/fsmc_nand.c | 148 +++++++++++++++++++++------------------ 1 file changed, 78 insertions(+), 70 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 59524d181bfe..f418236fa020 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -919,6 +919,82 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev, return 0; } +static int fsmc_nand_attach_chip(struct nand_chip *nand) +{ + struct mtd_info *mtd = nand_to_mtd(nand); + struct fsmc_nand_data *host = mtd_to_fsmc(mtd); + + if (AMBA_REV_BITS(host->pid) >= 8) { + switch (mtd->oobsize) { + case 16: + case 64: + case 128: + case 224: + case 256: + break; + default: + dev_warn(host->dev, + "No oob scheme defined for oobsize %d\n", + mtd->oobsize); + return -EINVAL; + } + + mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops); + + return 0; + } + + switch (nand->ecc.mode) { + case NAND_ECC_HW: + dev_info(host->dev, "Using 1-bit HW ECC scheme\n"); + nand->ecc.calculate = fsmc_read_hwecc_ecc1; + nand->ecc.correct = nand_correct_data; + nand->ecc.bytes = 3; + nand->ecc.strength = 1; + break; + + case NAND_ECC_SOFT: + if (nand->ecc.algo == NAND_ECC_BCH) { + dev_info(host->dev, + "Using 4-bit SW BCH ECC scheme\n"); + break; + } + + case NAND_ECC_ON_DIE: + break; + + default: + dev_err(host->dev, "Unsupported ECC mode!\n"); + return -ENOTSUPP; + } + + /* + * Don't set layout for BCH4 SW ECC. This will be + * generated later in nand_bch_init() later. + */ + if (nand->ecc.mode == NAND_ECC_HW) { + switch (mtd->oobsize) { + case 16: + case 64: + case 128: + mtd_set_ooblayout(mtd, + &fsmc_ecc1_ooblayout_ops); + break; + default: + dev_warn(host->dev, + "No oob scheme defined for oobsize %d\n", + mtd->oobsize); + return -EINVAL; + } + } + + return 0; +} + +static const struct nand_controller_ops fsmc_nand_controller_ops = { + .attach_chip = fsmc_nand_attach_chip, +}; + /* * fsmc_nand_probe - Probe function * @pdev: platform device structure @@ -1048,76 +1124,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) /* * Scan to find existence of the device */ - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) { - dev_err(&pdev->dev, "No NAND Device found!\n"); - goto release_dma_write_chan; - } - - if (AMBA_REV_BITS(host->pid) >= 8) { - switch (mtd->oobsize) { - case 16: - case 64: - case 128: - case 224: - case 256: - break; - default: - dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n", - mtd->oobsize); - ret = -EINVAL; - goto release_dma_write_chan; - } - - mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops); - } else { - switch (nand->ecc.mode) { - case NAND_ECC_HW: - dev_info(&pdev->dev, "Using 1-bit HW ECC scheme\n"); - nand->ecc.calculate = fsmc_read_hwecc_ecc1; - nand->ecc.correct = nand_correct_data; - nand->ecc.bytes = 3; - nand->ecc.strength = 1; - break; - - case NAND_ECC_SOFT: - if (nand->ecc.algo == NAND_ECC_BCH) { - dev_info(&pdev->dev, "Using 4-bit SW BCH ECC scheme\n"); - break; - } - - case NAND_ECC_ON_DIE: - break; - - default: - dev_err(&pdev->dev, "Unsupported ECC mode!\n"); - goto release_dma_write_chan; - } - - /* - * Don't set layout for BCH4 SW ECC. This will be - * generated later in nand_bch_init() later. - */ - if (nand->ecc.mode == NAND_ECC_HW) { - switch (mtd->oobsize) { - case 16: - case 64: - case 128: - mtd_set_ooblayout(mtd, - &fsmc_ecc1_ooblayout_ops); - break; - default: - dev_warn(&pdev->dev, - "No oob scheme defined for oobsize %d\n", - mtd->oobsize); - ret = -EINVAL; - goto release_dma_write_chan; - } - } - } - - /* Second stage of scan to fill MTD data-structures */ - ret = nand_scan_tail(mtd); + nand->dummy_controller.ops = &fsmc_nand_controller_ops; + ret = nand_scan(mtd, 1); if (ret) goto release_dma_write_chan; -- cgit v1.2.3 From 5f8374d9b8b564e3be3309205d4d02d285c8d64a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:15:00 +0200 Subject: mtd: rawnand: gpmi: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 56 ++++++++++++++++++------------ 1 file changed, 33 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 6294d018df65..1c1ebbc82824 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -744,9 +744,9 @@ static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this) * [2] Allocate a read/write data buffer. * The gpmi_alloc_dma_buffer can be called twice. * We allocate a PAGE_SIZE length buffer if gpmi_alloc_dma_buffer - * is called before the nand_scan_ident; and we allocate a buffer - * of the real NAND page size when the gpmi_alloc_dma_buffer is - * called after the nand_scan_ident. + * is called before the NAND identification; and we allocate a + * buffer of the real NAND page size when the gpmi_alloc_dma_buffer + * is called after. */ this->data_buffer_dma = kzalloc(mtd->writesize ?: PAGE_SIZE, GFP_DMA | GFP_KERNEL); @@ -1865,6 +1865,34 @@ static int gpmi_init_last(struct gpmi_nand_data *this) return 0; } +static int gpmi_nand_attach_chip(struct nand_chip *chip) +{ + struct gpmi_nand_data *this = nand_get_controller_data(chip); + int ret; + + if (chip->bbt_options & NAND_BBT_USE_FLASH) { + chip->bbt_options |= NAND_BBT_NO_OOB; + + if (of_property_read_bool(this->dev->of_node, + "fsl,no-blockmark-swap")) + this->swap_block_mark = false; + } + dev_dbg(this->dev, "Blockmark swapping %sabled\n", + this->swap_block_mark ? "en" : "dis"); + + ret = gpmi_init_last(this); + if (ret) + return ret; + + chip->options |= NAND_SKIP_BBTSCAN; + + return 0; +} + +static const struct nand_controller_ops gpmi_nand_controller_ops = { + .attach_chip = gpmi_nand_attach_chip, +}; + static int gpmi_nand_init(struct gpmi_nand_data *this) { struct nand_chip *chip = &this->nand; @@ -1905,26 +1933,8 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) if (ret) goto err_out; - ret = nand_scan_ident(mtd, GPMI_IS_MX6(this) ? 2 : 1, NULL); - if (ret) - goto err_out; - - if (chip->bbt_options & NAND_BBT_USE_FLASH) { - chip->bbt_options |= NAND_BBT_NO_OOB; - - if (of_property_read_bool(this->dev->of_node, - "fsl,no-blockmark-swap")) - this->swap_block_mark = false; - } - dev_dbg(this->dev, "Blockmark swapping %sabled\n", - this->swap_block_mark ? "en" : "dis"); - - ret = gpmi_init_last(this); - if (ret) - goto err_out; - - chip->options |= NAND_SKIP_BBTSCAN; - ret = nand_scan_tail(mtd); + chip->dummy_controller.ops = &gpmi_nand_controller_ops; + ret = nand_scan(mtd, GPMI_IS_MX6(this) ? 2 : 1); if (ret) goto err_out; -- cgit v1.2.3 From 07c1a4b3d8231910424bffabff5c0d51c80e2f6b Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:15:01 +0200 Subject: mtd: rawnand: hisi504: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/hisi504_nand.c | 78 +++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c index a1e009c8e556..950dc7789296 100644 --- a/drivers/mtd/nand/raw/hisi504_nand.c +++ b/drivers/mtd/nand/raw/hisi504_nand.c @@ -709,9 +709,50 @@ static int hisi_nfc_ecc_probe(struct hinfc_host *host) return 0; } +static int hisi_nfc_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct hinfc_host *host = nand_get_controller_data(chip); + int flag; + + host->buffer = dmam_alloc_coherent(host->dev, + mtd->writesize + mtd->oobsize, + &host->dma_buffer, GFP_KERNEL); + if (!host->buffer) + return -ENOMEM; + + host->dma_oob = host->dma_buffer + mtd->writesize; + memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize); + + flag = hinfc_read(host, HINFC504_CON); + flag &= ~(HINFC504_CON_PAGESIZE_MASK << HINFC504_CON_PAGEISZE_SHIFT); + switch (mtd->writesize) { + case 2048: + flag |= (0x001 << HINFC504_CON_PAGEISZE_SHIFT); + break; + /* + * TODO: add more pagesize support, + * default pagesize has been set in hisi_nfc_host_init + */ + default: + dev_err(host->dev, "NON-2KB page size nand flash\n"); + return -EINVAL; + } + hinfc_write(host, flag, HINFC504_CON); + + if (chip->ecc.mode == NAND_ECC_HW) + hisi_nfc_ecc_probe(host); + + return 0; +} + +static const struct nand_controller_ops hisi_nfc_controller_ops = { + .attach_chip = hisi_nfc_attach_chip, +}; + static int hisi_nfc_probe(struct platform_device *pdev) { - int ret = 0, irq, flag, max_chips = HINFC504_MAX_CHIP; + int ret = 0, irq, max_chips = HINFC504_MAX_CHIP; struct device *dev = &pdev->dev; struct hinfc_host *host; struct nand_chip *chip; @@ -769,42 +810,11 @@ static int hisi_nfc_probe(struct platform_device *pdev) return ret; } - ret = nand_scan_ident(mtd, max_chips, NULL); + chip->dummy_controller.ops = &hisi_nfc_controller_ops; + ret = nand_scan(mtd, max_chips); if (ret) return ret; - host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize, - &host->dma_buffer, GFP_KERNEL); - if (!host->buffer) - return -ENOMEM; - - host->dma_oob = host->dma_buffer + mtd->writesize; - memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize); - - flag = hinfc_read(host, HINFC504_CON); - flag &= ~(HINFC504_CON_PAGESIZE_MASK << HINFC504_CON_PAGEISZE_SHIFT); - switch (mtd->writesize) { - case 2048: - flag |= (0x001 << HINFC504_CON_PAGEISZE_SHIFT); break; - /* - * TODO: add more pagesize support, - * default pagesize has been set in hisi_nfc_host_init - */ - default: - dev_err(dev, "NON-2KB page size nand flash\n"); - return -EINVAL; - } - hinfc_write(host, flag, HINFC504_CON); - - if (chip->ecc.mode == NAND_ECC_HW) - hisi_nfc_ecc_probe(host); - - ret = nand_scan_tail(mtd); - if (ret) { - dev_err(dev, "nand_scan_tail failed: %d\n", ret); - return ret; - } - ret = mtd_device_register(mtd, NULL, 0); if (ret) { dev_err(dev, "Err MTD partition=%d\n", ret); -- cgit v1.2.3 From eef2b13a33141aa49e17831937acd744cc049844 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:15:02 +0200 Subject: mtd: rawnand: jz4780: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Acked-by: Harvey Hunt Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/jz4780_nand.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/jz4780_nand.c b/drivers/mtd/nand/raw/jz4780_nand.c index 49841dad347c..db4fa60bd52a 100644 --- a/drivers/mtd/nand/raw/jz4780_nand.c +++ b/drivers/mtd/nand/raw/jz4780_nand.c @@ -158,9 +158,8 @@ static int jz4780_nand_ecc_correct(struct mtd_info *mtd, u8 *dat, return jz4780_bch_correct(nfc->bch, ¶ms, dat, read_ecc); } -static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *dev) +static int jz4780_nand_attach_chip(struct nand_chip *chip) { - struct nand_chip *chip = &nand->chip; struct mtd_info *mtd = nand_to_mtd(chip); struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(chip->controller); int eccbytes; @@ -171,7 +170,8 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de switch (chip->ecc.mode) { case NAND_ECC_HW: if (!nfc->bch) { - dev_err(dev, "HW BCH selected, but BCH controller not found\n"); + dev_err(nfc->dev, + "HW BCH selected, but BCH controller not found\n"); return -ENODEV; } @@ -180,15 +180,16 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de chip->ecc.correct = jz4780_nand_ecc_correct; /* fall through */ case NAND_ECC_SOFT: - dev_info(dev, "using %s (strength %d, size %d, bytes %d)\n", - (nfc->bch) ? "hardware BCH" : "software ECC", - chip->ecc.strength, chip->ecc.size, chip->ecc.bytes); + dev_info(nfc->dev, "using %s (strength %d, size %d, bytes %d)\n", + (nfc->bch) ? "hardware BCH" : "software ECC", + chip->ecc.strength, chip->ecc.size, chip->ecc.bytes); break; case NAND_ECC_NONE: - dev_info(dev, "not using ECC\n"); + dev_info(nfc->dev, "not using ECC\n"); break; default: - dev_err(dev, "ECC mode %d not supported\n", chip->ecc.mode); + dev_err(nfc->dev, "ECC mode %d not supported\n", + chip->ecc.mode); return -EINVAL; } @@ -200,7 +201,7 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes; if (eccbytes > mtd->oobsize - 2) { - dev_err(dev, + dev_err(nfc->dev, "invalid ECC config: required %d ECC bytes, but only %d are available", eccbytes, mtd->oobsize - 2); return -EINVAL; @@ -211,6 +212,10 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de return 0; } +static const struct nand_controller_ops jz4780_nand_controller_ops = { + .attach_chip = jz4780_nand_attach_chip, +}; + static int jz4780_nand_init_chip(struct platform_device *pdev, struct jz4780_nand_controller *nfc, struct device_node *np, @@ -280,15 +285,8 @@ static int jz4780_nand_init_chip(struct platform_device *pdev, chip->controller = &nfc->controller; nand_set_flash_node(chip, np); - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - return ret; - - ret = jz4780_nand_init_ecc(nand, dev); - if (ret) - return ret; - - ret = nand_scan_tail(mtd); + chip->controller->ops = &jz4780_nand_controller_ops; + ret = nand_scan(mtd, 1); if (ret) return ret; -- cgit v1.2.3 From f4a48d7bf89e073fb76a1ef18935d36169a3b6b1 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:15:04 +0200 Subject: mtd: rawnand: lpc32xx_slc: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/lpc32xx_slc.c | 77 +++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index 42820aa1abab..a4e8b7e75135 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -779,6 +779,46 @@ static struct lpc32xx_nand_cfg_slc *lpc32xx_parse_dt(struct device *dev) return ncfg; } +static int lpc32xx_nand_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + + /* OOB and ECC CPU and DMA work areas */ + host->ecc_buf = (uint32_t *)(host->data_buf + LPC32XX_DMA_DATA_SIZE); + + /* + * Small page FLASH has a unique OOB layout, but large and huge + * page FLASH use the standard layout. Small page FLASH uses a + * custom BBT marker layout. + */ + if (mtd->writesize <= 512) + mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); + + /* These sizes remain the same regardless of page size */ + chip->ecc.size = 256; + chip->ecc.bytes = LPC32XX_SLC_DEV_ECC_BYTES; + chip->ecc.prepad = 0; + chip->ecc.postpad = 0; + + /* + * Use a custom BBT marker setup for small page FLASH that + * won't interfere with the ECC layout. Large and huge page + * FLASH use the standard layout. + */ + if ((chip->bbt_options & NAND_BBT_USE_FLASH) && + mtd->writesize <= 512) { + chip->bbt_td = &bbt_smallpage_main_descr; + chip->bbt_md = &bbt_smallpage_mirror_descr; + } + + return 0; +} + +static const struct nand_controller_ops lpc32xx_nand_controller_ops = { + .attach_chip = lpc32xx_nand_attach_chip, +}; + /* * Probe for NAND controller */ @@ -884,41 +924,8 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) } /* Find NAND device */ - res = nand_scan_ident(mtd, 1, NULL); - if (res) - goto release_dma; - - /* OOB and ECC CPU and DMA work areas */ - host->ecc_buf = (uint32_t *)(host->data_buf + LPC32XX_DMA_DATA_SIZE); - - /* - * Small page FLASH has a unique OOB layout, but large and huge - * page FLASH use the standard layout. Small page FLASH uses a - * custom BBT marker layout. - */ - if (mtd->writesize <= 512) - mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); - - /* These sizes remain the same regardless of page size */ - chip->ecc.size = 256; - chip->ecc.bytes = LPC32XX_SLC_DEV_ECC_BYTES; - chip->ecc.prepad = chip->ecc.postpad = 0; - - /* - * Use a custom BBT marker setup for small page FLASH that - * won't interfere with the ECC layout. Large and huge page - * FLASH use the standard layout. - */ - if ((chip->bbt_options & NAND_BBT_USE_FLASH) && - mtd->writesize <= 512) { - chip->bbt_td = &bbt_smallpage_main_descr; - chip->bbt_md = &bbt_smallpage_mirror_descr; - } - - /* - * Fills out all the uninitialized function pointers with the defaults - */ - res = nand_scan_tail(mtd); + chip->dummy_controller.ops = &lpc32xx_nand_controller_ops; + res = nand_scan(mtd, 1); if (res) goto release_dma; -- cgit v1.2.3 From 8831e48bad1425d237219dedde547f6061081807 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:15:05 +0200 Subject: mtd: rawnand: marvell: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/marvell_nand.c | 205 +++++++++++++++++++----------------- 1 file changed, 108 insertions(+), 97 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index bd5f9a4b7b16..218e09431d3d 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2290,6 +2290,111 @@ static int marvell_nfc_setup_data_interface(struct mtd_info *mtd, int chipnr, return 0; } +static int marvell_nand_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip); + struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(nfc->dev); + int ret; + + if (pdata && pdata->flash_bbt) + chip->bbt_options |= NAND_BBT_USE_FLASH; + + if (chip->bbt_options & NAND_BBT_USE_FLASH) { + /* + * We'll use a bad block table stored in-flash and don't + * allow writing the bad block marker to the flash. + */ + chip->bbt_options |= NAND_BBT_NO_OOB_BBM; + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; + } + + /* Save the chip-specific fields of NDCR */ + marvell_nand->ndcr = NDCR_PAGE_SZ(mtd->writesize); + if (chip->options & NAND_BUSWIDTH_16) + marvell_nand->ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C; + + /* + * On small page NANDs, only one cycle is needed to pass the + * column address. + */ + if (mtd->writesize <= 512) { + marvell_nand->addr_cyc = 1; + } else { + marvell_nand->addr_cyc = 2; + marvell_nand->ndcr |= NDCR_RA_START; + } + + /* + * Now add the number of cycles needed to pass the row + * address. + * + * Addressing a chip using CS 2 or 3 should also need the third row + * cycle but due to inconsistance in the documentation and lack of + * hardware to test this situation, this case is not supported. + */ + if (chip->options & NAND_ROW_ADDR_3) + marvell_nand->addr_cyc += 3; + else + marvell_nand->addr_cyc += 2; + + if (pdata) { + chip->ecc.size = pdata->ecc_step_size; + chip->ecc.strength = pdata->ecc_strength; + } + + ret = marvell_nand_ecc_init(mtd, &chip->ecc); + if (ret) { + dev_err(nfc->dev, "ECC init failed: %d\n", ret); + return ret; + } + + if (chip->ecc.mode == NAND_ECC_HW) { + /* + * Subpage write not available with hardware ECC, prohibit also + * subpage read as in userspace subpage access would still be + * allowed and subpage write, if used, would lead to numerous + * uncorrectable ECC errors. + */ + chip->options |= NAND_NO_SUBPAGE_WRITE; + } + + if (pdata || nfc->caps->legacy_of_bindings) { + /* + * We keep the MTD name unchanged to avoid breaking platforms + * where the MTD cmdline parser is used and the bootloader + * has not been updated to use the new naming scheme. + */ + mtd->name = "pxa3xx_nand-0"; + } else if (!mtd->name) { + /* + * If the new bindings are used and the bootloader has not been + * updated to pass a new mtdparts parameter on the cmdline, you + * should define the following property in your NAND node, ie: + * + * label = "main-storage"; + * + * This way, mtd->name will be set by the core when + * nand_set_flash_node() is called. + */ + mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL, + "%s:nand.%d", dev_name(nfc->dev), + marvell_nand->sels[0].cs); + if (!mtd->name) { + dev_err(nfc->dev, "Failed to allocate mtd->name\n"); + return -ENOMEM; + } + } + + return 0; +} + +static const struct nand_controller_ops marvell_nand_controller_ops = { + .attach_chip = marvell_nand_attach_chip, +}; + static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, struct device_node *np) { @@ -2432,105 +2537,10 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, marvell_nand->ndtr1 = readl_relaxed(nfc->regs + NDTR1); chip->options |= NAND_BUSWIDTH_AUTO; - ret = nand_scan_ident(mtd, marvell_nand->nsels, NULL); - if (ret) { - dev_err(dev, "could not identify the nand chip\n"); - return ret; - } - - if (pdata && pdata->flash_bbt) - chip->bbt_options |= NAND_BBT_USE_FLASH; - - if (chip->bbt_options & NAND_BBT_USE_FLASH) { - /* - * We'll use a bad block table stored in-flash and don't - * allow writing the bad block marker to the flash. - */ - chip->bbt_options |= NAND_BBT_NO_OOB_BBM; - chip->bbt_td = &bbt_main_descr; - chip->bbt_md = &bbt_mirror_descr; - } - - /* Save the chip-specific fields of NDCR */ - marvell_nand->ndcr = NDCR_PAGE_SZ(mtd->writesize); - if (chip->options & NAND_BUSWIDTH_16) - marvell_nand->ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C; - - /* - * On small page NANDs, only one cycle is needed to pass the - * column address. - */ - if (mtd->writesize <= 512) { - marvell_nand->addr_cyc = 1; - } else { - marvell_nand->addr_cyc = 2; - marvell_nand->ndcr |= NDCR_RA_START; - } - - /* - * Now add the number of cycles needed to pass the row - * address. - * - * Addressing a chip using CS 2 or 3 should also need the third row - * cycle but due to inconsistance in the documentation and lack of - * hardware to test this situation, this case is not supported. - */ - if (chip->options & NAND_ROW_ADDR_3) - marvell_nand->addr_cyc += 3; - else - marvell_nand->addr_cyc += 2; - - if (pdata) { - chip->ecc.size = pdata->ecc_step_size; - chip->ecc.strength = pdata->ecc_strength; - } - - ret = marvell_nand_ecc_init(mtd, &chip->ecc); - if (ret) { - dev_err(dev, "ECC init failed: %d\n", ret); - return ret; - } - - if (chip->ecc.mode == NAND_ECC_HW) { - /* - * Subpage write not available with hardware ECC, prohibit also - * subpage read as in userspace subpage access would still be - * allowed and subpage write, if used, would lead to numerous - * uncorrectable ECC errors. - */ - chip->options |= NAND_NO_SUBPAGE_WRITE; - } - - if (pdata || nfc->caps->legacy_of_bindings) { - /* - * We keep the MTD name unchanged to avoid breaking platforms - * where the MTD cmdline parser is used and the bootloader - * has not been updated to use the new naming scheme. - */ - mtd->name = "pxa3xx_nand-0"; - } else if (!mtd->name) { - /* - * If the new bindings are used and the bootloader has not been - * updated to pass a new mtdparts parameter on the cmdline, you - * should define the following property in your NAND node, ie: - * - * label = "main-storage"; - * - * This way, mtd->name will be set by the core when - * nand_set_flash_node() is called. - */ - mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL, - "%s:nand.%d", dev_name(nfc->dev), - marvell_nand->sels[0].cs); - if (!mtd->name) { - dev_err(nfc->dev, "Failed to allocate mtd->name\n"); - return -ENOMEM; - } - } - ret = nand_scan_tail(mtd); + ret = nand_scan(mtd, marvell_nand->nsels); if (ret) { - dev_err(dev, "nand_scan_tail failed: %d\n", ret); + dev_err(dev, "could not scan the nand chip\n"); return ret; } @@ -2746,6 +2756,7 @@ static int marvell_nfc_probe(struct platform_device *pdev) nfc->dev = dev; nand_controller_init(&nfc->controller); + nfc->controller.ops = &marvell_nand_controller_ops; INIT_LIST_HEAD(&nfc->chips); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- cgit v1.2.3 From 1ce7826d7f178ab683d40d280ca079cf281aecff Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:15:06 +0200 Subject: mtd: rawnand: mtk: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon Acked-by: Xiaolei Li --- drivers/mtd/nand/raw/mtk_nand.c | 75 ++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index 7bc6be3f6ec0..57b5ed1699e3 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -1250,13 +1250,54 @@ static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd) return 0; } +static int mtk_nfc_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct device *dev = mtd->dev.parent; + struct mtk_nfc *nfc = nand_get_controller_data(chip); + struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); + int len; + int ret; + + if (chip->options & NAND_BUSWIDTH_16) { + dev_err(dev, "16bits buswidth not supported"); + return -EINVAL; + } + + /* store bbt magic in page, cause OOB is not protected */ + if (chip->bbt_options & NAND_BBT_USE_FLASH) + chip->bbt_options |= NAND_BBT_NO_OOB; + + ret = mtk_nfc_ecc_init(dev, mtd); + if (ret) + return ret; + + ret = mtk_nfc_set_spare_per_sector(&mtk_nand->spare_per_sector, mtd); + if (ret) + return ret; + + mtk_nfc_set_fdm(&mtk_nand->fdm, mtd); + mtk_nfc_set_bad_mark_ctl(&mtk_nand->bad_mark, mtd); + + len = mtd->writesize + mtd->oobsize; + nfc->buffer = devm_kzalloc(dev, len, GFP_KERNEL); + if (!nfc->buffer) + return -ENOMEM; + + return 0; +} + +static const struct nand_controller_ops mtk_nfc_controller_ops = { + .attach_chip = mtk_nfc_attach_chip, +}; + static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, struct device_node *np) { struct mtk_nfc_nand_chip *chip; struct nand_chip *nand; struct mtd_info *mtd; - int nsels, len; + int nsels; u32 tmp; int ret; int i; @@ -1324,36 +1365,7 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, mtk_nfc_hw_init(nfc); - ret = nand_scan_ident(mtd, nsels, NULL); - if (ret) - return ret; - - /* store bbt magic in page, cause OOB is not protected */ - if (nand->bbt_options & NAND_BBT_USE_FLASH) - nand->bbt_options |= NAND_BBT_NO_OOB; - - ret = mtk_nfc_ecc_init(dev, mtd); - if (ret) - return -EINVAL; - - if (nand->options & NAND_BUSWIDTH_16) { - dev_err(dev, "16bits buswidth not supported"); - return -EINVAL; - } - - ret = mtk_nfc_set_spare_per_sector(&chip->spare_per_sector, mtd); - if (ret) - return ret; - - mtk_nfc_set_fdm(&chip->fdm, mtd); - mtk_nfc_set_bad_mark_ctl(&chip->bad_mark, mtd); - - len = mtd->writesize + mtd->oobsize; - nfc->buffer = devm_kzalloc(dev, len, GFP_KERNEL); - if (!nfc->buffer) - return -ENOMEM; - - ret = nand_scan_tail(mtd); + ret = nand_scan(mtd, nsels); if (ret) return ret; @@ -1443,6 +1455,7 @@ static int mtk_nfc_probe(struct platform_device *pdev) spin_lock_init(&nfc->controller.lock); init_waitqueue_head(&nfc->controller.wq); INIT_LIST_HEAD(&nfc->chips); + nfc->controller.ops = &mtk_nfc_controller_ops; /* probe defer if not ready */ nfc->ecc = of_mtk_ecc_get(np); -- cgit v1.2.3 From 96fa8e6e2619c0fe5b5935404d6cc18bae92c7ae Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:15:07 +0200 Subject: mtd: rawnand: mxc: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/mxc_nand.c | 136 +++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 65 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index c6eff61e909d..4c9214dea424 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -1691,6 +1691,74 @@ static int mxcnd_probe_dt(struct mxc_nand_host *host) } #endif +static int mxcnd_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + struct device *dev = mtd->dev.parent; + + switch (chip->ecc.mode) { + case NAND_ECC_HW: + chip->ecc.read_page = mxc_nand_read_page; + chip->ecc.read_page_raw = mxc_nand_read_page_raw; + chip->ecc.read_oob = mxc_nand_read_oob; + chip->ecc.write_page = mxc_nand_write_page_ecc; + chip->ecc.write_page_raw = mxc_nand_write_page_raw; + chip->ecc.write_oob = mxc_nand_write_oob; + break; + + case NAND_ECC_SOFT: + break; + + default: + return -EINVAL; + } + + if (chip->bbt_options & NAND_BBT_USE_FLASH) { + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; + } + + /* Allocate the right size buffer now */ + devm_kfree(dev, (void *)host->data_buf); + host->data_buf = devm_kzalloc(dev, mtd->writesize + mtd->oobsize, + GFP_KERNEL); + if (!host->data_buf) + return -ENOMEM; + + /* Call preset again, with correct writesize chip time */ + host->devtype_data->preset(mtd); + + if (!chip->ecc.bytes) { + if (host->eccsize == 8) + chip->ecc.bytes = 18; + else if (host->eccsize == 4) + chip->ecc.bytes = 9; + } + + /* + * Experimentation shows that i.MX NFC can only handle up to 218 oob + * bytes. Limit used_oobsize to 218 so as to not confuse copy_spare() + * into copying invalid data to/from the spare IO buffer, as this + * might cause ECC data corruption when doing sub-page write to a + * partially written page. + */ + host->used_oobsize = min(mtd->oobsize, 218U); + + if (chip->ecc.mode == NAND_ECC_HW) { + if (is_imx21_nfc(host) || is_imx27_nfc(host)) + chip->ecc.strength = 1; + else + chip->ecc.strength = (host->eccsize == 4) ? 4 : 8; + } + + return 0; +} + +static const struct nand_controller_ops mxcnd_controller_ops = { + .attach_chip = mxcnd_attach_chip, +}; + static int mxcnd_probe(struct platform_device *pdev) { struct nand_chip *this; @@ -1830,71 +1898,9 @@ static int mxcnd_probe(struct platform_device *pdev) host->devtype_data->irq_control(host, 1); } - /* first scan to find the device and get the page size */ - err = nand_scan_ident(mtd, is_imx25_nfc(host) ? 4 : 1, NULL); - if (err) - goto escan; - - switch (this->ecc.mode) { - case NAND_ECC_HW: - this->ecc.read_page = mxc_nand_read_page; - this->ecc.read_page_raw = mxc_nand_read_page_raw; - this->ecc.read_oob = mxc_nand_read_oob; - this->ecc.write_page = mxc_nand_write_page_ecc; - this->ecc.write_page_raw = mxc_nand_write_page_raw; - this->ecc.write_oob = mxc_nand_write_oob; - break; - - case NAND_ECC_SOFT: - break; - - default: - err = -EINVAL; - goto escan; - } - - if (this->bbt_options & NAND_BBT_USE_FLASH) { - this->bbt_td = &bbt_main_descr; - this->bbt_md = &bbt_mirror_descr; - } - - /* allocate the right size buffer now */ - devm_kfree(&pdev->dev, (void *)host->data_buf); - host->data_buf = devm_kzalloc(&pdev->dev, mtd->writesize + mtd->oobsize, - GFP_KERNEL); - if (!host->data_buf) { - err = -ENOMEM; - goto escan; - } - - /* Call preset again, with correct writesize this time */ - host->devtype_data->preset(mtd); - - if (!this->ecc.bytes) { - if (host->eccsize == 8) - this->ecc.bytes = 18; - else if (host->eccsize == 4) - this->ecc.bytes = 9; - } - - /* - * Experimentation shows that i.MX NFC can only handle up to 218 oob - * bytes. Limit used_oobsize to 218 so as to not confuse copy_spare() - * into copying invalid data to/from the spare IO buffer, as this - * might cause ECC data corruption when doing sub-page write to a - * partially written page. - */ - host->used_oobsize = min(mtd->oobsize, 218U); - - if (this->ecc.mode == NAND_ECC_HW) { - if (is_imx21_nfc(host) || is_imx27_nfc(host)) - this->ecc.strength = 1; - else - this->ecc.strength = (host->eccsize == 4) ? 4 : 8; - } - - /* second phase scan */ - err = nand_scan_tail(mtd); + /* Scan the NAND device */ + this->dummy_controller.ops = &mxcnd_controller_ops; + err = nand_scan(mtd, is_imx25_nfc(host) ? 4 : 1); if (err) goto escan; -- cgit v1.2.3 From 5cbad9e3da60db9b3e5cf7e5b6a609578cbf93e0 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:15:08 +0200 Subject: mtd: rawnand: nandsim: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/nandsim.c | 82 +++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nandsim.c b/drivers/mtd/nand/raw/nandsim.c index 8a3b36cfe5ea..71ac034aee9c 100644 --- a/drivers/mtd/nand/raw/nandsim.c +++ b/drivers/mtd/nand/raw/nandsim.c @@ -2192,6 +2192,48 @@ static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) return; } +static int ns_attach_chip(struct nand_chip *chip) +{ + unsigned int eccsteps, eccbytes; + + if (!bch) + return 0; + + if (!mtd_nand_has_bch()) { + NS_ERR("BCH ECC support is disabled\n"); + return -EINVAL; + } + + /* Use 512-byte ecc blocks */ + eccsteps = nsmtd->writesize / 512; + eccbytes = ((bch * 13) + 7) / 8; + + /* Do not bother supporting small page devices */ + if (nsmtd->oobsize < 64 || !eccsteps) { + NS_ERR("BCH not available on small page devices\n"); + return -EINVAL; + } + + if (((eccbytes * eccsteps) + 2) > nsmtd->oobsize) { + NS_ERR("Invalid BCH value %u\n", bch); + return -EINVAL; + } + + chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_BCH; + chip->ecc.size = 512; + chip->ecc.strength = bch; + chip->ecc.bytes = eccbytes; + + NS_INFO("Using %u-bit/%u bytes BCH ECC\n", bch, chip->ecc.size); + + return 0; +} + +static const struct nand_controller_ops ns_controller_ops = { + .attach_chip = ns_attach_chip, +}; + /* * Module initialization function */ @@ -2276,44 +2318,10 @@ static int __init ns_init_module(void) if ((retval = parse_gravepages()) != 0) goto error; - retval = nand_scan_ident(nsmtd, 1, NULL); - if (retval) { - NS_ERR("cannot scan NAND Simulator device\n"); - goto error; - } - - if (bch) { - unsigned int eccsteps, eccbytes; - if (!mtd_nand_has_bch()) { - NS_ERR("BCH ECC support is disabled\n"); - retval = -EINVAL; - goto error; - } - /* use 512-byte ecc blocks */ - eccsteps = nsmtd->writesize/512; - eccbytes = (bch*13+7)/8; - /* do not bother supporting small page devices */ - if ((nsmtd->oobsize < 64) || !eccsteps) { - NS_ERR("bch not available on small page devices\n"); - retval = -EINVAL; - goto error; - } - if ((eccbytes*eccsteps+2) > nsmtd->oobsize) { - NS_ERR("invalid bch value %u\n", bch); - retval = -EINVAL; - goto error; - } - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_BCH; - chip->ecc.size = 512; - chip->ecc.strength = bch; - chip->ecc.bytes = eccbytes; - NS_INFO("using %u-bit/%u bytes BCH ECC\n", bch, chip->ecc.size); - } - - retval = nand_scan_tail(nsmtd); + chip->dummy_controller.ops = &ns_controller_ops; + retval = nand_scan(nsmtd, 1); if (retval) { - NS_ERR("can't register NAND Simulator\n"); + NS_ERR("Could not scan NAND Simulator device\n"); goto error; } -- cgit v1.2.3 From 127483187a4afc143d59cb2809ccdb9399f7d27e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:15:10 +0200 Subject: mtd: rawnand: s3c2410: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/s3c2410.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c index e8bf64832213..c21e8892394a 100644 --- a/drivers/mtd/nand/raw/s3c2410.c +++ b/drivers/mtd/nand/raw/s3c2410.c @@ -915,20 +915,19 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, } /** - * s3c2410_nand_update_chip - post probe update - * @info: The controller instance. - * @nmtd: The driver version of the MTD instance. + * s3c2410_nand_attach_chip - Init the ECC engine after NAND scan + * @chip: The NAND chip * - * This routine is called after the chip probe has successfully completed - * and the relevant per-chip information updated. This call ensure that + * This hook is called by the core after the identification of the NAND chip, + * once the relevant per-chip information is up to date.. This call ensure that * we update the internal state accordingly. * * The internal state is currently limited to the ECC state information. */ -static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info, - struct s3c2410_nand_mtd *nmtd) +static int s3c2410_nand_attach_chip(struct nand_chip *chip) { - struct nand_chip *chip = &nmtd->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); switch (chip->ecc.mode) { @@ -998,6 +997,10 @@ static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info, return 0; } +static const struct nand_controller_ops s3c24xx_nand_controller_ops = { + .attach_chip = s3c2410_nand_attach_chip, +}; + static const struct of_device_id s3c24xx_nand_dt_ids[] = { { .compatible = "samsung,s3c2410-nand", @@ -1095,6 +1098,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); nand_controller_init(&info->controller); + info->controller.ops = &s3c24xx_nand_controller_ops; /* get the clock source and enable it */ @@ -1166,15 +1170,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) mtd->dev.parent = &pdev->dev; s3c2410_nand_init_chip(info, nmtd, sets); - err = nand_scan_ident(mtd, (sets) ? sets->nr_chips : 1, NULL); - if (err) - goto exit_error; - - err = s3c2410_nand_update_chip(info, nmtd); - if (err < 0) - goto exit_error; - - err = nand_scan_tail(mtd); + err = nand_scan(mtd, sets ? sets->nr_chips : 1); if (err) goto exit_error; -- cgit v1.2.3 From 5f20da0fe8af638266a5e8449ab5ebfe6112c2c0 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:15:11 +0200 Subject: mtd: rawnand: sh_flctl: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/sh_flctl.c | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c index c7abceffcc40..bb8866e05ff7 100644 --- a/drivers/mtd/nand/raw/sh_flctl.c +++ b/drivers/mtd/nand/raw/sh_flctl.c @@ -1002,10 +1002,17 @@ static void flctl_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) flctl->index += len; } -static int flctl_chip_init_tail(struct mtd_info *mtd) +static int flctl_chip_attach_chip(struct nand_chip *chip) { + struct mtd_info *mtd = nand_to_mtd(chip); struct sh_flctl *flctl = mtd_to_flctl(mtd); - struct nand_chip *chip = &flctl->chip; + + /* + * NAND_BUSWIDTH_16 may have been set by nand_scan_ident(). + * Add the SEL_16BIT flag in flctl->flcmncr_base. + */ + if (chip->options & NAND_BUSWIDTH_16) + flctl->flcmncr_base |= SEL_16BIT; if (mtd->writesize == 512) { flctl->page_size = 0; @@ -1063,6 +1070,10 @@ static int flctl_chip_init_tail(struct mtd_info *mtd) return 0; } +static const struct nand_controller_ops flctl_nand_controller_ops = { + .attach_chip = flctl_chip_attach_chip, +}; + static irqreturn_t flctl_handle_flste(int irq, void *dev_id) { struct sh_flctl *flctl = dev_id; @@ -1191,25 +1202,8 @@ static int flctl_probe(struct platform_device *pdev) flctl_setup_dma(flctl); - ret = nand_scan_ident(flctl_mtd, 1, NULL); - if (ret) - goto err_chip; - - if (nand->options & NAND_BUSWIDTH_16) { - /* - * NAND_BUSWIDTH_16 may have been set by nand_scan_ident(). - * Add the SEL_16BIT flag in pdata->flcmncr_val and re-assign - * flctl->flcmncr_base to pdata->flcmncr_val. - */ - pdata->flcmncr_val |= SEL_16BIT; - flctl->flcmncr_base = pdata->flcmncr_val; - } - - ret = flctl_chip_init_tail(flctl_mtd); - if (ret) - goto err_chip; - - ret = nand_scan_tail(flctl_mtd); + nand->dummy_controller.ops = &flctl_nand_controller_ops; + ret = nand_scan(flctl_mtd, 1); if (ret) goto err_chip; -- cgit v1.2.3 From 2a4d9c16e8d10ecee4422f90f6a47acf737e3447 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:15:13 +0200 Subject: mtd: rawnand: sunxi: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/sunxi_nand.c | 43 +++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 2c0cbe72b449..1f0b7ee38df5 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -1814,12 +1814,21 @@ static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) } } -static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, - struct device_node *np) +static int sunxi_nand_attach_chip(struct nand_chip *nand) { - struct nand_chip *nand = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(nand); + struct nand_ecc_ctrl *ecc = &nand->ecc; + struct device_node *np = nand_get_flash_node(nand); int ret; + if (nand->bbt_options & NAND_BBT_USE_FLASH) + nand->bbt_options |= NAND_BBT_NO_OOB; + + if (nand->options & NAND_NEED_SCRAMBLING) + nand->options |= NAND_NO_SUBPAGE_WRITE; + + nand->options |= NAND_SUBPAGE_READ; + if (!ecc->size) { ecc->size = nand->ecc_step_ds; ecc->strength = nand->ecc_strength_ds; @@ -1844,6 +1853,10 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, return 0; } +static const struct nand_controller_ops sunxi_nand_controller_ops = { + .attach_chip = sunxi_nand_attach_chip, +}; + static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, struct device_node *np) { @@ -1909,6 +1922,8 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, /* Default tR value specified in the ONFI spec (chapter 4.15.1) */ nand->chip_delay = 200; nand->controller = &nfc->controller; + nand->controller->ops = &sunxi_nand_controller_ops; + /* * Set the ECC mode to the default value in case nothing is specified * in the DT. @@ -1925,30 +1940,10 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, mtd = nand_to_mtd(nand); mtd->dev.parent = dev; - ret = nand_scan_ident(mtd, nsels, NULL); + ret = nand_scan(mtd, nsels); if (ret) return ret; - if (nand->bbt_options & NAND_BBT_USE_FLASH) - nand->bbt_options |= NAND_BBT_NO_OOB; - - if (nand->options & NAND_NEED_SCRAMBLING) - nand->options |= NAND_NO_SUBPAGE_WRITE; - - nand->options |= NAND_SUBPAGE_READ; - - ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np); - if (ret) { - dev_err(dev, "ECC init failed: %d\n", ret); - return ret; - } - - ret = nand_scan_tail(mtd); - if (ret) { - dev_err(dev, "nand_scan_tail failed: %d\n", ret); - return ret; - } - ret = mtd_device_register(mtd, NULL, 0); if (ret) { dev_err(dev, "failed to register mtd device: %d\n", ret); -- cgit v1.2.3 From 6a9035ceb980ac7ded378fa83f73d9664128777c Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:15:14 +0200 Subject: mtd: rawnand: tango: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/tango_nand.c | 40 +++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/tango_nand.c b/drivers/mtd/nand/raw/tango_nand.c index dd7a26efdf4f..72698691727d 100644 --- a/drivers/mtd/nand/raw/tango_nand.c +++ b/drivers/mtd/nand/raw/tango_nand.c @@ -517,6 +517,28 @@ static int tango_set_timings(struct mtd_info *mtd, int csline, return 0; } +static int tango_attach_chip(struct nand_chip *chip) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + + ecc->mode = NAND_ECC_HW; + ecc->algo = NAND_ECC_BCH; + ecc->bytes = DIV_ROUND_UP(ecc->strength * FIELD_ORDER, BITS_PER_BYTE); + + ecc->read_page_raw = tango_read_page_raw; + ecc->write_page_raw = tango_write_page_raw; + ecc->read_page = tango_read_page; + ecc->write_page = tango_write_page; + ecc->read_oob = tango_read_oob; + ecc->write_oob = tango_write_oob; + + return 0; +} + +static const struct nand_controller_ops tango_controller_ops = { + .attach_chip = tango_attach_chip, +}; + static int chip_init(struct device *dev, struct device_node *np) { u32 cs; @@ -566,22 +588,7 @@ static int chip_init(struct device *dev, struct device_node *np) mtd_set_ooblayout(mtd, &tango_nand_ooblayout_ops); mtd->dev.parent = dev; - err = nand_scan_ident(mtd, 1, NULL); - if (err) - return err; - - ecc->mode = NAND_ECC_HW; - ecc->algo = NAND_ECC_BCH; - ecc->bytes = DIV_ROUND_UP(ecc->strength * FIELD_ORDER, BITS_PER_BYTE); - - ecc->read_page_raw = tango_read_page_raw; - ecc->write_page_raw = tango_write_page_raw; - ecc->read_page = tango_read_page; - ecc->write_page = tango_write_page; - ecc->read_oob = tango_read_oob; - ecc->write_oob = tango_write_oob; - - err = nand_scan_tail(mtd); + err = nand_scan(mtd, 1); if (err) return err; @@ -655,6 +662,7 @@ static int tango_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, nfc); nand_controller_init(&nfc->hw); + nfc->hw.ops = &tango_controller_ops; nfc->freq_kHz = clk_get_rate(clk) / 1000; for_each_child_of_node(pdev->dev.of_node, np) { -- cgit v1.2.3 From a001058a9aa291ee85ba5454891aa947658ae694 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:15:15 +0200 Subject: mtd: rawnand: txx9ndfmc: rename nand controller internal structure As already done in the core, calling a struct nand_controller 'hw_control' is misleading. Use the same name as in nand_base.c: 'controller'. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/txx9ndfmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c index 8f5bbbac4612..9019022774f7 100644 --- a/drivers/mtd/nand/raw/txx9ndfmc.c +++ b/drivers/mtd/nand/raw/txx9ndfmc.c @@ -73,7 +73,7 @@ struct txx9ndfmc_drvdata { void __iomem *base; unsigned char hold; /* in gbusclock */ unsigned char spw; /* in gbusclock */ - struct nand_controller hw_control; + struct nand_controller controller; }; static struct platform_device *mtd_to_platdev(struct mtd_info *mtd) @@ -303,7 +303,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n", (gbusclk + 500000) / 1000000, hold, spw); - nand_controller_init(&drvdata->hw_control); + nand_controller_init(&drvdata->controller); platform_set_drvdata(dev, drvdata); txx9ndfmc_initialize(dev); @@ -337,7 +337,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) chip->ecc.bytes = 3; chip->ecc.strength = 1; chip->chip_delay = 100; - chip->controller = &drvdata->hw_control; + chip->controller = &drvdata->controller; nand_set_controller_data(chip, txx9_priv); txx9_priv->dev = dev; -- cgit v1.2.3 From 962c35ef1e68346ae9c9691e14670f0ee96dcdbd Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:15:17 +0200 Subject: mtd: rawnand: vf610: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon Reviewed-by: Stefan Agner --- drivers/mtd/nand/raw/vf610_nfc.c | 127 ++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 61 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c index d5a22fc96878..6f6dcbf9095b 100644 --- a/drivers/mtd/nand/raw/vf610_nfc.c +++ b/drivers/mtd/nand/raw/vf610_nfc.c @@ -747,6 +747,69 @@ static void vf610_nfc_init_controller(struct vf610_nfc *nfc) } } +static int vf610_nfc_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + + vf610_nfc_init_controller(nfc); + + /* Bad block options. */ + if (chip->bbt_options & NAND_BBT_USE_FLASH) + chip->bbt_options |= NAND_BBT_NO_OOB; + + /* Single buffer only, max 256 OOB minus ECC status */ + if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) { + dev_err(nfc->dev, "Unsupported flash page size\n"); + return -ENXIO; + } + + if (chip->ecc.mode != NAND_ECC_HW) + return 0; + + if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) { + dev_err(nfc->dev, "Unsupported flash with hwecc\n"); + return -ENXIO; + } + + if (chip->ecc.size != mtd->writesize) { + dev_err(nfc->dev, "Step size needs to be page size\n"); + return -ENXIO; + } + + /* Only 64 byte ECC layouts known */ + if (mtd->oobsize > 64) + mtd->oobsize = 64; + + /* Use default large page ECC layout defined in NAND core */ + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + if (chip->ecc.strength == 32) { + nfc->ecc_mode = ECC_60_BYTE; + chip->ecc.bytes = 60; + } else if (chip->ecc.strength == 24) { + nfc->ecc_mode = ECC_45_BYTE; + chip->ecc.bytes = 45; + } else { + dev_err(nfc->dev, "Unsupported ECC strength\n"); + return -ENXIO; + } + + chip->ecc.read_page = vf610_nfc_read_page; + chip->ecc.write_page = vf610_nfc_write_page; + chip->ecc.read_page_raw = vf610_nfc_read_page_raw; + chip->ecc.write_page_raw = vf610_nfc_write_page_raw; + chip->ecc.read_oob = vf610_nfc_read_oob; + chip->ecc.write_oob = vf610_nfc_write_oob; + + chip->ecc.size = PAGE_2K; + + return 0; +} + +static const struct nand_controller_ops vf610_nfc_controller_ops = { + .attach_chip = vf610_nfc_attach_chip, +}; + static int vf610_nfc_probe(struct platform_device *pdev) { struct vf610_nfc *nfc; @@ -827,67 +890,9 @@ static int vf610_nfc_probe(struct platform_device *pdev) vf610_nfc_preinit_controller(nfc); - /* first scan to find the device and get the page size */ - err = nand_scan_ident(mtd, 1, NULL); - if (err) - goto err_disable_clk; - - vf610_nfc_init_controller(nfc); - - /* Bad block options. */ - if (chip->bbt_options & NAND_BBT_USE_FLASH) - chip->bbt_options |= NAND_BBT_NO_OOB; - - /* Single buffer only, max 256 OOB minus ECC status */ - if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) { - dev_err(nfc->dev, "Unsupported flash page size\n"); - err = -ENXIO; - goto err_disable_clk; - } - - if (chip->ecc.mode == NAND_ECC_HW) { - if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) { - dev_err(nfc->dev, "Unsupported flash with hwecc\n"); - err = -ENXIO; - goto err_disable_clk; - } - - if (chip->ecc.size != mtd->writesize) { - dev_err(nfc->dev, "Step size needs to be page size\n"); - err = -ENXIO; - goto err_disable_clk; - } - - /* Only 64 byte ECC layouts known */ - if (mtd->oobsize > 64) - mtd->oobsize = 64; - - /* Use default large page ECC layout defined in NAND core */ - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); - if (chip->ecc.strength == 32) { - nfc->ecc_mode = ECC_60_BYTE; - chip->ecc.bytes = 60; - } else if (chip->ecc.strength == 24) { - nfc->ecc_mode = ECC_45_BYTE; - chip->ecc.bytes = 45; - } else { - dev_err(nfc->dev, "Unsupported ECC strength\n"); - err = -ENXIO; - goto err_disable_clk; - } - - chip->ecc.read_page = vf610_nfc_read_page; - chip->ecc.write_page = vf610_nfc_write_page; - chip->ecc.read_page_raw = vf610_nfc_read_page_raw; - chip->ecc.write_page_raw = vf610_nfc_write_page_raw; - chip->ecc.read_oob = vf610_nfc_read_oob; - chip->ecc.write_oob = vf610_nfc_write_oob; - - chip->ecc.size = PAGE_2K; - } - - /* second phase scan */ - err = nand_scan_tail(mtd); + /* Scan the NAND chip */ + chip->dummy_controller.ops = &vf610_nfc_controller_ops; + err = nand_scan(mtd, 1); if (err) goto err_disable_clk; -- cgit v1.2.3 From 92aa292d2e47be6210c0ba6074fc0ddfc02f7856 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sun, 25 Feb 2018 23:09:14 +0100 Subject: mtd: rawnand: sm_common: fix the probe function error path nand_cleanup() should be called upon error after a successful nand_scan_tail(). Rework the error path to follow this rule . Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/sm_common.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/sm_common.c b/drivers/mtd/nand/raw/sm_common.c index 7f5044a79f01..8bd16639ef85 100644 --- a/drivers/mtd/nand/raw/sm_common.c +++ b/drivers/mtd/nand/raw/sm_common.c @@ -192,7 +192,11 @@ int sm_register_device(struct mtd_info *mtd, int smartmedia) if (ret) return ret; - return mtd_device_register(mtd, NULL, 0); + ret = mtd_device_register(mtd, NULL, 0); + if (ret) + nand_cleanup(chip); + + return ret; } EXPORT_SYMBOL_GPL(sm_register_device); -- cgit v1.2.3 From fe13ae02b07a1d14f47b0cbd63bc624b5f3cb890 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 27 Jul 2018 01:18:05 +0200 Subject: mtd: rawnand: sm_common: convert driver to nand_scan_with_ids() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan_with_ids() (alternative to nand_scan() for passing a flash IDs table) instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/sm_common.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/sm_common.c b/drivers/mtd/nand/raw/sm_common.c index 8bd16639ef85..73aafe8c3ef3 100644 --- a/drivers/mtd/nand/raw/sm_common.c +++ b/drivers/mtd/nand/raw/sm_common.c @@ -160,19 +160,9 @@ static struct nand_flash_dev nand_xd_flash_ids[] = { {NULL} }; -int sm_register_device(struct mtd_info *mtd, int smartmedia) +static int sm_attach_chip(struct nand_chip *chip) { - struct nand_chip *chip = mtd_to_nand(mtd); - int ret; - - chip->options |= NAND_SKIP_BBTSCAN; - - /* Scan for card properties */ - ret = nand_scan_ident(mtd, 1, smartmedia ? - nand_smartmedia_flash_ids : nand_xd_flash_ids); - - if (ret) - return ret; + struct mtd_info *mtd = nand_to_mtd(chip); /* Bad block marker position */ chip->badblockpos = 0x05; @@ -187,8 +177,25 @@ int sm_register_device(struct mtd_info *mtd, int smartmedia) else return -ENODEV; - ret = nand_scan_tail(mtd); + return 0; +} + +static const struct nand_controller_ops sm_controller_ops = { + .attach_chip = sm_attach_chip, +}; +int sm_register_device(struct mtd_info *mtd, int smartmedia) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_flash_dev *flash_ids; + int ret; + + chip->options |= NAND_SKIP_BBTSCAN; + + /* Scan for card properties */ + chip->dummy_controller.ops = &sm_controller_ops; + flash_ids = smartmedia ? nand_smartmedia_flash_ids : nand_xd_flash_ids; + ret = nand_scan_with_ids(mtd, 1, flash_ids); if (ret) return ret; -- cgit v1.2.3 From 6a3cec64f18c118c776ee3185f026814e3fe45d4 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 20 Jul 2018 17:15:22 +0200 Subject: mtd: rawnand: qcom: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/qcom_nandc.c | 71 +++++++++++++-------------------------- 1 file changed, 24 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index aa6c3e026ef1..d1d470bb32e4 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2475,10 +2475,10 @@ qcom_nandc_calc_ecc_bytes(int step_size, int strength) NAND_ECC_CAPS_SINGLE(qcom_nandc_ecc_caps, qcom_nandc_calc_ecc_bytes, NANDC_STEP_SIZE, 4, 8); -static int qcom_nand_host_setup(struct qcom_nand_host *host) +static int qcom_nand_attach_chip(struct nand_chip *chip) { - struct nand_chip *chip = &host->chip; struct mtd_info *mtd = nand_to_mtd(chip); + struct qcom_nand_host *host = to_qcom_nand_host(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); int cwperpage, bad_block_byte, ret; @@ -2640,6 +2640,10 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) return 0; } +static const struct nand_controller_ops qcom_nandc_ops = { + .attach_chip = qcom_nand_attach_chip, +}; + static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) { int ret; @@ -2729,6 +2733,7 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) INIT_LIST_HEAD(&nandc->host_list); nand_controller_init(&nandc->controller); + nandc->controller.ops = &qcom_nandc_ops; return 0; } @@ -2781,9 +2786,9 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc) return 0; } -static int qcom_nand_host_init(struct qcom_nand_controller *nandc, - struct qcom_nand_host *host, - struct device_node *dn) +static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc, + struct qcom_nand_host *host, + struct device_node *dn) { struct nand_chip *chip = &host->chip; struct mtd_info *mtd = nand_to_mtd(chip); @@ -2830,30 +2835,13 @@ static int qcom_nand_host_init(struct qcom_nand_controller *nandc, /* set up initial status value */ host->status = NAND_STATUS_READY | NAND_STATUS_WP; - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - return ret; - - ret = qcom_nand_host_setup(host); - - return ret; -} - -static int qcom_nand_mtd_register(struct qcom_nand_controller *nandc, - struct qcom_nand_host *host, - struct device_node *dn) -{ - struct nand_chip *chip = &host->chip; - struct mtd_info *mtd = nand_to_mtd(chip); - int ret; - - ret = nand_scan_tail(mtd); + ret = nand_scan(mtd, 1); if (ret) return ret; ret = mtd_device_register(mtd, NULL, 0); if (ret) - nand_cleanup(mtd_to_nand(mtd)); + nand_cleanup(chip); return ret; } @@ -2862,28 +2850,9 @@ static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc) { struct device *dev = nandc->dev; struct device_node *dn = dev->of_node, *child; - struct qcom_nand_host *host, *tmp; + struct qcom_nand_host *host; int ret; - for_each_available_child_of_node(dn, child) { - host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); - if (!host) { - of_node_put(child); - return -ENOMEM; - } - - ret = qcom_nand_host_init(nandc, host, child); - if (ret) { - devm_kfree(dev, host); - continue; - } - - list_add_tail(&host->node, &nandc->host_list); - } - - if (list_empty(&nandc->host_list)) - return -ENODEV; - if (nandc->props->is_bam) { free_bam_transaction(nandc); nandc->bam_txn = alloc_bam_transaction(nandc); @@ -2894,12 +2863,20 @@ static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc) } } - list_for_each_entry_safe(host, tmp, &nandc->host_list, node) { - ret = qcom_nand_mtd_register(nandc, host, child); + for_each_available_child_of_node(dn, child) { + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) { + of_node_put(child); + return -ENOMEM; + } + + ret = qcom_nand_host_init_and_register(nandc, host, child); if (ret) { - list_del(&host->node); devm_kfree(dev, host); + continue; } + + list_add_tail(&host->node, &nandc->host_list); } if (list_empty(&nandc->host_list)) -- cgit v1.2.3 From 16909c81c6298c2db5587340106523e0a6c2bfe2 Mon Sep 17 00:00:00 2001 From: Anders Roxell Date: Wed, 25 Jul 2018 11:19:28 +0200 Subject: drivers/memory/Kconfig: Add CONFIG_OF dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JZ4780_NEMC doesn't depend on OF, and if OF isn't enabled we get this error: drivers/memory/jz4780-nemc.c: In function ‘jz4780_nemc_num_banks’: drivers/memory/jz4780-nemc.c:72:10: error: implicit declaration of function ‘of_read_number’; did you mean ‘down_read_nested’? [-Werror=implicit-function-declaration] bank = of_read_number(prop, 1); ^~~~~~~~~~~~~~ down_read_nested Make JZ4780_NEMC depend on OF. Fixes: ab99e11062c1 ("memory: jz4780-nemc: Allow selection of this driver when COMPILE_TEST=y") Reported-by: Randy Dunlap Signed-off-by: Anders Roxell Acked-by: Randy Dunlap Signed-off-by: Miquel Raynal --- drivers/memory/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index a642552dfdc9..63389f075f1d 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -123,7 +123,7 @@ config JZ4780_NEMC bool "Ingenic JZ4780 SoC NEMC driver" default y depends on MACH_JZ4780 || COMPILE_TEST - depends on HAS_IOMEM + depends on HAS_IOMEM && OF help This driver is for the NAND/External Memory Controller (NEMC) in the Ingenic JZ4780. This controller is used to handle external -- cgit v1.2.3 From 4918b905736fddc67f14618d5c82f7edc5944db8 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:36 +0200 Subject: mtd: rawnand: brcmnand: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 63 ++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 2e5efa0f9ea2..4b90d5b380c2 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -2208,6 +2208,40 @@ static int brcmnand_setup_dev(struct brcmnand_host *host) return 0; } +static int brcmnand_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct brcmnand_host *host = nand_get_controller_data(chip); + int ret; + + chip->options |= NAND_NO_SUBPAGE_WRITE; + /* + * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA + * to/from, and have nand_base pass us a bounce buffer instead, as + * needed. + */ + chip->options |= NAND_USE_BOUNCE_BUFFER; + + if (chip->bbt_options & NAND_BBT_USE_FLASH) + chip->bbt_options |= NAND_BBT_NO_OOB; + + if (brcmnand_setup_dev(host)) + return -ENXIO; + + chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512; + + /* only use our internal HW threshold */ + mtd->bitflip_threshold = 1; + + ret = brcmstb_choose_ecc_layout(host); + + return ret; +} + +static const struct nand_controller_ops brcmnand_controller_ops = { + .attach_chip = brcmnand_attach_chip, +}; + static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) { struct brcmnand_controller *ctrl = host->ctrl; @@ -2267,33 +2301,7 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) nand_writereg(ctrl, cfg_offs, nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH); - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - return ret; - - chip->options |= NAND_NO_SUBPAGE_WRITE; - /* - * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA - * to/from, and have nand_base pass us a bounce buffer instead, as - * needed. - */ - chip->options |= NAND_USE_BOUNCE_BUFFER; - - if (chip->bbt_options & NAND_BBT_USE_FLASH) - chip->bbt_options |= NAND_BBT_NO_OOB; - - if (brcmnand_setup_dev(host)) - return -ENXIO; - - chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512; - /* only use our internal HW threshold */ - mtd->bitflip_threshold = 1; - - ret = brcmstb_choose_ecc_layout(host); - if (ret) - return ret; - - ret = nand_scan_tail(mtd); + ret = nand_scan(mtd, 1); if (ret) return ret; @@ -2434,6 +2442,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) init_completion(&ctrl->done); init_completion(&ctrl->dma_done); nand_controller_init(&ctrl->controller); + ctrl->controller.ops = &brcmnand_controller_ops; INIT_LIST_HEAD(&ctrl->host_list); /* NAND register range */ -- cgit v1.2.3 From 73a27db8658125faadb857f1919559de8a5f71b0 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:37 +0200 Subject: mtd: rawnand: cafe: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/cafe_nand.c | 135 ++++++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c index ac0be933a490..1dbe43adcfe7 100644 --- a/drivers/mtd/nand/raw/cafe_nand.c +++ b/drivers/mtd/nand/raw/cafe_nand.c @@ -67,6 +67,7 @@ struct cafe_priv { int nr_data; int data_pos; int page_addr; + bool usedma; dma_addr_t dmaaddr; unsigned char *dmabuf; }; @@ -121,7 +122,7 @@ static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) struct nand_chip *chip = mtd_to_nand(mtd); struct cafe_priv *cafe = nand_get_controller_data(chip); - if (usedma) + if (cafe->usedma) memcpy(cafe->dmabuf + cafe->datalen, buf, len); else memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len); @@ -137,7 +138,7 @@ static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) struct nand_chip *chip = mtd_to_nand(mtd); struct cafe_priv *cafe = nand_get_controller_data(chip); - if (usedma) + if (cafe->usedma) memcpy(buf, cafe->dmabuf + cafe->datalen, len); else memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len); @@ -253,7 +254,7 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, /* NB: The datasheet lies -- we really should be subtracting 1 here */ cafe_writel(cafe, cafe->datalen, NAND_DATA_LEN); cafe_writel(cafe, 0x90000000, NAND_IRQ); - if (usedma && (ctl1 & (3<<25))) { + if (cafe->usedma && (ctl1 & (3<<25))) { uint32_t dmactl = 0xc0000000 + cafe->datalen; /* If WR or RD bits set, set up DMA */ if (ctl1 & (1<<26)) { @@ -593,6 +594,76 @@ static int cafe_mul(int x) return gf4096_mul(x, 0xe01); } +static int cafe_nand_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct cafe_priv *cafe = nand_get_controller_data(chip); + int err = 0; + + cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112, + &cafe->dmaaddr, GFP_KERNEL); + if (!cafe->dmabuf) + return -ENOMEM; + + /* Set up DMA address */ + cafe_writel(cafe, lower_32_bits(cafe->dmaaddr), NAND_DMA_ADDR0); + cafe_writel(cafe, upper_32_bits(cafe->dmaaddr), NAND_DMA_ADDR1); + + cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", + cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf); + + /* Restore the DMA flag */ + cafe->usedma = usedma; + + cafe->ctl2 = BIT(27); /* Reed-Solomon ECC */ + if (mtd->writesize == 2048) + cafe->ctl2 |= BIT(29); /* 2KiB page size */ + + /* Set up ECC according to the type of chip we found */ + mtd_set_ooblayout(mtd, &cafe_ooblayout_ops); + if (mtd->writesize == 2048) { + cafe->nand.bbt_td = &cafe_bbt_main_descr_2048; + cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048; + } else if (mtd->writesize == 512) { + cafe->nand.bbt_td = &cafe_bbt_main_descr_512; + cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512; + } else { + dev_warn(&cafe->pdev->dev, + "Unexpected NAND flash writesize %d. Aborting\n", + mtd->writesize); + err = -ENOTSUPP; + goto out_free_dma; + } + + cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; + cafe->nand.ecc.size = mtd->writesize; + cafe->nand.ecc.bytes = 14; + cafe->nand.ecc.strength = 4; + cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel; + cafe->nand.ecc.write_oob = cafe_nand_write_oob; + cafe->nand.ecc.read_page = cafe_nand_read_page; + cafe->nand.ecc.read_oob = cafe_nand_read_oob; + + return 0; + + out_free_dma: + dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); + + return err; +} + +static void cafe_nand_detach_chip(struct nand_chip *chip) +{ + struct cafe_priv *cafe = nand_get_controller_data(chip); + + dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); +} + +static const struct nand_controller_ops cafe_nand_controller_ops = { + .attach_chip = cafe_nand_attach_chip, + .detach_chip = cafe_nand_detach_chip, +}; + static int cafe_nand_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -600,7 +671,6 @@ static int cafe_nand_probe(struct pci_dev *pdev, struct cafe_priv *cafe; uint32_t ctrl; int err = 0; - int old_dma; /* Very old versions shared the same PCI ident for all three functions on the chip. Verify the class too... */ @@ -708,62 +778,15 @@ static int cafe_nand_probe(struct pci_dev *pdev, cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); - /* Do not use the DMA for the nand_scan_ident() */ - old_dma = usedma; - usedma = 0; + /* Do not use the DMA during the NAND identification */ + cafe->usedma = 0; /* Scan to find existence of the device */ - err = nand_scan_ident(mtd, 2, NULL); + cafe->nand.dummy_controller.ops = &cafe_nand_controller_ops; + err = nand_scan(mtd, 2); if (err) goto out_irq; - cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112, - &cafe->dmaaddr, GFP_KERNEL); - if (!cafe->dmabuf) { - err = -ENOMEM; - goto out_irq; - } - - /* Set up DMA address */ - cafe_writel(cafe, lower_32_bits(cafe->dmaaddr), NAND_DMA_ADDR0); - cafe_writel(cafe, upper_32_bits(cafe->dmaaddr), NAND_DMA_ADDR1); - - cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", - cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf); - - /* Restore the DMA flag */ - usedma = old_dma; - - cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */ - if (mtd->writesize == 2048) - cafe->ctl2 |= 1<<29; /* 2KiB page size */ - - /* Set up ECC according to the type of chip we found */ - mtd_set_ooblayout(mtd, &cafe_ooblayout_ops); - if (mtd->writesize == 2048) { - cafe->nand.bbt_td = &cafe_bbt_main_descr_2048; - cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048; - } else if (mtd->writesize == 512) { - cafe->nand.bbt_td = &cafe_bbt_main_descr_512; - cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512; - } else { - pr_warn("Unexpected NAND flash writesize %d. Aborting\n", - mtd->writesize); - goto out_free_dma; - } - cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; - cafe->nand.ecc.size = mtd->writesize; - cafe->nand.ecc.bytes = 14; - cafe->nand.ecc.strength = 4; - cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel; - cafe->nand.ecc.write_oob = cafe_nand_write_oob; - cafe->nand.ecc.read_page = cafe_nand_read_page; - cafe->nand.ecc.read_oob = cafe_nand_read_oob; - - err = nand_scan_tail(mtd); - if (err) - goto out_free_dma; - pci_set_drvdata(pdev, mtd); mtd->name = "cafe_nand"; @@ -775,8 +798,6 @@ static int cafe_nand_probe(struct pci_dev *pdev, out_cleanup_nand: nand_cleanup(&cafe->nand); - out_free_dma: - dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); out_irq: /* Disable NAND IRQ in global IRQ mask register */ cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); -- cgit v1.2.3 From c49f3bee8cb5435ad5832930829f913a391a8a58 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:38 +0200 Subject: mtd: rawnand: lpc32xx_mlc: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/lpc32xx_mlc.c | 61 +++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index 052d123a8304..e82abada130a 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -184,6 +184,7 @@ static struct nand_bbt_descr lpc32xx_nand_bbt_mirror = { }; struct lpc32xx_nand_host { + struct platform_device *pdev; struct nand_chip nand_chip; struct lpc32xx_mlc_platform_data *pdata; struct clk *clk; @@ -653,6 +654,32 @@ static struct lpc32xx_nand_cfg_mlc *lpc32xx_parse_dt(struct device *dev) return ncfg; } +static int lpc32xx_nand_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + struct device *dev = &host->pdev->dev; + + host->dma_buf = devm_kzalloc(dev, mtd->writesize, GFP_KERNEL); + if (!host->dma_buf) + return -ENOMEM; + + host->dummy_buf = devm_kzalloc(dev, mtd->writesize, GFP_KERNEL); + if (!host->dummy_buf) + return -ENOMEM; + + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.size = 512; + mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); + host->mlcsubpages = mtd->writesize / 512; + + return 0; +} + +static const struct nand_controller_ops lpc32xx_nand_controller_ops = { + .attach_chip = lpc32xx_nand_attach_chip, +}; + /* * Probe for NAND controller */ @@ -669,6 +696,8 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) if (!host) return -ENOMEM; + host->pdev = pdev; + rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->io_base = devm_ioremap_resource(&pdev->dev, rc); if (IS_ERR(host->io_base)) @@ -748,31 +777,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) } } - /* - * Scan to find existance of the device and - * Get the type of NAND device SMALL block or LARGE block - */ - res = nand_scan_ident(mtd, 1, NULL); - if (res) - goto release_dma_chan; - - host->dma_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL); - if (!host->dma_buf) { - res = -ENOMEM; - goto release_dma_chan; - } - - host->dummy_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL); - if (!host->dummy_buf) { - res = -ENOMEM; - goto release_dma_chan; - } - - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.size = 512; - mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); - host->mlcsubpages = mtd->writesize / 512; - /* initially clear interrupt status */ readb(MLC_IRQ_SR(host->io_base)); @@ -794,10 +798,11 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) } /* - * Fills out all the uninitialized function pointers with the defaults - * And scans for a bad block table if appropriate. + * Scan to find existence of the device and get the type of NAND device: + * SMALL block or LARGE block. */ - res = nand_scan_tail(mtd); + nand_chip->dummy_controller.ops = &lpc32xx_nand_controller_ops; + res = nand_scan(mtd, 1); if (res) goto free_irq; -- cgit v1.2.3 From e1e6255c311bae4f89bf627fe54ced88bb5caa7e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:39 +0200 Subject: mtd: rawnand: omap2: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/omap2.c | 463 +++++++++++++++++++++---------------------- 1 file changed, 230 insertions(+), 233 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index e943b2e5a5e2..4546ac0bed4a 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -144,12 +144,6 @@ static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc, 0xac, 0x6b, 0xff, 0x99, 0x7b}; static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10}; -/* Shared among all NAND instances to synchronize access to the ECC Engine */ -static struct nand_controller omap_gpmc_controller = { - .lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock), - .wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq), -}; - struct omap_nand_info { struct nand_chip nand; struct platform_device *pdev; @@ -1915,106 +1909,26 @@ static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = { .free = omap_sw_ooblayout_free, }; -static int omap_nand_probe(struct platform_device *pdev) +static int omap_nand_attach_chip(struct nand_chip *chip) { - struct omap_nand_info *info; - struct mtd_info *mtd; - struct nand_chip *nand_chip; - int err; - dma_cap_mask_t mask; - struct resource *res; - struct device *dev = &pdev->dev; - int min_oobbytes = BADBLOCK_MARKER_LENGTH; - int oobbytes_per_step; - - info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), - GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->pdev = pdev; - - err = omap_get_dt_info(dev, info); - if (err) - return err; - - info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs); - if (!info->ops) { - dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n"); - return -ENODEV; - } - - nand_chip = &info->nand; - mtd = nand_to_mtd(nand_chip); - mtd->dev.parent = &pdev->dev; - nand_chip->ecc.priv = NULL; - nand_set_flash_node(nand_chip, dev->of_node); - - if (!mtd->name) { - mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, - "omap2-nand.%d", info->gpmc_cs); - if (!mtd->name) { - dev_err(&pdev->dev, "Failed to set MTD name\n"); - return -ENOMEM; - } - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(nand_chip->IO_ADDR_R)) - return PTR_ERR(nand_chip->IO_ADDR_R); - - info->phys_base = res->start; - - nand_chip->controller = &omap_gpmc_controller; - - nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R; - nand_chip->cmd_ctrl = omap_hwcontrol; - - info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb", - GPIOD_IN); - if (IS_ERR(info->ready_gpiod)) { - dev_err(dev, "failed to get ready gpio\n"); - return PTR_ERR(info->ready_gpiod); - } - - /* - * If RDY/BSY line is connected to OMAP then use the omap ready - * function and the generic nand_wait function which reads the status - * register after monitoring the RDY/BSY line. Otherwise use a standard - * chip delay which is slightly more than tR (AC Timing) of the NAND - * device and read status register until you get a failure or success - */ - if (info->ready_gpiod) { - nand_chip->dev_ready = omap_dev_ready; - nand_chip->chip_delay = 0; - } else { - nand_chip->waitfunc = omap_wait; - nand_chip->chip_delay = 50; - } - - if (info->flash_bbt) - nand_chip->bbt_options |= NAND_BBT_USE_FLASH; - - /* scan NAND device connected to chip controller */ - nand_chip->options |= info->devsize & NAND_BUSWIDTH_16; - err = nand_scan_ident(mtd, 1, NULL); - if (err) { - dev_err(&info->pdev->dev, - "scan failed, may be bus-width mismatch\n"); - goto return_error; - } + struct mtd_info *mtd = nand_to_mtd(chip); + struct omap_nand_info *info = mtd_to_omap(mtd); + struct device *dev = &info->pdev->dev; + int min_oobbytes = BADBLOCK_MARKER_LENGTH; + int oobbytes_per_step; + dma_cap_mask_t mask; + int err; - if (nand_chip->bbt_options & NAND_BBT_USE_FLASH) - nand_chip->bbt_options |= NAND_BBT_NO_OOB; + if (chip->bbt_options & NAND_BBT_USE_FLASH) + chip->bbt_options |= NAND_BBT_NO_OOB; else - nand_chip->options |= NAND_SKIP_BBTSCAN; + chip->options |= NAND_SKIP_BBTSCAN; - /* re-populate low-level callbacks based on xfer modes */ + /* Re-populate low-level callbacks based on xfer modes */ switch (info->xfer_type) { case NAND_OMAP_PREFETCH_POLLED: - nand_chip->read_buf = omap_read_buf_pref; - nand_chip->write_buf = omap_write_buf_pref; + chip->read_buf = omap_read_buf_pref; + chip->write_buf = omap_write_buf_pref; break; case NAND_OMAP_POLLED: @@ -2024,12 +1938,11 @@ static int omap_nand_probe(struct platform_device *pdev) case NAND_OMAP_PREFETCH_DMA: dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - info->dma = dma_request_chan(pdev->dev.parent, "rxtx"); + info->dma = dma_request_chan(dev, "rxtx"); if (IS_ERR(info->dma)) { - dev_err(&pdev->dev, "DMA engine request failed\n"); - err = PTR_ERR(info->dma); - goto return_error; + dev_err(dev, "DMA engine request failed\n"); + return PTR_ERR(info->dma); } else { struct dma_slave_config cfg; @@ -2042,222 +1955,306 @@ static int omap_nand_probe(struct platform_device *pdev) cfg.dst_maxburst = 16; err = dmaengine_slave_config(info->dma, &cfg); if (err) { - dev_err(&pdev->dev, "DMA engine slave config failed: %d\n", + dev_err(dev, + "DMA engine slave config failed: %d\n", err); - goto return_error; + return err; } - nand_chip->read_buf = omap_read_buf_dma_pref; - nand_chip->write_buf = omap_write_buf_dma_pref; + chip->read_buf = omap_read_buf_dma_pref; + chip->write_buf = omap_write_buf_dma_pref; } break; case NAND_OMAP_PREFETCH_IRQ: - info->gpmc_irq_fifo = platform_get_irq(pdev, 0); + info->gpmc_irq_fifo = platform_get_irq(info->pdev, 0); if (info->gpmc_irq_fifo <= 0) { - dev_err(&pdev->dev, "error getting fifo irq\n"); - err = -ENODEV; - goto return_error; + dev_err(dev, "Error getting fifo IRQ\n"); + return -ENODEV; } - err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo, - omap_nand_irq, IRQF_SHARED, - "gpmc-nand-fifo", info); + err = devm_request_irq(dev, info->gpmc_irq_fifo, + omap_nand_irq, IRQF_SHARED, + "gpmc-nand-fifo", info); if (err) { - dev_err(&pdev->dev, "requesting irq(%d) error:%d", - info->gpmc_irq_fifo, err); + dev_err(dev, "Requesting IRQ %d, error %d\n", + info->gpmc_irq_fifo, err); info->gpmc_irq_fifo = 0; - goto return_error; + return err; } - info->gpmc_irq_count = platform_get_irq(pdev, 1); + info->gpmc_irq_count = platform_get_irq(info->pdev, 1); if (info->gpmc_irq_count <= 0) { - dev_err(&pdev->dev, "error getting count irq\n"); - err = -ENODEV; - goto return_error; + dev_err(dev, "Error getting IRQ count\n"); + return -ENODEV; } - err = devm_request_irq(&pdev->dev, info->gpmc_irq_count, - omap_nand_irq, IRQF_SHARED, - "gpmc-nand-count", info); + err = devm_request_irq(dev, info->gpmc_irq_count, + omap_nand_irq, IRQF_SHARED, + "gpmc-nand-count", info); if (err) { - dev_err(&pdev->dev, "requesting irq(%d) error:%d", - info->gpmc_irq_count, err); + dev_err(dev, "Requesting IRQ %d, error %d\n", + info->gpmc_irq_count, err); info->gpmc_irq_count = 0; - goto return_error; + return err; } - nand_chip->read_buf = omap_read_buf_irq_pref; - nand_chip->write_buf = omap_write_buf_irq_pref; + chip->read_buf = omap_read_buf_irq_pref; + chip->write_buf = omap_write_buf_irq_pref; break; default: - dev_err(&pdev->dev, - "xfer_type(%d) not supported!\n", info->xfer_type); - err = -EINVAL; - goto return_error; + dev_err(dev, "xfer_type %d not supported!\n", info->xfer_type); + return -EINVAL; } - if (!omap2_nand_ecc_check(info)) { - err = -EINVAL; - goto return_error; - } + if (!omap2_nand_ecc_check(info)) + return -EINVAL; /* * Bail out earlier to let NAND_ECC_SOFT code create its own * ooblayout instead of using ours. */ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) { - nand_chip->ecc.mode = NAND_ECC_SOFT; - nand_chip->ecc.algo = NAND_ECC_HAMMING; - goto scan_tail; + chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; + return 0; } - /* populate MTD interface based on ECC scheme */ + /* Populate MTD interface based on ECC scheme */ switch (info->ecc_opt) { case OMAP_ECC_HAM1_CODE_HW: - pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n"); - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.bytes = 3; - nand_chip->ecc.size = 512; - nand_chip->ecc.strength = 1; - nand_chip->ecc.calculate = omap_calculate_ecc; - nand_chip->ecc.hwctl = omap_enable_hwecc; - nand_chip->ecc.correct = omap_correct_data; + dev_info(dev, "nand: using OMAP_ECC_HAM1_CODE_HW\n"); + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.bytes = 3; + chip->ecc.size = 512; + chip->ecc.strength = 1; + chip->ecc.calculate = omap_calculate_ecc; + chip->ecc.hwctl = omap_enable_hwecc; + chip->ecc.correct = omap_correct_data; mtd_set_ooblayout(mtd, &omap_ooblayout_ops); - oobbytes_per_step = nand_chip->ecc.bytes; + oobbytes_per_step = chip->ecc.bytes; - if (!(nand_chip->options & NAND_BUSWIDTH_16)) - min_oobbytes = 1; + if (!(chip->options & NAND_BUSWIDTH_16)) + min_oobbytes = 1; break; case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n"); - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.size = 512; - nand_chip->ecc.bytes = 7; - nand_chip->ecc.strength = 4; - nand_chip->ecc.hwctl = omap_enable_hwecc_bch; - nand_chip->ecc.correct = nand_bch_correct_data; - nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw; + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.size = 512; + chip->ecc.bytes = 7; + chip->ecc.strength = 4; + chip->ecc.hwctl = omap_enable_hwecc_bch; + chip->ecc.correct = nand_bch_correct_data; + chip->ecc.calculate = omap_calculate_ecc_bch_sw; mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); /* Reserve one byte for the OMAP marker */ - oobbytes_per_step = nand_chip->ecc.bytes + 1; - /* software bch library is used for locating errors */ - nand_chip->ecc.priv = nand_bch_init(mtd); - if (!nand_chip->ecc.priv) { - dev_err(&info->pdev->dev, "unable to use BCH library\n"); - err = -EINVAL; - goto return_error; + oobbytes_per_step = chip->ecc.bytes + 1; + /* Software BCH library is used for locating errors */ + chip->ecc.priv = nand_bch_init(mtd); + if (!chip->ecc.priv) { + dev_err(dev, "Unable to use BCH library\n"); + return -EINVAL; } break; case OMAP_ECC_BCH4_CODE_HW: pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n"); - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.size = 512; + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.size = 512; /* 14th bit is kept reserved for ROM-code compatibility */ - nand_chip->ecc.bytes = 7 + 1; - nand_chip->ecc.strength = 4; - nand_chip->ecc.hwctl = omap_enable_hwecc_bch; - nand_chip->ecc.correct = omap_elm_correct_data; - nand_chip->ecc.read_page = omap_read_page_bch; - nand_chip->ecc.write_page = omap_write_page_bch; - nand_chip->ecc.write_subpage = omap_write_subpage_bch; + chip->ecc.bytes = 7 + 1; + chip->ecc.strength = 4; + chip->ecc.hwctl = omap_enable_hwecc_bch; + chip->ecc.correct = omap_elm_correct_data; + chip->ecc.read_page = omap_read_page_bch; + chip->ecc.write_page = omap_write_page_bch; + chip->ecc.write_subpage = omap_write_subpage_bch; mtd_set_ooblayout(mtd, &omap_ooblayout_ops); - oobbytes_per_step = nand_chip->ecc.bytes; + oobbytes_per_step = chip->ecc.bytes; err = elm_config(info->elm_dev, BCH4_ECC, - mtd->writesize / nand_chip->ecc.size, - nand_chip->ecc.size, nand_chip->ecc.bytes); + mtd->writesize / chip->ecc.size, + chip->ecc.size, chip->ecc.bytes); if (err < 0) - goto return_error; + return err; break; case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.size = 512; - nand_chip->ecc.bytes = 13; - nand_chip->ecc.strength = 8; - nand_chip->ecc.hwctl = omap_enable_hwecc_bch; - nand_chip->ecc.correct = nand_bch_correct_data; - nand_chip->ecc.calculate = omap_calculate_ecc_bch_sw; + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.size = 512; + chip->ecc.bytes = 13; + chip->ecc.strength = 8; + chip->ecc.hwctl = omap_enable_hwecc_bch; + chip->ecc.correct = nand_bch_correct_data; + chip->ecc.calculate = omap_calculate_ecc_bch_sw; mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); /* Reserve one byte for the OMAP marker */ - oobbytes_per_step = nand_chip->ecc.bytes + 1; - /* software bch library is used for locating errors */ - nand_chip->ecc.priv = nand_bch_init(mtd); - if (!nand_chip->ecc.priv) { - dev_err(&info->pdev->dev, "unable to use BCH library\n"); - err = -EINVAL; - goto return_error; + oobbytes_per_step = chip->ecc.bytes + 1; + /* Software BCH library is used for locating errors */ + chip->ecc.priv = nand_bch_init(mtd); + if (!chip->ecc.priv) { + dev_err(dev, "unable to use BCH library\n"); + return -EINVAL; } break; case OMAP_ECC_BCH8_CODE_HW: pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n"); - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.size = 512; + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.size = 512; /* 14th bit is kept reserved for ROM-code compatibility */ - nand_chip->ecc.bytes = 13 + 1; - nand_chip->ecc.strength = 8; - nand_chip->ecc.hwctl = omap_enable_hwecc_bch; - nand_chip->ecc.correct = omap_elm_correct_data; - nand_chip->ecc.read_page = omap_read_page_bch; - nand_chip->ecc.write_page = omap_write_page_bch; - nand_chip->ecc.write_subpage = omap_write_subpage_bch; + chip->ecc.bytes = 13 + 1; + chip->ecc.strength = 8; + chip->ecc.hwctl = omap_enable_hwecc_bch; + chip->ecc.correct = omap_elm_correct_data; + chip->ecc.read_page = omap_read_page_bch; + chip->ecc.write_page = omap_write_page_bch; + chip->ecc.write_subpage = omap_write_subpage_bch; mtd_set_ooblayout(mtd, &omap_ooblayout_ops); - oobbytes_per_step = nand_chip->ecc.bytes; + oobbytes_per_step = chip->ecc.bytes; err = elm_config(info->elm_dev, BCH8_ECC, - mtd->writesize / nand_chip->ecc.size, - nand_chip->ecc.size, nand_chip->ecc.bytes); + mtd->writesize / chip->ecc.size, + chip->ecc.size, chip->ecc.bytes); if (err < 0) - goto return_error; + return err; break; case OMAP_ECC_BCH16_CODE_HW: - pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n"); - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.size = 512; - nand_chip->ecc.bytes = 26; - nand_chip->ecc.strength = 16; - nand_chip->ecc.hwctl = omap_enable_hwecc_bch; - nand_chip->ecc.correct = omap_elm_correct_data; - nand_chip->ecc.read_page = omap_read_page_bch; - nand_chip->ecc.write_page = omap_write_page_bch; - nand_chip->ecc.write_subpage = omap_write_subpage_bch; + pr_info("Using OMAP_ECC_BCH16_CODE_HW ECC scheme\n"); + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.size = 512; + chip->ecc.bytes = 26; + chip->ecc.strength = 16; + chip->ecc.hwctl = omap_enable_hwecc_bch; + chip->ecc.correct = omap_elm_correct_data; + chip->ecc.read_page = omap_read_page_bch; + chip->ecc.write_page = omap_write_page_bch; + chip->ecc.write_subpage = omap_write_subpage_bch; mtd_set_ooblayout(mtd, &omap_ooblayout_ops); - oobbytes_per_step = nand_chip->ecc.bytes; + oobbytes_per_step = chip->ecc.bytes; err = elm_config(info->elm_dev, BCH16_ECC, - mtd->writesize / nand_chip->ecc.size, - nand_chip->ecc.size, nand_chip->ecc.bytes); + mtd->writesize / chip->ecc.size, + chip->ecc.size, chip->ecc.bytes); if (err < 0) - goto return_error; + return err; break; default: - dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n"); - err = -EINVAL; - goto return_error; + dev_err(dev, "Invalid or unsupported ECC scheme\n"); + return -EINVAL; } - /* check if NAND device's OOB is enough to store ECC signatures */ + /* Check if NAND device's OOB is enough to store ECC signatures */ min_oobbytes += (oobbytes_per_step * - (mtd->writesize / nand_chip->ecc.size)); + (mtd->writesize / chip->ecc.size)); if (mtd->oobsize < min_oobbytes) { - dev_err(&info->pdev->dev, - "not enough OOB bytes required = %d, available=%d\n", + dev_err(dev, + "Not enough OOB bytes: required = %d, available=%d\n", min_oobbytes, mtd->oobsize); - err = -EINVAL; - goto return_error; + return -EINVAL; + } + + return 0; +} + +static const struct nand_controller_ops omap_nand_controller_ops = { + .attach_chip = omap_nand_attach_chip, +}; + +/* Shared among all NAND instances to synchronize access to the ECC Engine */ +static struct nand_controller omap_gpmc_controller = { + .lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock), + .wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq), + .ops = &omap_nand_controller_ops, +}; + +static int omap_nand_probe(struct platform_device *pdev) +{ + struct omap_nand_info *info; + struct mtd_info *mtd; + struct nand_chip *nand_chip; + int err; + struct resource *res; + struct device *dev = &pdev->dev; + + info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pdev = pdev; + + err = omap_get_dt_info(dev, info); + if (err) + return err; + + info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs); + if (!info->ops) { + dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n"); + return -ENODEV; + } + + nand_chip = &info->nand; + mtd = nand_to_mtd(nand_chip); + mtd->dev.parent = &pdev->dev; + nand_chip->ecc.priv = NULL; + nand_set_flash_node(nand_chip, dev->of_node); + + if (!mtd->name) { + mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "omap2-nand.%d", info->gpmc_cs); + if (!mtd->name) { + dev_err(&pdev->dev, "Failed to set MTD name\n"); + return -ENOMEM; + } + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(nand_chip->IO_ADDR_R)) + return PTR_ERR(nand_chip->IO_ADDR_R); + + info->phys_base = res->start; + + nand_chip->controller = &omap_gpmc_controller; + + nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R; + nand_chip->cmd_ctrl = omap_hwcontrol; + + info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb", + GPIOD_IN); + if (IS_ERR(info->ready_gpiod)) { + dev_err(dev, "failed to get ready gpio\n"); + return PTR_ERR(info->ready_gpiod); + } + + /* + * If RDY/BSY line is connected to OMAP then use the omap ready + * function and the generic nand_wait function which reads the status + * register after monitoring the RDY/BSY line. Otherwise use a standard + * chip delay which is slightly more than tR (AC Timing) of the NAND + * device and read status register until you get a failure or success + */ + if (info->ready_gpiod) { + nand_chip->dev_ready = omap_dev_ready; + nand_chip->chip_delay = 0; + } else { + nand_chip->waitfunc = omap_wait; + nand_chip->chip_delay = 50; } -scan_tail: - /* second phase scan */ - err = nand_scan_tail(mtd); + if (info->flash_bbt) + nand_chip->bbt_options |= NAND_BBT_USE_FLASH; + + /* scan NAND device connected to chip controller */ + nand_chip->options |= info->devsize & NAND_BUSWIDTH_16; + + err = nand_scan(mtd, 1); if (err) goto return_error; -- cgit v1.2.3 From 7928225ffcb3d01c424983af30aba3c16ab5a585 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:40 +0200 Subject: mtd: rawnand: atmel: clarify NAND addition/removal paths No need for an atmel_nand_register() function, let's move the code in it directly where the function was called: in atmel_nand_controller_add_nand(). To make things consistent, also rename atmel_nand_unregister() into atmel_nand_controller_remove_nand(). Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/atmel/nand-controller.c | 102 ++++++++++++--------------- 1 file changed, 45 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index 4d27401f1299..143d029710f0 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -1573,7 +1573,7 @@ static int atmel_nand_detect(struct atmel_nand *nand) return ret; } -static int atmel_nand_unregister(struct atmel_nand *nand) +static int atmel_nand_controller_remove_nand(struct atmel_nand *nand) { struct nand_chip *chip = &nand->base; struct mtd_info *mtd = nand_to_mtd(chip); @@ -1589,60 +1589,6 @@ static int atmel_nand_unregister(struct atmel_nand *nand) return 0; } -static int atmel_nand_register(struct atmel_nand *nand) -{ - struct nand_chip *chip = &nand->base; - struct mtd_info *mtd = nand_to_mtd(chip); - struct atmel_nand_controller *nc; - int ret; - - nc = to_nand_controller(chip->controller); - - if (nc->caps->legacy_of_bindings || !nc->dev->of_node) { - /* - * We keep the MTD name unchanged to avoid breaking platforms - * where the MTD cmdline parser is used and the bootloader - * has not been updated to use the new naming scheme. - */ - mtd->name = "atmel_nand"; - } else if (!mtd->name) { - /* - * If the new bindings are used and the bootloader has not been - * updated to pass a new mtdparts parameter on the cmdline, you - * should define the following property in your nand node: - * - * label = "atmel_nand"; - * - * This way, mtd->name will be set by the core when - * nand_set_flash_node() is called. - */ - mtd->name = devm_kasprintf(nc->dev, GFP_KERNEL, - "%s:nand.%d", dev_name(nc->dev), - nand->cs[0].id); - if (!mtd->name) { - dev_err(nc->dev, "Failed to allocate mtd->name\n"); - return -ENOMEM; - } - } - - ret = nand_scan_tail(mtd); - if (ret) { - dev_err(nc->dev, "nand_scan_tail() failed: %d\n", ret); - return ret; - } - - ret = mtd_device_register(mtd, NULL, 0); - if (ret) { - dev_err(nc->dev, "Failed to register mtd device: %d\n", ret); - nand_cleanup(chip); - return ret; - } - - list_add_tail(&nand->node, &nc->chips); - - return 0; -} - static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc, struct device_node *np, int reg_cells) @@ -1772,7 +1718,49 @@ atmel_nand_controller_add_nand(struct atmel_nand_controller *nc, if (ret) return ret; - return atmel_nand_register(nand); + if (nc->caps->legacy_of_bindings || !nc->dev->of_node) { + /* + * We keep the MTD name unchanged to avoid breaking platforms + * where the MTD cmdline parser is used and the bootloader + * has not been updated to use the new naming scheme. + */ + mtd->name = "atmel_nand"; + } else if (!mtd->name) { + /* + * If the new bindings are used and the bootloader has not been + * updated to pass a new mtdparts parameter on the cmdline, you + * should define the following property in your nand node: + * + * label = "atmel_nand"; + * + * This way, mtd->name will be set by the core when + * nand_set_flash_node() is called. + */ + mtd->name = devm_kasprintf(nc->dev, GFP_KERNEL, + "%s:nand.%d", dev_name(nc->dev), + nand->cs[0].id); + if (!mtd->name) { + dev_err(nc->dev, "Failed to allocate mtd->name\n"); + return -ENOMEM; + } + } + + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(nc->dev, "nand_scan_tail() failed: %d\n", ret); + return ret; + } + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + dev_err(nc->dev, "Failed to register mtd device: %d\n", ret); + nand_cleanup(chip); + return ret; + } + + list_add_tail(&nand->node, &nc->chips); + + return 0; } static int @@ -1782,7 +1770,7 @@ atmel_nand_controller_remove_nands(struct atmel_nand_controller *nc) int ret; list_for_each_entry_safe(nand, tmp, &nc->chips, node) { - ret = atmel_nand_unregister(nand); + ret = atmel_nand_controller_remove_nand(nand); if (ret) return ret; } -- cgit v1.2.3 From 577e010c24bceb80dbbc146b2a4bd7ff0527fa9b Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:41 +0200 Subject: mtd: rawnand: atmel: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/atmel/nand-controller.c | 113 +++++++++++++-------------- 1 file changed, 54 insertions(+), 59 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index 143d029710f0..a068b214ebaa 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -201,7 +201,7 @@ struct atmel_nand_controller_ops { int (*remove)(struct atmel_nand_controller *nc); void (*nand_init)(struct atmel_nand_controller *nc, struct atmel_nand *nand); - int (*ecc_init)(struct atmel_nand *nand); + int (*ecc_init)(struct nand_chip *chip); int (*setup_data_interface)(struct atmel_nand *nand, int csline, const struct nand_data_interface *conf); }; @@ -1132,9 +1132,8 @@ static int atmel_nand_pmecc_init(struct nand_chip *chip) return 0; } -static int atmel_nand_ecc_init(struct atmel_nand *nand) +static int atmel_nand_ecc_init(struct nand_chip *chip) { - struct nand_chip *chip = &nand->base; struct atmel_nand_controller *nc; int ret; @@ -1169,12 +1168,11 @@ static int atmel_nand_ecc_init(struct atmel_nand *nand) return 0; } -static int atmel_hsmc_nand_ecc_init(struct atmel_nand *nand) +static int atmel_hsmc_nand_ecc_init(struct nand_chip *chip) { - struct nand_chip *chip = &nand->base; int ret; - ret = atmel_nand_ecc_init(nand); + ret = atmel_nand_ecc_init(chip); if (ret) return ret; @@ -1557,22 +1555,6 @@ static void atmel_hsmc_nand_init(struct atmel_nand_controller *nc, chip->select_chip = atmel_hsmc_nand_select_chip; } -static int atmel_nand_detect(struct atmel_nand *nand) -{ - struct nand_chip *chip = &nand->base; - struct mtd_info *mtd = nand_to_mtd(chip); - struct atmel_nand_controller *nc; - int ret; - - nc = to_nand_controller(chip->controller); - - ret = nand_scan_ident(mtd, nand->numcs, NULL); - if (ret) - dev_err(nc->dev, "nand_scan_ident() failed: %d\n", ret); - - return ret; -} - static int atmel_nand_controller_remove_nand(struct atmel_nand *nand) { struct nand_chip *chip = &nand->base; @@ -1700,6 +1682,8 @@ static int atmel_nand_controller_add_nand(struct atmel_nand_controller *nc, struct atmel_nand *nand) { + struct nand_chip *chip = &nand->base; + struct mtd_info *mtd = nand_to_mtd(chip); int ret; /* No card inserted, skip this NAND. */ @@ -1710,44 +1694,9 @@ atmel_nand_controller_add_nand(struct atmel_nand_controller *nc, nc->caps->ops->nand_init(nc, nand); - ret = atmel_nand_detect(nand); - if (ret) - return ret; - - ret = nc->caps->ops->ecc_init(nand); - if (ret) - return ret; - - if (nc->caps->legacy_of_bindings || !nc->dev->of_node) { - /* - * We keep the MTD name unchanged to avoid breaking platforms - * where the MTD cmdline parser is used and the bootloader - * has not been updated to use the new naming scheme. - */ - mtd->name = "atmel_nand"; - } else if (!mtd->name) { - /* - * If the new bindings are used and the bootloader has not been - * updated to pass a new mtdparts parameter on the cmdline, you - * should define the following property in your nand node: - * - * label = "atmel_nand"; - * - * This way, mtd->name will be set by the core when - * nand_set_flash_node() is called. - */ - mtd->name = devm_kasprintf(nc->dev, GFP_KERNEL, - "%s:nand.%d", dev_name(nc->dev), - nand->cs[0].id); - if (!mtd->name) { - dev_err(nc->dev, "Failed to allocate mtd->name\n"); - return -ENOMEM; - } - } - - ret = nand_scan_tail(mtd); + ret = nand_scan(mtd, nand->numcs); if (ret) { - dev_err(nc->dev, "nand_scan_tail() failed: %d\n", ret); + dev_err(nc->dev, "NAND scan failed: %d\n", ret); return ret; } @@ -1945,6 +1894,51 @@ static const struct of_device_id atmel_matrix_of_ids[] = { { /* sentinel */ }, }; +static int atmel_nand_attach_chip(struct nand_chip *chip) +{ + struct atmel_nand_controller *nc = to_nand_controller(chip->controller); + struct atmel_nand *nand = to_atmel_nand(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + ret = nc->caps->ops->ecc_init(chip); + if (ret) + return ret; + + if (nc->caps->legacy_of_bindings || !nc->dev->of_node) { + /* + * We keep the MTD name unchanged to avoid breaking platforms + * where the MTD cmdline parser is used and the bootloader + * has not been updated to use the new naming scheme. + */ + mtd->name = "atmel_nand"; + } else if (!mtd->name) { + /* + * If the new bindings are used and the bootloader has not been + * updated to pass a new mtdparts parameter on the cmdline, you + * should define the following property in your nand node: + * + * label = "atmel_nand"; + * + * This way, mtd->name will be set by the core when + * nand_set_flash_node() is called. + */ + mtd->name = devm_kasprintf(nc->dev, GFP_KERNEL, + "%s:nand.%d", dev_name(nc->dev), + nand->cs[0].id); + if (!mtd->name) { + dev_err(nc->dev, "Failed to allocate mtd->name\n"); + return -ENOMEM; + } + } + + return 0; +} + +static const struct nand_controller_ops atmel_nand_controller_ops = { + .attach_chip = atmel_nand_attach_chip, +}; + static int atmel_nand_controller_init(struct atmel_nand_controller *nc, struct platform_device *pdev, const struct atmel_nand_controller_caps *caps) @@ -1954,6 +1948,7 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc, int ret; nand_controller_init(&nc->base); + nc->base.ops = &atmel_nand_controller_ops; INIT_LIST_HEAD(&nc->chips); nc->dev = dev; nc->caps = caps; -- cgit v1.2.3 From 49aa76b166765188af6682cd22985cbda1bb39c2 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:42 +0200 Subject: mtd: rawnand: do not execute nand_scan_ident() if maxchips is zero Some driver (eg. docg4) will need to handle themselves the identification phase. As part of the migration to use nand_scan() everywhere (which will unconditionnaly call nand_scan_ident()), we add a condition at the start of nand_scan_with_ids() to jump over nand_scan_ident() if the maxchips parameters is zero, meaning that the driver does not want the core to handle this phase. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/nand_base.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index dea41fa25be1..42a7a934a17b 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -6735,7 +6735,9 @@ static void nand_detach(struct nand_chip *chip) /** * nand_scan_with_ids - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure - * @maxchips: number of chips to scan for + * @maxchips: number of chips to scan for. @nand_scan_ident() will not be run if + * this parameter is zero (useful for specific drivers that must + * handle this part of the process themselves, e.g docg4). * @ids: optional flash IDs table * * This fills out all the uninitialized function pointers with the defaults. @@ -6748,9 +6750,11 @@ int nand_scan_with_ids(struct mtd_info *mtd, int maxchips, struct nand_chip *chip = mtd_to_nand(mtd); int ret; - ret = nand_scan_ident(mtd, maxchips, ids); - if (ret) - return ret; + if (maxchips) { + ret = nand_scan_ident(mtd, maxchips, ids); + if (ret) + return ret; + } ret = nand_attach(chip); if (ret) -- cgit v1.2.3 From 66a38478dcc5b5a35e49055f2eb301f8ac594e3f Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:43 +0200 Subject: mtd: rawnand: docg4: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/docg4.c | 71 ++++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/docg4.c b/drivers/mtd/nand/raw/docg4.c index 4dccdfba6140..a3f04315c05c 100644 --- a/drivers/mtd/nand/raw/docg4.c +++ b/drivers/mtd/nand/raw/docg4.c @@ -1227,10 +1227,9 @@ static void __init init_mtd_structs(struct mtd_info *mtd) * required within a nand driver because they are performed by the nand * infrastructure code as part of nand_scan(). In this case they need * to be initialized here because we skip call to nand_scan_ident() (the - * first half of nand_scan()). The call to nand_scan_ident() is skipped - * because for this device the chip id is not read in the manner of a - * standard nand device. Unfortunately, nand_scan_ident() does other - * things as well, such as call nand_set_defaults(). + * first half of nand_scan()). The call to nand_scan_ident() could be + * skipped because for this device the chip id is not read in the manner + * of a standard nand device. */ struct nand_chip *nand = mtd_to_nand(mtd); @@ -1315,6 +1314,40 @@ static int __init read_id_reg(struct mtd_info *mtd) static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL }; +static int docg4_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct docg4_priv *doc = (struct docg4_priv *)(chip + 1); + int ret; + + init_mtd_structs(mtd); + + /* Initialize kernel BCH algorithm */ + doc->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY); + if (!doc->bch) + return -EINVAL; + + reset(mtd); + + ret = read_id_reg(mtd); + if (ret) + free_bch(doc->bch); + + return ret; +} + +static void docg4_detach_chip(struct nand_chip *chip) +{ + struct docg4_priv *doc = (struct docg4_priv *)(chip + 1); + + free_bch(doc->bch); +} + +static const struct nand_controller_ops docg4_controller_ops = { + .attach_chip = docg4_attach_chip, + .detach_chip = docg4_detach_chip, +}; + static int __init probe_docg4(struct platform_device *pdev) { struct mtd_info *mtd; @@ -1350,28 +1383,17 @@ static int __init probe_docg4(struct platform_device *pdev) mtd->dev.parent = &pdev->dev; doc->virtadr = virtadr; doc->dev = dev; - - init_mtd_structs(mtd); - - /* initialize kernel bch algorithm */ - doc->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY); - if (doc->bch == NULL) { - retval = -EINVAL; - goto free_nand; - } - platform_set_drvdata(pdev, doc); - reset(mtd); - retval = read_id_reg(mtd); - if (retval == -ENODEV) { - dev_warn(dev, "No diskonchip G4 device found.\n"); - goto free_bch; - } - - retval = nand_scan_tail(mtd); + /* + * Running nand_scan() with maxchips == 0 will skip nand_scan_ident(), + * which is a specific operation with this driver and done in the + * ->attach_chip callback. + */ + nand->dummy_controller.ops = &docg4_controller_ops; + retval = nand_scan(mtd, 0); if (retval) - goto free_bch; + goto free_nand; retval = read_factory_bbt(mtd); if (retval) @@ -1387,8 +1409,6 @@ static int __init probe_docg4(struct platform_device *pdev) cleanup_nand: nand_cleanup(nand); -free_bch: - free_bch(doc->bch); free_nand: kfree(nand); unmap: @@ -1401,7 +1421,6 @@ static int __exit cleanup_docg4(struct platform_device *pdev) { struct docg4_priv *doc = platform_get_drvdata(pdev); nand_release(doc->mtd); - free_bch(doc->bch); kfree(mtd_to_nand(doc->mtd)); iounmap(doc->virtadr); return 0; -- cgit v1.2.3 From f57bbfb9e3f47b6d4eecb1d8037dab1cda9996a4 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:44 +0200 Subject: mtd: rawnand: jz4740: fix probe function error path An error after nand_scan_tail() should trigger a nand_cleanup(), not a nand_release() as mtd_device_register() (or one of its variants) has not been called and there is no need to deregister any MTD device yet. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/jz4740_nand.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/jz4740_nand.c b/drivers/mtd/nand/raw/jz4740_nand.c index a4052b03249c..3abf87420c10 100644 --- a/drivers/mtd/nand/raw/jz4740_nand.c +++ b/drivers/mtd/nand/raw/jz4740_nand.c @@ -472,15 +472,15 @@ static int jz_nand_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Failed to add mtd device\n"); - goto err_nand_release; + goto err_cleanup_nand; } dev_info(&pdev->dev, "Successfully registered JZ4740 NAND driver\n"); return 0; -err_nand_release: - nand_release(mtd); +err_cleanup_nand: + nand_cleanup(chip); err_unclaim_banks: while (chipnr--) { unsigned char bank = nand->banks[chipnr]; -- cgit v1.2.3 From 2f2173449b0350a206faf0d1aa71595602a40c98 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:45 +0200 Subject: mtd: rawnand: jz4740: group nand_scan_{ident, tail} calls Prepare the migration to nand_scan() by moving both calls to nand_scan_ident() and nand_scan_tail() in a single spot. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/jz4740_nand.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/jz4740_nand.c b/drivers/mtd/nand/raw/jz4740_nand.c index 3abf87420c10..ccd8f897db7c 100644 --- a/drivers/mtd/nand/raw/jz4740_nand.c +++ b/drivers/mtd/nand/raw/jz4740_nand.c @@ -309,6 +309,7 @@ static int jz_nand_detect_bank(struct platform_device *pdev, size_t chipnr, uint8_t *nand_maf_id, uint8_t *nand_dev_id) { + struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); int ret; char res_name[6]; uint32_t ctrl; @@ -335,6 +336,16 @@ static int jz_nand_detect_bank(struct platform_device *pdev, if (ret) goto notfound_id; + if (pdata && pdata->ident_callback) + pdata->ident_callback(pdev, mtd, &pdata->partitions, + &pdata->num_partitions); + + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(&pdev->dev, "Failed to scan NAND\n"); + goto notfound_id; + } + /* Retrieve the IDs from the first chip. */ chip->select_chip(mtd, 0); nand_reset_op(chip); @@ -456,17 +467,6 @@ static int jz_nand_probe(struct platform_device *pdev) goto err_iounmap_mmio; } - if (pdata && pdata->ident_callback) { - pdata->ident_callback(pdev, mtd, &pdata->partitions, - &pdata->num_partitions); - } - - ret = nand_scan_tail(mtd); - if (ret) { - dev_err(&pdev->dev, "Failed to scan NAND\n"); - goto err_unclaim_banks; - } - ret = mtd_device_register(mtd, pdata ? pdata->partitions : NULL, pdata ? pdata->num_partitions : 0); @@ -481,7 +481,6 @@ static int jz_nand_probe(struct platform_device *pdev) err_cleanup_nand: nand_cleanup(chip); -err_unclaim_banks: while (chipnr--) { unsigned char bank = nand->banks[chipnr]; jz_nand_iounmap_resource(nand->bank_mem[bank - 1], -- cgit v1.2.3 From ccadc14975c9bbd17da3a4b0b443325a5bda0ba2 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:46 +0200 Subject: mtd: rawnand: jz4740: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/jz4740_nand.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/jz4740_nand.c b/drivers/mtd/nand/raw/jz4740_nand.c index ccd8f897db7c..a7515452bc59 100644 --- a/drivers/mtd/nand/raw/jz4740_nand.c +++ b/drivers/mtd/nand/raw/jz4740_nand.c @@ -309,7 +309,6 @@ static int jz_nand_detect_bank(struct platform_device *pdev, size_t chipnr, uint8_t *nand_maf_id, uint8_t *nand_dev_id) { - struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); int ret; char res_name[6]; uint32_t ctrl; @@ -332,20 +331,10 @@ static int jz_nand_detect_bank(struct platform_device *pdev, if (chipnr == 0) { /* Detect first chip. */ - ret = nand_scan_ident(mtd, 1, NULL); + ret = nand_scan(mtd, 1); if (ret) goto notfound_id; - if (pdata && pdata->ident_callback) - pdata->ident_callback(pdev, mtd, &pdata->partitions, - &pdata->num_partitions); - - ret = nand_scan_tail(mtd); - if (ret) { - dev_err(&pdev->dev, "Failed to scan NAND\n"); - goto notfound_id; - } - /* Retrieve the IDs from the first chip. */ chip->select_chip(mtd, 0); nand_reset_op(chip); @@ -379,6 +368,24 @@ notfound_id: return ret; } +static int jz_nand_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct device *dev = mtd->dev.parent; + struct jz_nand_platform_data *pdata = dev_get_platdata(dev); + struct platform_device *pdev = to_platform_device(dev); + + if (pdata && pdata->ident_callback) + pdata->ident_callback(pdev, mtd, &pdata->partitions, + &pdata->num_partitions); + + return 0; +} + +static const struct nand_controller_ops jz_nand_controller_ops = { + .attach_chip = jz_nand_attach_chip, +}; + static int jz_nand_probe(struct platform_device *pdev) { int ret; @@ -422,6 +429,7 @@ static int jz_nand_probe(struct platform_device *pdev) chip->chip_delay = 50; chip->cmd_ctrl = jz_nand_cmd_ctrl; chip->select_chip = jz_nand_select_chip; + chip->dummy_controller.ops = &jz_nand_controller_ops; if (nand->busy_gpio) chip->dev_ready = jz_nand_dev_ready; -- cgit v1.2.3 From 176fc2f28ee5ebd494c39d25dda3e0a71e0b649a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:47 +0200 Subject: mtd: rawnand: tegra: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/tegra_nand.c | 162 +++++++++++++++++++++----------------- 1 file changed, 88 insertions(+), 74 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c index 31c0d9ca9d23..79da1efc88d1 100644 --- a/drivers/mtd/nand/raw/tegra_nand.c +++ b/drivers/mtd/nand/raw/tegra_nand.c @@ -906,74 +906,13 @@ static int tegra_nand_select_strength(struct nand_chip *chip, int oobsize) bits_per_step, oobsize); } -static int tegra_nand_chips_init(struct device *dev, - struct tegra_nand_controller *ctrl) +static int tegra_nand_attach_chip(struct nand_chip *chip) { - struct device_node *np = dev->of_node; - struct device_node *np_nand; - int nsels, nchips = of_get_child_count(np); - struct tegra_nand_chip *nand; - struct mtd_info *mtd; - struct nand_chip *chip; + struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); + struct tegra_nand_chip *nand = to_tegra_chip(chip); + struct mtd_info *mtd = nand_to_mtd(chip); int bits_per_step; int ret; - u32 cs; - - if (nchips != 1) { - dev_err(dev, "Currently only one NAND chip supported\n"); - return -EINVAL; - } - - np_nand = of_get_next_child(np, NULL); - - nsels = of_property_count_elems_of_size(np_nand, "reg", sizeof(u32)); - if (nsels != 1) { - dev_err(dev, "Missing/invalid reg property\n"); - return -EINVAL; - } - - /* Retrieve CS id, currently only single die NAND supported */ - ret = of_property_read_u32(np_nand, "reg", &cs); - if (ret) { - dev_err(dev, "could not retrieve reg property: %d\n", ret); - return ret; - } - - nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL); - if (!nand) - return -ENOMEM; - - nand->cs[0] = cs; - - nand->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_LOW); - - if (IS_ERR(nand->wp_gpio)) { - ret = PTR_ERR(nand->wp_gpio); - dev_err(dev, "Failed to request WP GPIO: %d\n", ret); - return ret; - } - - chip = &nand->chip; - chip->controller = &ctrl->controller; - - mtd = nand_to_mtd(chip); - - mtd->dev.parent = dev; - mtd->owner = THIS_MODULE; - - nand_set_flash_node(chip, np_nand); - - if (!mtd->name) - mtd->name = "tegra_nand"; - - chip->options = NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER; - chip->exec_op = tegra_nand_exec_op; - chip->select_chip = tegra_nand_select_chip; - chip->setup_data_interface = tegra_nand_setup_data_interface; - - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - return ret; if (chip->bbt_options & NAND_BBT_USE_FLASH) chip->bbt_options |= NAND_BBT_NO_OOB; @@ -982,7 +921,8 @@ static int tegra_nand_chips_init(struct device *dev, chip->ecc.size = 512; chip->ecc.steps = mtd->writesize / chip->ecc.size; if (chip->ecc_step_ds != 512) { - dev_err(dev, "Unsupported step size %d\n", chip->ecc_step_ds); + dev_err(ctrl->dev, "Unsupported step size %d\n", + chip->ecc_step_ds); return -EINVAL; } @@ -1004,14 +944,15 @@ static int tegra_nand_chips_init(struct device *dev, } if (chip->ecc.algo == NAND_ECC_BCH && mtd->writesize < 2048) { - dev_err(dev, "BCH supports 2K or 4K page size only\n"); + dev_err(ctrl->dev, "BCH supports 2K or 4K page size only\n"); return -EINVAL; } if (!chip->ecc.strength) { ret = tegra_nand_select_strength(chip, mtd->oobsize); if (ret < 0) { - dev_err(dev, "No valid strength found, minimum %d\n", + dev_err(ctrl->dev, + "No valid strength found, minimum %d\n", chip->ecc_strength_ds); return ret; } @@ -1039,7 +980,7 @@ static int tegra_nand_chips_init(struct device *dev, nand->config_ecc |= CONFIG_TVAL_8; break; default: - dev_err(dev, "ECC strength %d not supported\n", + dev_err(ctrl->dev, "ECC strength %d not supported\n", chip->ecc.strength); return -EINVAL; } @@ -1062,17 +1003,17 @@ static int tegra_nand_chips_init(struct device *dev, nand->bch_config |= BCH_TVAL_16; break; default: - dev_err(dev, "ECC strength %d not supported\n", + dev_err(ctrl->dev, "ECC strength %d not supported\n", chip->ecc.strength); return -EINVAL; } break; default: - dev_err(dev, "ECC algorithm not supported\n"); + dev_err(ctrl->dev, "ECC algorithm not supported\n"); return -EINVAL; } - dev_info(dev, "Using %s with strength %d per 512 byte step\n", + dev_info(ctrl->dev, "Using %s with strength %d per 512 byte step\n", chip->ecc.algo == NAND_ECC_BCH ? "BCH" : "RS", chip->ecc.strength); @@ -1095,7 +1036,8 @@ static int tegra_nand_chips_init(struct device *dev, nand->config |= CONFIG_PS_4096; break; default: - dev_err(dev, "Unsupported writesize %d\n", mtd->writesize); + dev_err(ctrl->dev, "Unsupported writesize %d\n", + mtd->writesize); return -ENODEV; } @@ -1106,7 +1048,78 @@ static int tegra_nand_chips_init(struct device *dev, nand->config |= CONFIG_TAG_BYTE_SIZE(mtd->oobsize - 1); writel_relaxed(nand->config, ctrl->regs + CONFIG); - ret = nand_scan_tail(mtd); + return 0; +} + +static const struct nand_controller_ops tegra_nand_controller_ops = { + .attach_chip = &tegra_nand_attach_chip, +}; + +static int tegra_nand_chips_init(struct device *dev, + struct tegra_nand_controller *ctrl) +{ + struct device_node *np = dev->of_node; + struct device_node *np_nand; + int nsels, nchips = of_get_child_count(np); + struct tegra_nand_chip *nand; + struct mtd_info *mtd; + struct nand_chip *chip; + int ret; + u32 cs; + + if (nchips != 1) { + dev_err(dev, "Currently only one NAND chip supported\n"); + return -EINVAL; + } + + np_nand = of_get_next_child(np, NULL); + + nsels = of_property_count_elems_of_size(np_nand, "reg", sizeof(u32)); + if (nsels != 1) { + dev_err(dev, "Missing/invalid reg property\n"); + return -EINVAL; + } + + /* Retrieve CS id, currently only single die NAND supported */ + ret = of_property_read_u32(np_nand, "reg", &cs); + if (ret) { + dev_err(dev, "could not retrieve reg property: %d\n", ret); + return ret; + } + + nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL); + if (!nand) + return -ENOMEM; + + nand->cs[0] = cs; + + nand->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_LOW); + + if (IS_ERR(nand->wp_gpio)) { + ret = PTR_ERR(nand->wp_gpio); + dev_err(dev, "Failed to request WP GPIO: %d\n", ret); + return ret; + } + + chip = &nand->chip; + chip->controller = &ctrl->controller; + + mtd = nand_to_mtd(chip); + + mtd->dev.parent = dev; + mtd->owner = THIS_MODULE; + + nand_set_flash_node(chip, np_nand); + + if (!mtd->name) + mtd->name = "tegra_nand"; + + chip->options = NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER; + chip->exec_op = tegra_nand_exec_op; + chip->select_chip = tegra_nand_select_chip; + chip->setup_data_interface = tegra_nand_setup_data_interface; + + ret = nand_scan(mtd, 1); if (ret) return ret; @@ -1137,6 +1150,7 @@ static int tegra_nand_probe(struct platform_device *pdev) ctrl->dev = &pdev->dev; nand_controller_init(&ctrl->controller); + ctrl->controller.ops = &tegra_nand_controller_ops; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ctrl->regs = devm_ioremap_resource(&pdev->dev, res); -- cgit v1.2.3 From abe23d1c5cdf888639313bf87880d2692d0b8724 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:48 +0200 Subject: mtd: rawnand: txx9ndfmc: clarify ECC parameters assignation A comment in the probe declares that values are assigned to ecc.size and ecc.bytes, but these values will be overwritten. This is not entirely right as they are overwritten only if mtd->writesize >= 512. Let's clarify this by moving these assignations to txx9ndfmc_nand_scan(). Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/txx9ndfmc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c index 9019022774f7..9808b18b15e2 100644 --- a/drivers/mtd/nand/raw/txx9ndfmc.c +++ b/drivers/mtd/nand/raw/txx9ndfmc.c @@ -262,10 +262,13 @@ static int txx9ndfmc_nand_scan(struct mtd_info *mtd) ret = nand_scan_ident(mtd, 1, NULL); if (!ret) { if (mtd->writesize >= 512) { - /* Hardware ECC 6 byte ECC per 512 Byte data */ chip->ecc.size = 512; chip->ecc.bytes = 6; + } else { + chip->ecc.size = 256; + chip->ecc.bytes = 3; } + ret = nand_scan_tail(mtd); } return ret; @@ -332,9 +335,6 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) chip->ecc.correct = txx9ndfmc_correct_data; chip->ecc.hwctl = txx9ndfmc_enable_hwecc; chip->ecc.mode = NAND_ECC_HW; - /* txx9ndfmc_nand_scan will overwrite ecc.size and ecc.bytes */ - chip->ecc.size = 256; - chip->ecc.bytes = 3; chip->ecc.strength = 1; chip->chip_delay = 100; chip->controller = &drvdata->controller; -- cgit v1.2.3 From ee1af8296dbc34c8198cbdd54cc244c19a10321e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:49 +0200 Subject: mtd: rawnand: txx9ndfmc: convert driver to nand_scan() Two helpers have been added to the core to do all kind of controller side configuration/initialization between the detection phase and the final NAND scan. Implement these hooks so that we can convert the driver to just use nand_scan() instead of the nand_scan_ident() + nand_scan_tail() pair. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/txx9ndfmc.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c index 9808b18b15e2..4d61a14fcb65 100644 --- a/drivers/mtd/nand/raw/txx9ndfmc.c +++ b/drivers/mtd/nand/raw/txx9ndfmc.c @@ -254,26 +254,25 @@ static void txx9ndfmc_initialize(struct platform_device *dev) #define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \ DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000) -static int txx9ndfmc_nand_scan(struct mtd_info *mtd) +static int txx9ndfmc_attach_chip(struct nand_chip *chip) { - struct nand_chip *chip = mtd_to_nand(mtd); - int ret; - - ret = nand_scan_ident(mtd, 1, NULL); - if (!ret) { - if (mtd->writesize >= 512) { - chip->ecc.size = 512; - chip->ecc.bytes = 6; - } else { - chip->ecc.size = 256; - chip->ecc.bytes = 3; - } - - ret = nand_scan_tail(mtd); + struct mtd_info *mtd = nand_to_mtd(chip); + + if (mtd->writesize >= 512) { + chip->ecc.size = 512; + chip->ecc.bytes = 6; + } else { + chip->ecc.size = 256; + chip->ecc.bytes = 3; } - return ret; + + return 0; } +static const struct nand_controller_ops txx9ndfmc_controller_ops = { + .attach_chip = txx9ndfmc_attach_chip, +}; + static int __init txx9ndfmc_probe(struct platform_device *dev) { struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); @@ -307,6 +306,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) (gbusclk + 500000) / 1000000, hold, spw); nand_controller_init(&drvdata->controller); + drvdata->controller.ops = &txx9ndfmc_controller_ops; platform_set_drvdata(dev, drvdata); txx9ndfmc_initialize(dev); @@ -359,7 +359,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) if (plat->wide_mask & (1 << i)) chip->options |= NAND_BUSWIDTH_16; - if (txx9ndfmc_nand_scan(mtd)) { + if (nand_scan(mtd, 1)) { kfree(txx9_priv->mtdname); kfree(txx9_priv); continue; -- cgit v1.2.3 From 98732da1a08ebb666983d469981a8b994e77d556 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:50 +0200 Subject: mtd: rawnand: do not export nand_scan_[ident|tail]() anymore Both nand_scan_ident() and nand_scan_tail() helpers used to be called directly from controller drivers that needed to tweak some ECC-related parameters before nand_scan_tail(). This separation prevented dynamic allocations during the phase of NAND identification, which was inconvenient. All controller drivers have been moved to use nand_scan(), in conjunction with the chip->ecc.[attach|detach]_chip() hooks that actually do the required tweaking sequence between both ident/tail calls, allowing programmers to use dynamic allocation as they need all across the scanning sequence. Declare nand_scan_[ident|tail]() statically now. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/nand_base.c | 16 +++++++++------- include/linux/mtd/rawnand.h | 18 ++++++------------ 2 files changed, 15 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 42a7a934a17b..34ea44f90fd8 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5924,7 +5924,7 @@ static int nand_dt_init(struct nand_chip *chip) } /** - * nand_scan_ident - [NAND Interface] Scan for the NAND device + * nand_scan_ident - Scan for the NAND device * @mtd: MTD device structure * @maxchips: number of chips to scan for * @table: alternative NAND ID table @@ -5932,9 +5932,13 @@ static int nand_dt_init(struct nand_chip *chip) * This is the first phase of the normal nand_scan() function. It reads the * flash ID and sets up MTD fields accordingly. * + * This helper used to be called directly from controller drivers that needed + * to tweak some ECC-related parameters before nand_scan_tail(). This separation + * prevented dynamic allocations during this phase which was unconvenient and + * as been banned for the benefit of the ->init_ecc()/cleanup_ecc() hooks. */ -int nand_scan_ident(struct mtd_info *mtd, int maxchips, - struct nand_flash_dev *table) +static int nand_scan_ident(struct mtd_info *mtd, int maxchips, + struct nand_flash_dev *table) { int i, nand_maf_id, nand_dev_id; struct nand_chip *chip = mtd_to_nand(mtd); @@ -6008,7 +6012,6 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, return 0; } -EXPORT_SYMBOL(nand_scan_ident); static int nand_set_ecc_soft_ops(struct mtd_info *mtd) { @@ -6385,14 +6388,14 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd) } /** - * nand_scan_tail - [NAND Interface] Scan for the NAND device + * nand_scan_tail - Scan for the NAND device * @mtd: MTD device structure * * This is the second phase of the normal nand_scan() function. It fills out * all the uninitialized function pointers with the defaults and scans for a * bad block table if appropriate. */ -int nand_scan_tail(struct mtd_info *mtd) +static int nand_scan_tail(struct mtd_info *mtd) { struct nand_chip *chip = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &chip->ecc; @@ -6716,7 +6719,6 @@ err_free_buf: return ret; } -EXPORT_SYMBOL(nand_scan_tail); static int nand_attach(struct nand_chip *chip) { diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index fbd6b29cf22c..71571ed23a20 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -35,17 +35,6 @@ static inline int nand_scan(struct mtd_info *mtd, int max_chips) return nand_scan_with_ids(mtd, max_chips, NULL); } -/* - * Separate phases of nand_scan(), allowing board driver to intervene - * and override command or ECC setup according to flash type. - */ -int nand_scan_ident(struct mtd_info *mtd, int max_chips, - struct nand_flash_dev *table); -int nand_scan_tail(struct mtd_info *mtd); - -/* Unregister the MTD device and free resources held by the NAND device */ -void nand_release(struct mtd_info *mtd); - /* Internal helper for board drivers which need to override command function */ void nand_wait_ready(struct mtd_info *mtd); @@ -1745,8 +1734,13 @@ int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len, int nand_write_data_op(struct nand_chip *chip, const void *buf, unsigned int len, bool force_8bit); -/* Free resources held by the NAND device */ +/* + * Free resources held by the NAND device, must be called on error after a + * sucessful nand_scan(). + */ void nand_cleanup(struct nand_chip *chip); +/* Unregister the MTD device and calls nand_cleanup() */ +void nand_release(struct mtd_info *mtd); /* Default extended ID decoding function */ void nand_decode_ext_id(struct nand_chip *chip); -- cgit v1.2.3 From 2023f1fa216f30b1877d65be2057fbaf0bbd49b3 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:51 +0200 Subject: mtd: rawnand: allocate model parameter dynamically Thanks to the migration of all drivers to use nand_scan() and the related nand_controller_ops, we can now allocate data during the detection phase. Let's do it first for the NAND model parameter which is allocated in nand_detect(). Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/nand_base.c | 52 +++++++++++++++++++++++++++++++--------- include/linux/mtd/rawnand.h | 2 +- 2 files changed, 42 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 34ea44f90fd8..00e80781124a 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5225,8 +5225,11 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) sanitize_string(p->manufacturer, sizeof(p->manufacturer)); sanitize_string(p->model, sizeof(p->model)); - strncpy(chip->parameters.model, p->model, - sizeof(chip->parameters.model) - 1); + chip->parameters.model = kstrdup(p->model, GFP_KERNEL); + if (!chip->parameters.model) { + ret = -ENOMEM; + goto free_onfi_param_page; + } mtd->writesize = le32_to_cpu(p->byte_per_page); @@ -5356,8 +5359,11 @@ static int nand_flash_detect_jedec(struct nand_chip *chip) sanitize_string(p->manufacturer, sizeof(p->manufacturer)); sanitize_string(p->model, sizeof(p->model)); - strncpy(chip->parameters.model, p->model, - sizeof(chip->parameters.model) - 1); + chip->parameters.model = kstrdup(p->model, GFP_KERNEL); + if (!chip->parameters.model) { + ret = -ENOMEM; + goto free_jedec_param_page; + } mtd->writesize = le32_to_cpu(p->byte_per_page); @@ -5546,8 +5552,9 @@ static bool find_full_id_nand(struct nand_chip *chip, chip->onfi_timing_mode_default = type->onfi_timing_mode_default; - strncpy(chip->parameters.model, type->name, - sizeof(chip->parameters.model) - 1); + chip->parameters.model = kstrdup(type->name, GFP_KERNEL); + if (!chip->parameters.model) + return false; return true; } @@ -5706,8 +5713,9 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) if (!type->name) return -ENODEV; - strncpy(chip->parameters.model, type->name, - sizeof(chip->parameters.model) - 1); + chip->parameters.model = kstrdup(type->name, GFP_KERNEL); + if (!chip->parameters.model) + return -ENOMEM; chip->chipsize = (uint64_t)type->chipsize << 20; @@ -5737,7 +5745,9 @@ ident_done: mtd->name); pr_warn("bus width %d instead of %d bits\n", busw ? 16 : 8, (chip->options & NAND_BUSWIDTH_16) ? 16 : 8); - return -EINVAL; + ret = -EINVAL; + + goto free_detect_allocation; } nand_decode_bbm_options(chip); @@ -5774,6 +5784,11 @@ ident_done: (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", mtd->erasesize >> 10, mtd->writesize, mtd->oobsize); return 0; + +free_detect_allocation: + kfree(chip->parameters.model); + + return ret; } static const char * const nand_ecc_modes[] = { @@ -6013,6 +6028,11 @@ static int nand_scan_ident(struct mtd_info *mtd, int maxchips, return 0; } +static void nand_scan_ident_cleanup(struct nand_chip *chip) +{ + kfree(chip->parameters.model); +} + static int nand_set_ecc_soft_ops(struct mtd_info *mtd) { struct nand_chip *chip = mtd_to_nand(mtd); @@ -6760,11 +6780,18 @@ int nand_scan_with_ids(struct mtd_info *mtd, int maxchips, ret = nand_attach(chip); if (ret) - return ret; + goto cleanup_ident; ret = nand_scan_tail(mtd); if (ret) - nand_detach(chip); + goto detach_chip; + + return 0; + +detach_chip: + nand_detach(chip); +cleanup_ident: + nand_scan_ident_cleanup(chip); return ret; } @@ -6796,6 +6823,9 @@ void nand_cleanup(struct nand_chip *chip) /* Free controller specific allocations after chip identification */ nand_detach(chip); + + /* Free identification phase allocations */ + nand_scan_ident_cleanup(chip); } EXPORT_SYMBOL_GPL(nand_cleanup); diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 71571ed23a20..099fa166569a 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -476,7 +476,7 @@ struct onfi_params { */ struct nand_parameters { /* Generic parameters */ - char model[100]; + const char *model; bool supports_set_get_features; DECLARE_BITMAP(set_feature_list, ONFI_FEATURE_NUMBER); DECLARE_BITMAP(get_feature_list, ONFI_FEATURE_NUMBER); -- cgit v1.2.3 From 3d3fe3c05d5a17ebdf55b936c51017c127c0ed44 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Jul 2018 15:31:52 +0200 Subject: mtd: rawnand: allocate dynamically ONFI parameters during detection Now that it is possible to do dynamic allocations during the identification phase, convert the onfi_params structure (which is only needed with ONFI compliant chips) into a pointer that will be allocated only if needed. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/nand/raw/nand_base.c | 54 +++++++++++++++++++++++-------------- drivers/mtd/nand/raw/nand_micron.c | 7 ++--- drivers/mtd/nand/raw/nand_timings.c | 12 ++++----- include/linux/mtd/rawnand.h | 6 ++--- 4 files changed, 47 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 00e80781124a..d527e448ce19 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5151,6 +5151,8 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); struct nand_onfi_params *p; + struct onfi_params *onfi; + int onfi_version = 0; char id[4]; int i, ret, val; @@ -5206,21 +5208,19 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) /* Check version */ val = le16_to_cpu(p->revision); if (val & ONFI_VERSION_2_3) - chip->parameters.onfi.version = 23; + onfi_version = 23; else if (val & ONFI_VERSION_2_2) - chip->parameters.onfi.version = 22; + onfi_version = 22; else if (val & ONFI_VERSION_2_1) - chip->parameters.onfi.version = 21; + onfi_version = 21; else if (val & ONFI_VERSION_2_0) - chip->parameters.onfi.version = 20; + onfi_version = 20; else if (val & ONFI_VERSION_1_0) - chip->parameters.onfi.version = 10; + onfi_version = 10; - if (!chip->parameters.onfi.version) { + if (!onfi_version) { pr_info("unsupported ONFI version: %d\n", val); goto free_onfi_param_page; - } else { - ret = 1; } sanitize_string(p->manufacturer, sizeof(p->manufacturer)); @@ -5257,7 +5257,7 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) if (p->ecc_bits != 0xff) { chip->ecc_strength_ds = p->ecc_bits; chip->ecc_step_ds = 512; - } else if (chip->parameters.onfi.version >= 21 && + } else if (onfi_version >= 21 && (le16_to_cpu(p->features) & ONFI_FEATURE_EXT_PARAM_PAGE)) { /* @@ -5284,19 +5284,33 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) bitmap_set(chip->parameters.set_feature_list, ONFI_FEATURE_ADDR_TIMING_MODE, 1); } - chip->parameters.onfi.tPROG = le16_to_cpu(p->t_prog); - chip->parameters.onfi.tBERS = le16_to_cpu(p->t_bers); - chip->parameters.onfi.tR = le16_to_cpu(p->t_r); - chip->parameters.onfi.tCCS = le16_to_cpu(p->t_ccs); - chip->parameters.onfi.async_timing_mode = - le16_to_cpu(p->async_timing_mode); - chip->parameters.onfi.vendor_revision = - le16_to_cpu(p->vendor_revision); - memcpy(chip->parameters.onfi.vendor, p->vendor, - sizeof(p->vendor)); + onfi = kzalloc(sizeof(*onfi), GFP_KERNEL); + if (!onfi) { + ret = -ENOMEM; + goto free_model; + } + + onfi->version = onfi_version; + onfi->tPROG = le16_to_cpu(p->t_prog); + onfi->tBERS = le16_to_cpu(p->t_bers); + onfi->tR = le16_to_cpu(p->t_r); + onfi->tCCS = le16_to_cpu(p->t_ccs); + onfi->async_timing_mode = le16_to_cpu(p->async_timing_mode); + onfi->vendor_revision = le16_to_cpu(p->vendor_revision); + memcpy(onfi->vendor, p->vendor, sizeof(p->vendor)); + chip->parameters.onfi = onfi; + + /* Identification done, free the full ONFI parameter page and exit */ + kfree(p); + + return 1; + +free_model: + kfree(chip->parameters.model); free_onfi_param_page: kfree(p); + return ret; } @@ -5693,7 +5707,6 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) } } - chip->parameters.onfi.version = 0; if (!type->name || !type->pagesize) { /* Check if the chip is ONFI compliant */ ret = nand_flash_detect_onfi(chip); @@ -6031,6 +6044,7 @@ static int nand_scan_ident(struct mtd_info *mtd, int maxchips, static void nand_scan_ident_cleanup(struct nand_chip *chip) { kfree(chip->parameters.model); + kfree(chip->parameters.onfi); } static int nand_set_ecc_soft_ops(struct mtd_info *mtd) diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index 656947d91841..f5dc0a7a2456 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -88,9 +88,10 @@ static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) static int micron_nand_onfi_init(struct nand_chip *chip) { struct nand_parameters *p = &chip->parameters; - struct nand_onfi_vendor_micron *micron = (void *)p->onfi.vendor; - if (chip->parameters.onfi.version && p->onfi.vendor_revision) { + if (p->onfi) { + struct nand_onfi_vendor_micron *micron = (void *)p->onfi->vendor; + chip->read_retries = micron->read_retry_options; chip->setup_read_retry = micron_nand_setup_read_retry; } @@ -382,7 +383,7 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) u8 id[5]; int ret; - if (!chip->parameters.onfi.version) + if (!chip->parameters.onfi) return MICRON_ON_DIE_UNSUPPORTED; if (chip->bits_per_cell != 1) diff --git a/drivers/mtd/nand/raw/nand_timings.c b/drivers/mtd/nand/raw/nand_timings.c index 9bb599106a31..ebc7b5f76f77 100644 --- a/drivers/mtd/nand/raw/nand_timings.c +++ b/drivers/mtd/nand/raw/nand_timings.c @@ -294,6 +294,7 @@ int onfi_fill_data_interface(struct nand_chip *chip, int timing_mode) { struct nand_data_interface *iface = &chip->data_interface; + struct onfi_params *onfi = chip->parameters.onfi; if (type != NAND_SDR_IFACE) return -EINVAL; @@ -308,17 +309,16 @@ int onfi_fill_data_interface(struct nand_chip *chip, * tPROG, tBERS, tR and tCCS. * These information are part of the ONFI parameter page. */ - if (chip->parameters.onfi.version) { - struct nand_parameters *params = &chip->parameters; + if (onfi) { struct nand_sdr_timings *timings = &iface->timings.sdr; /* microseconds -> picoseconds */ - timings->tPROG_max = 1000000ULL * params->onfi.tPROG; - timings->tBERS_max = 1000000ULL * params->onfi.tBERS; - timings->tR_max = 1000000ULL * params->onfi.tR; + timings->tPROG_max = 1000000ULL * onfi->tPROG; + timings->tBERS_max = 1000000ULL * onfi->tBERS; + timings->tR_max = 1000000ULL * onfi->tR; /* nanoseconds -> picoseconds */ - timings->tCCS_min = 1000UL * params->onfi.tCCS; + timings->tCCS_min = 1000UL * onfi->tCCS; } else { struct nand_sdr_timings *timings = &iface->timings.sdr; /* diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 099fa166569a..efb2345359bb 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -482,7 +482,7 @@ struct nand_parameters { DECLARE_BITMAP(get_feature_list, ONFI_FEATURE_NUMBER); /* ONFI parameters */ - struct onfi_params onfi; + struct onfi_params *onfi; }; /* The maximum expected count of bytes in the NAND ID sequence */ @@ -1618,10 +1618,10 @@ struct platform_nand_data { /* return the supported asynchronous timing mode. */ static inline int onfi_get_async_timing_mode(struct nand_chip *chip) { - if (!chip->parameters.onfi.version) + if (!chip->parameters.onfi) return ONFI_TIMING_MODE_UNKNOWN; - return chip->parameters.onfi.async_timing_mode; + return chip->parameters.onfi->async_timing_mode; } int onfi_fill_data_interface(struct nand_chip *chip, -- cgit v1.2.3