summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/bridge/panel.c2
-rw-r--r--drivers/gpu/drm/drm_framebuffer.c1
-rw-r--r--drivers/gpu/drm/drm_ioc32.c3
-rw-r--r--drivers/gpu/drm/drm_vblank.c2
-rw-r--r--drivers/gpu/drm/i915/gvt/cmd_parser.c10
-rw-r--r--drivers/gpu/drm/i915/gvt/display.c22
-rw-r--r--drivers/gpu/drm/i915/gvt/gtt.c2
-rw-r--r--drivers/gpu/drm/i915/gvt/handlers.c37
-rw-r--r--drivers/gpu/drm/i915/gvt/kvmgt.c14
-rw-r--r--drivers/gpu/drm/i915/gvt/scheduler.c28
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs.c2
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c8
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c38
-rw-r--r--drivers/gpu/drm/i915/i915_perf.c12
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h2
-rw-r--r--drivers/gpu/drm/i915/intel_cdclk.c20
-rw-r--r--drivers/gpu/drm/i915/intel_engine_cs.c14
-rw-r--r--drivers/gpu/drm/i915/intel_fbdev.c11
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_gem_dmabuf.c6
-rw-r--r--drivers/gpu/drm/mediatek/Makefile3
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_color.c176
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_ovl.c7
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_crtc.c2
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c80
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_drv.c33
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_drv.h1
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_plane.c2
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dsi.c2
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi.c27
-rw-r--r--drivers/gpu/drm/radeon/atombios_encoders.c13
-rw-r--r--drivers/gpu/drm/rockchip/cdn-dp-core.c2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.h2
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_gem.c5
-rw-r--r--drivers/mtd/Kconfig4
-rw-r--r--drivers/mtd/Makefile1
-rw-r--r--drivers/mtd/bcm47xxpart.c99
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0020.c2
-rw-r--r--drivers/mtd/devices/Kconfig10
-rw-r--r--drivers/mtd/devices/Makefile1
-rw-r--r--drivers/mtd/devices/m25p80.c121
-rw-r--r--drivers/mtd/devices/mchp23k256.c236
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c200
-rw-r--r--drivers/mtd/devices/serial_flash_cmds.h1
-rw-r--r--drivers/mtd/devices/st_spi_fsm.c4
-rw-r--r--drivers/mtd/maps/physmap_of_gemini.c2
-rw-r--r--drivers/mtd/mtdcore.c2
-rw-r--r--drivers/mtd/mtdpart.c370
-rw-r--r--drivers/mtd/nand/Kconfig3
-rw-r--r--drivers/mtd/nand/atmel/nand-controller.c354
-rw-r--r--drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c2
-rw-r--r--drivers/mtd/nand/cafe_nand.c2
-rw-r--r--drivers/mtd/nand/davinci_nand.c3
-rw-r--r--drivers/mtd/nand/denali.c1831
-rw-r--r--drivers/mtd/nand/denali.h315
-rw-r--r--drivers/mtd/nand/denali_dt.c53
-rw-r--r--drivers/mtd/nand/denali_pci.c26
-rw-r--r--drivers/mtd/nand/docg4.c2
-rw-r--r--drivers/mtd/nand/fsl_elbc_nand.c2
-rw-r--r--drivers/mtd/nand/fsl_ifc_nand.c81
-rw-r--r--drivers/mtd/nand/fsmc_nand.c122
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-lib.c6
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.c75
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.h13
-rw-r--r--drivers/mtd/nand/hisi504_nand.c2
-rw-r--r--drivers/mtd/nand/jz4780_nand.c2
-rw-r--r--drivers/mtd/nand/mpc5121_nfc.c2
-rw-r--r--drivers/mtd/nand/mtk_ecc.c228
-rw-r--r--drivers/mtd/nand/mtk_ecc.h2
-rw-r--r--drivers/mtd/nand/mtk_nand.c279
-rw-r--r--drivers/mtd/nand/mxc_nand.c12
-rw-r--r--drivers/mtd/nand/nand_base.c349
-rw-r--r--drivers/mtd/nand/nand_micron.c222
-rw-r--r--drivers/mtd/nand/orion_nand.c6
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c2
-rw-r--r--drivers/mtd/nand/qcom_nandc.c2
-rw-r--r--drivers/mtd/nand/s3c2410.c5
-rw-r--r--drivers/mtd/nand/sh_flctl.c2
-rw-r--r--drivers/mtd/nand/sunxi_nand.c9
-rw-r--r--drivers/mtd/nand/tango_nand.c22
-rw-r--r--drivers/mtd/nand/vf610_nfc.c2
-rw-r--r--drivers/mtd/parsers/Kconfig8
-rw-r--r--drivers/mtd/parsers/Makefile1
-rw-r--r--drivers/mtd/parsers/parser_trx.c126
-rw-r--r--drivers/mtd/spi-nor/Kconfig2
-rw-r--r--drivers/mtd/spi-nor/aspeed-smc.c183
-rw-r--r--drivers/mtd/spi-nor/atmel-quadspi.c83
-rw-r--r--drivers/mtd/spi-nor/cadence-quadspi.c20
-rw-r--r--drivers/mtd/spi-nor/fsl-quadspi.c6
-rw-r--r--drivers/mtd/spi-nor/hisi-sfc.c31
-rw-r--r--drivers/mtd/spi-nor/intel-spi.c7
-rw-r--r--drivers/mtd/spi-nor/mtk-quadspi.c15
-rw-r--r--drivers/mtd/spi-nor/nxp-spifi.c22
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c488
-rw-r--r--drivers/mtd/spi-nor/stm32-quadspi.c32
-rw-r--r--drivers/mtd/tests/subpagetest.c2
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.c42
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.h4
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c3
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c16
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c42
-rw-r--r--drivers/net/ethernet/cisco/enic/vnic_dev.c9
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_enet.c16
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/Makefile4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/accel/Makefile1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/Makefile1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fpga/Makefile1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ipoib/Makefile1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/Makefile1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/gid.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c9
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/metadata.c2
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_iwarp.c6
-rw-r--r--drivers/net/ethernet/sfc/ef10.c8
-rw-r--r--drivers/net/ethernet/smsc/smsc911x.c5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c10
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c14
-rw-r--r--drivers/net/tap.c18
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c5
-rw-r--r--drivers/platform/x86/alienware-wmi.c6
-rw-r--r--drivers/platform/x86/asus-wmi.c4
-rw-r--r--drivers/platform/x86/compal-laptop.c2
-rw-r--r--drivers/platform/x86/fujitsu-laptop.c14
-rw-r--r--drivers/platform/x86/ideapad-laptop.c85
-rw-r--r--drivers/platform/x86/intel_telemetry_debugfs.c33
-rw-r--r--drivers/platform/x86/panasonic-laptop.c2
-rw-r--r--drivers/platform/x86/peaq-wmi.c2
-rw-r--r--drivers/platform/x86/samsung-laptop.c2
-rw-r--r--drivers/platform/x86/silead_dmi.c32
-rw-r--r--drivers/platform/x86/toshiba_acpi.c2
-rw-r--r--drivers/power/supply/twl4030_charger.c43
-rw-r--r--drivers/pwm/core.c4
-rw-r--r--drivers/pwm/pwm-bfin.c4
-rw-r--r--drivers/pwm/pwm-cros-ec.c4
-rw-r--r--drivers/pwm/pwm-hibvt.c2
-rw-r--r--drivers/pwm/pwm-meson.c48
-rw-r--r--drivers/pwm/pwm-sun4i.c263
-rw-r--r--drivers/pwm/pwm-tegra.c18
-rw-r--r--drivers/rtc/Kconfig37
-rw-r--r--drivers/rtc/Makefile4
-rw-r--r--drivers/rtc/class.c202
-rw-r--r--drivers/rtc/interface.c9
-rw-r--r--drivers/rtc/nvmem.c113
-rw-r--r--drivers/rtc/rtc-at91rm9200.c14
-rw-r--r--drivers/rtc/rtc-brcmstb-waketimer.c330
-rw-r--r--drivers/rtc/rtc-core.h8
-rw-r--r--drivers/rtc/rtc-dev.c2
-rw-r--r--drivers/rtc/rtc-ds1307.c957
-rw-r--r--drivers/rtc/rtc-ds3232.c119
-rw-r--r--drivers/rtc/rtc-ftrtc010.c (renamed from drivers/rtc/rtc-gemini.c)119
-rw-r--r--drivers/rtc/rtc-m41t80.c251
-rw-r--r--drivers/rtc/rtc-mxc.c11
-rw-r--r--drivers/rtc/rtc-nuc900.c2
-rw-r--r--drivers/rtc/rtc-opal.c32
-rw-r--r--drivers/rtc/rtc-pcf8563.c2
-rw-r--r--drivers/rtc/rtc-rv8803.c72
-rw-r--r--drivers/rtc/rtc-s3c.c147
-rw-r--r--drivers/rtc/rtc-st-lpc.c19
-rw-r--r--drivers/rtc/rtc-stm32.c82
-rw-r--r--drivers/rtc/rtc-sysfs.c3
-rw-r--r--drivers/staging/mt29f_spinand/mt29f_spinand.c2
-rw-r--r--drivers/vfio/pci/vfio_pci.c4
-rw-r--r--drivers/vfio/vfio.c86
-rw-r--r--drivers/video/console/mdacon.c89
-rw-r--r--drivers/video/fbdev/aty/atyfb_base.c10
-rw-r--r--drivers/video/fbdev/core/fbmem.c5
-rw-r--r--drivers/video/fbdev/fsl-diu-fb.c4
-rw-r--r--drivers/video/fbdev/matrox/matroxfb_base.c2
-rw-r--r--drivers/video/fbdev/omap/omapfb_main.c8
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c4
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/manager-sysfs.c14
-rw-r--r--drivers/video/fbdev/pxafb.c5
-rw-r--r--drivers/video/fbdev/sh_mobile_lcdcfb.c2
-rw-r--r--drivers/video/fbdev/uvesafb.c148
-rw-r--r--drivers/video/fbdev/vermilion/cr_pll.c1
180 files changed, 6755 insertions, 3675 deletions
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 99f9a4beb859..67fe19e5a9c6 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -161,7 +161,7 @@ struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel,
int ret;
if (!panel)
- return ERR_PTR(EINVAL);
+ return ERR_PTR(-EINVAL);
panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge),
GFP_KERNEL);
diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c
index fc8ef42203ec..b3ef4f1c2630 100644
--- a/drivers/gpu/drm/drm_framebuffer.c
+++ b/drivers/gpu/drm/drm_framebuffer.c
@@ -832,6 +832,7 @@ unlock:
drm_atomic_clean_old_fb(dev, plane_mask, ret);
if (ret == -EDEADLK) {
+ drm_atomic_state_clear(state);
drm_modeset_backoff(&ctx);
goto retry;
}
diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c
index 0b2d8c4a2fa5..d1f202852028 100644
--- a/drivers/gpu/drm/drm_ioc32.c
+++ b/drivers/gpu/drm/drm_ioc32.c
@@ -112,6 +112,9 @@ static int compat_drm_version(struct file *file, unsigned int cmd,
v32.version_major = v.version_major;
v32.version_minor = v.version_minor;
v32.version_patchlevel = v.version_patchlevel;
+ v32.name_len = v.name_len;
+ v32.date_len = v.date_len;
+ v32.desc_len = v.desc_len;
if (copy_to_user((void __user *)arg, &v32, sizeof(v32)))
return -EFAULT;
return 0;
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index 463e4d81fb0d..e9f33cd805dd 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -242,7 +242,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
* Otherwise reinitialize delayed at next vblank interrupt and assign 0
* for now, to mark the vblanktimestamp as invalid.
*/
- if (!rc && in_vblank_irq)
+ if (!rc && !in_vblank_irq)
t_vblank = (struct timeval) {0, 0};
store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c
index 51241de5e7a7..713848c36349 100644
--- a/drivers/gpu/drm/i915/gvt/cmd_parser.c
+++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c
@@ -2536,6 +2536,11 @@ static int scan_workload(struct intel_vgpu_workload *workload)
gma_head == gma_tail)
return 0;
+ if (!intel_gvt_ggtt_validate_range(s.vgpu, s.ring_start, s.ring_size)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
ret = ip_gma_set(&s, gma_head);
if (ret)
goto out;
@@ -2579,6 +2584,11 @@ static int scan_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx)
s.rb_va = wa_ctx->indirect_ctx.shadow_va;
s.workload = workload;
+ if (!intel_gvt_ggtt_validate_range(s.vgpu, s.ring_start, s.ring_size)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
ret = ip_gma_set(&s, gma_head);
if (ret)
goto out;
diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c
index e0261fcc5b50..2deb05f618fb 100644
--- a/drivers/gpu/drm/i915/gvt/display.c
+++ b/drivers/gpu/drm/i915/gvt/display.c
@@ -197,6 +197,12 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu)
(TRANS_DDI_BPC_8 | TRANS_DDI_MODE_SELECT_DP_SST |
(PORT_B << TRANS_DDI_PORT_SHIFT) |
TRANS_DDI_FUNC_ENABLE);
+ if (IS_BROADWELL(dev_priv)) {
+ vgpu_vreg(vgpu, PORT_CLK_SEL(PORT_B)) &=
+ ~PORT_CLK_SEL_MASK;
+ vgpu_vreg(vgpu, PORT_CLK_SEL(PORT_B)) |=
+ PORT_CLK_SEL_LCPLL_810;
+ }
vgpu_vreg(vgpu, DDI_BUF_CTL(PORT_B)) |= DDI_BUF_CTL_ENABLE;
vgpu_vreg(vgpu, DDI_BUF_CTL(PORT_B)) &= ~DDI_BUF_IS_IDLE;
vgpu_vreg(vgpu, SDEISR) |= SDE_PORTB_HOTPLUG_CPT;
@@ -211,6 +217,12 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu)
(TRANS_DDI_BPC_8 | TRANS_DDI_MODE_SELECT_DP_SST |
(PORT_C << TRANS_DDI_PORT_SHIFT) |
TRANS_DDI_FUNC_ENABLE);
+ if (IS_BROADWELL(dev_priv)) {
+ vgpu_vreg(vgpu, PORT_CLK_SEL(PORT_C)) &=
+ ~PORT_CLK_SEL_MASK;
+ vgpu_vreg(vgpu, PORT_CLK_SEL(PORT_C)) |=
+ PORT_CLK_SEL_LCPLL_810;
+ }
vgpu_vreg(vgpu, DDI_BUF_CTL(PORT_C)) |= DDI_BUF_CTL_ENABLE;
vgpu_vreg(vgpu, DDI_BUF_CTL(PORT_C)) &= ~DDI_BUF_IS_IDLE;
vgpu_vreg(vgpu, SFUSE_STRAP) |= SFUSE_STRAP_DDIC_DETECTED;
@@ -225,6 +237,12 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu)
(TRANS_DDI_BPC_8 | TRANS_DDI_MODE_SELECT_DP_SST |
(PORT_D << TRANS_DDI_PORT_SHIFT) |
TRANS_DDI_FUNC_ENABLE);
+ if (IS_BROADWELL(dev_priv)) {
+ vgpu_vreg(vgpu, PORT_CLK_SEL(PORT_D)) &=
+ ~PORT_CLK_SEL_MASK;
+ vgpu_vreg(vgpu, PORT_CLK_SEL(PORT_D)) |=
+ PORT_CLK_SEL_LCPLL_810;
+ }
vgpu_vreg(vgpu, DDI_BUF_CTL(PORT_D)) |= DDI_BUF_CTL_ENABLE;
vgpu_vreg(vgpu, DDI_BUF_CTL(PORT_D)) &= ~DDI_BUF_IS_IDLE;
vgpu_vreg(vgpu, SFUSE_STRAP) |= SFUSE_STRAP_DDID_DETECTED;
@@ -244,6 +262,10 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu)
vgpu_vreg(vgpu, DDI_BUF_CTL(PORT_A)) |= DDI_INIT_DISPLAY_DETECTED;
}
+
+ /* Clear host CRT status, so guest couldn't detect this host CRT. */
+ if (IS_BROADWELL(dev_priv))
+ vgpu_vreg(vgpu, PCH_ADPA) &= ~ADPA_CRT_HOTPLUG_MONITOR_MASK;
}
static void clean_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num)
diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c
index 66374dba3b1a..6166e34d892b 100644
--- a/drivers/gpu/drm/i915/gvt/gtt.c
+++ b/drivers/gpu/drm/i915/gvt/gtt.c
@@ -2259,6 +2259,8 @@ int intel_gvt_init_gtt(struct intel_gvt *gvt)
ret = setup_spt_oos(gvt);
if (ret) {
gvt_err("fail to initialize SPT oos\n");
+ dma_unmap_page(dev, daddr, 4096, PCI_DMA_BIDIRECTIONAL);
+ __free_page(gvt->gtt.scratch_ggtt_page);
return ret;
}
}
diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c
index 1414d7e6148d..17febe830ff6 100644
--- a/drivers/gpu/drm/i915/gvt/handlers.c
+++ b/drivers/gpu/drm/i915/gvt/handlers.c
@@ -367,21 +367,24 @@ static int lcpll_ctl_mmio_write(struct intel_vgpu *vgpu, unsigned int offset,
static int dpy_reg_mmio_read(struct intel_vgpu *vgpu, unsigned int offset,
void *p_data, unsigned int bytes)
{
- *(u32 *)p_data = (1 << 17);
- return 0;
-}
-
-static int dpy_reg_mmio_read_2(struct intel_vgpu *vgpu, unsigned int offset,
- void *p_data, unsigned int bytes)
-{
- *(u32 *)p_data = 3;
- return 0;
-}
+ switch (offset) {
+ case 0xe651c:
+ case 0xe661c:
+ case 0xe671c:
+ case 0xe681c:
+ vgpu_vreg(vgpu, offset) = 1 << 17;
+ break;
+ case 0xe6c04:
+ vgpu_vreg(vgpu, offset) = 0x3;
+ break;
+ case 0xe6e1c:
+ vgpu_vreg(vgpu, offset) = 0x2f << 16;
+ break;
+ default:
+ return -EINVAL;
+ }
-static int dpy_reg_mmio_read_3(struct intel_vgpu *vgpu, unsigned int offset,
- void *p_data, unsigned int bytes)
-{
- *(u32 *)p_data = (0x2f << 16);
+ read_vreg(vgpu, offset, p_data, bytes);
return 0;
}
@@ -1925,7 +1928,7 @@ static int init_generic_mmio_info(struct intel_gvt *gvt)
MMIO_F(_PCH_DPD_AUX_CH_CTL, 6 * 4, 0, 0, 0, D_PRE_SKL, NULL,
dp_aux_ch_ctl_mmio_write);
- MMIO_RO(PCH_ADPA, D_ALL, 0, ADPA_CRT_HOTPLUG_MONITOR_MASK, NULL, pch_adpa_mmio_write);
+ MMIO_DH(PCH_ADPA, D_PRE_SKL, NULL, pch_adpa_mmio_write);
MMIO_DH(_PCH_TRANSACONF, D_ALL, NULL, transconf_mmio_write);
MMIO_DH(_PCH_TRANSBCONF, D_ALL, NULL, transconf_mmio_write);
@@ -2011,8 +2014,8 @@ static int init_generic_mmio_info(struct intel_gvt *gvt)
MMIO_DH(0xe661c, D_ALL, dpy_reg_mmio_read, NULL);
MMIO_DH(0xe671c, D_ALL, dpy_reg_mmio_read, NULL);
MMIO_DH(0xe681c, D_ALL, dpy_reg_mmio_read, NULL);
- MMIO_DH(0xe6c04, D_ALL, dpy_reg_mmio_read_2, NULL);
- MMIO_DH(0xe6e1c, D_ALL, dpy_reg_mmio_read_3, NULL);
+ MMIO_DH(0xe6c04, D_ALL, dpy_reg_mmio_read, NULL);
+ MMIO_DH(0xe6e1c, D_ALL, dpy_reg_mmio_read, NULL);
MMIO_RO(PCH_PORT_HOTPLUG, D_ALL, 0,
PORTA_HOTPLUG_STATUS_MASK
diff --git a/drivers/gpu/drm/i915/gvt/kvmgt.c b/drivers/gpu/drm/i915/gvt/kvmgt.c
index 1ae0b4083ce1..fd0c85f9ef3c 100644
--- a/drivers/gpu/drm/i915/gvt/kvmgt.c
+++ b/drivers/gpu/drm/i915/gvt/kvmgt.c
@@ -232,16 +232,20 @@ static void gvt_cache_destroy(struct intel_vgpu *vgpu)
struct device *dev = mdev_dev(vgpu->vdev.mdev);
unsigned long gfn;
- mutex_lock(&vgpu->vdev.cache_lock);
- while ((node = rb_first(&vgpu->vdev.cache))) {
+ for (;;) {
+ mutex_lock(&vgpu->vdev.cache_lock);
+ node = rb_first(&vgpu->vdev.cache);
+ if (!node) {
+ mutex_unlock(&vgpu->vdev.cache_lock);
+ break;
+ }
dma = rb_entry(node, struct gvt_dma, node);
gvt_dma_unmap_iova(vgpu, dma->iova);
gfn = dma->gfn;
-
- vfio_unpin_pages(dev, &gfn, 1);
__gvt_cache_remove_entry(vgpu, dma);
+ mutex_unlock(&vgpu->vdev.cache_lock);
+ vfio_unpin_pages(dev, &gfn, 1);
}
- mutex_unlock(&vgpu->vdev.cache_lock);
}
static struct intel_vgpu_type *intel_gvt_find_vgpu_type(struct intel_gvt *gvt,
diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c
index 488fdea348a9..4f7057d62d88 100644
--- a/drivers/gpu/drm/i915/gvt/scheduler.c
+++ b/drivers/gpu/drm/i915/gvt/scheduler.c
@@ -174,15 +174,6 @@ static int shadow_context_status_change(struct notifier_block *nb,
atomic_set(&workload->shadow_ctx_active, 1);
break;
case INTEL_CONTEXT_SCHEDULE_OUT:
- /* If the status is -EINPROGRESS means this workload
- * doesn't meet any issue during dispatching so when
- * get the SCHEDULE_OUT set the status to be zero for
- * good. If the status is NOT -EINPROGRESS means there
- * is something wrong happened during dispatching and
- * the status should not be set to zero
- */
- if (workload->status == -EINPROGRESS)
- workload->status = 0;
atomic_set(&workload->shadow_ctx_active, 0);
break;
default:
@@ -427,6 +418,18 @@ static void complete_current_workload(struct intel_gvt *gvt, int ring_id)
wait_event(workload->shadow_ctx_status_wq,
!atomic_read(&workload->shadow_ctx_active));
+ /* If this request caused GPU hang, req->fence.error will
+ * be set to -EIO. Use -EIO to set workload status so
+ * that when this request caused GPU hang, didn't trigger
+ * context switch interrupt to guest.
+ */
+ if (likely(workload->status == -EINPROGRESS)) {
+ if (workload->req->fence.error == -EIO)
+ workload->status = -EIO;
+ else
+ workload->status = 0;
+ }
+
i915_gem_request_put(fetch_and_zero(&workload->req));
if (!workload->status && !vgpu->resetting) {
@@ -464,8 +467,6 @@ struct workload_thread_param {
int ring_id;
};
-static DEFINE_MUTEX(scheduler_mutex);
-
static int workload_thread(void *priv)
{
struct workload_thread_param *p = (struct workload_thread_param *)priv;
@@ -497,8 +498,6 @@ static int workload_thread(void *priv)
if (!workload)
break;
- mutex_lock(&scheduler_mutex);
-
gvt_dbg_sched("ring id %d next workload %p vgpu %d\n",
workload->ring_id, workload,
workload->vgpu->id);
@@ -537,9 +536,6 @@ complete:
FORCEWAKE_ALL);
intel_runtime_pm_put(gvt->dev_priv);
-
- mutex_unlock(&scheduler_mutex);
-
}
return 0;
}
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 3f44076ec8a0..00d8967c8512 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -3087,7 +3087,7 @@ static void intel_connector_info(struct seq_file *m,
connector->display_info.cea_rev);
}
- if (!intel_encoder || intel_encoder->type == INTEL_OUTPUT_DP_MST)
+ if (!intel_encoder)
return;
switch (connector->connector_type) {
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index ee2325b180e7..fc307e03943c 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1132,10 +1132,12 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
* and the registers being closely associated.
*
* According to chipset errata, on the 965GM, MSI interrupts may
- * be lost or delayed, but we use them anyways to avoid
- * stuck interrupts on some machines.
+ * be lost or delayed, and was defeatured. MSI interrupts seem to
+ * get lost on g4x as well, and interrupt delivery seems to stay
+ * properly dead afterwards. So we'll just disable them for all
+ * pre-gen5 chipsets.
*/
- if (!IS_I945G(dev_priv) && !IS_I945GM(dev_priv)) {
+ if (INTEL_GEN(dev_priv) >= 5) {
if (pci_enable_msi(pdev) < 0)
DRM_DEBUG_DRIVER("can't enable MSI");
}
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 9337446f1068..054b2e54cdaf 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -288,20 +288,26 @@ static int eb_create(struct i915_execbuffer *eb)
* direct lookup.
*/
do {
+ unsigned int flags;
+
+ /* While we can still reduce the allocation size, don't
+ * raise a warning and allow the allocation to fail.
+ * On the last pass though, we want to try as hard
+ * as possible to perform the allocation and warn
+ * if it fails.
+ */
+ flags = GFP_TEMPORARY;
+ if (size > 1)
+ flags |= __GFP_NORETRY | __GFP_NOWARN;
+
eb->buckets = kzalloc(sizeof(struct hlist_head) << size,
- GFP_TEMPORARY |
- __GFP_NORETRY |
- __GFP_NOWARN);
+ flags);
if (eb->buckets)
break;
} while (--size);
- if (unlikely(!eb->buckets)) {
- eb->buckets = kzalloc(sizeof(struct hlist_head),
- GFP_TEMPORARY);
- if (unlikely(!eb->buckets))
- return -ENOMEM;
- }
+ if (unlikely(!size))
+ return -ENOMEM;
eb->lut_size = size;
} else {
@@ -452,7 +458,7 @@ eb_add_vma(struct i915_execbuffer *eb,
return err;
}
- if (eb->lut_size >= 0) {
+ if (eb->lut_size > 0) {
vma->exec_handle = entry->handle;
hlist_add_head(&vma->exec_node,
&eb->buckets[hash_32(entry->handle,
@@ -894,7 +900,7 @@ static void eb_release_vmas(const struct i915_execbuffer *eb)
static void eb_reset_vmas(const struct i915_execbuffer *eb)
{
eb_release_vmas(eb);
- if (eb->lut_size >= 0)
+ if (eb->lut_size > 0)
memset(eb->buckets, 0,
sizeof(struct hlist_head) << eb->lut_size);
}
@@ -903,7 +909,7 @@ static void eb_destroy(const struct i915_execbuffer *eb)
{
GEM_BUG_ON(eb->reloc_cache.rq);
- if (eb->lut_size >= 0)
+ if (eb->lut_size > 0)
kfree(eb->buckets);
}
@@ -2180,8 +2186,11 @@ i915_gem_do_execbuffer(struct drm_device *dev,
}
}
- if (eb_create(&eb))
- return -ENOMEM;
+ err = eb_create(&eb);
+ if (err)
+ goto err_out_fence;
+
+ GEM_BUG_ON(!eb.lut_size);
/*
* Take a local wakeref for preparing to dispatch the execbuf as
@@ -2340,6 +2349,7 @@ err_unlock:
err_rpm:
intel_runtime_pm_put(eb.i915);
eb_destroy(&eb);
+err_out_fence:
if (out_fence_fd != -1)
put_unused_fd(out_fence_fd);
err_in_fence:
diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c
index 38c44407bafc..9cd22f83b0cf 100644
--- a/drivers/gpu/drm/i915/i915_perf.c
+++ b/drivers/gpu/drm/i915/i915_perf.c
@@ -2067,10 +2067,6 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream,
return ret;
}
- ret = alloc_oa_buffer(dev_priv);
- if (ret)
- goto err_oa_buf_alloc;
-
/* PRM - observability performance counters:
*
* OACONTROL, performance counter enable, note:
@@ -2086,6 +2082,10 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream,
intel_runtime_pm_get(dev_priv);
intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
+ ret = alloc_oa_buffer(dev_priv);
+ if (ret)
+ goto err_oa_buf_alloc;
+
ret = dev_priv->perf.oa.ops.enable_metric_set(dev_priv);
if (ret)
goto err_enable;
@@ -2097,11 +2097,11 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream,
return 0;
err_enable:
- intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
- intel_runtime_pm_put(dev_priv);
free_oa_buffer(dev_priv);
err_oa_buf_alloc:
+ intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
+ intel_runtime_pm_put(dev_priv);
if (stream->ctx)
oa_put_render_ctx_id(stream);
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index c8647cfa81ba..64cc674b652a 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -1802,7 +1802,7 @@ enum skl_disp_power_wells {
#define POST_CURSOR_2(x) ((x) << 6)
#define POST_CURSOR_2_MASK (0x3F << 6)
#define CURSOR_COEFF(x) ((x) << 0)
-#define CURSOR_COEFF_MASK (0x3F << 6)
+#define CURSOR_COEFF_MASK (0x3F << 0)
#define _CNL_PORT_TX_DW5_GRP_AE 0x162354
#define _CNL_PORT_TX_DW5_GRP_B 0x1623D4
diff --git a/drivers/gpu/drm/i915/intel_cdclk.c b/drivers/gpu/drm/i915/intel_cdclk.c
index b8914db7d2e1..1241e5891b29 100644
--- a/drivers/gpu/drm/i915/intel_cdclk.c
+++ b/drivers/gpu/drm/i915/intel_cdclk.c
@@ -491,6 +491,14 @@ static void vlv_set_cdclk(struct drm_i915_private *dev_priv,
int cdclk = cdclk_state->cdclk;
u32 val, cmd;
+ /* There are cases where we can end up here with power domains
+ * off and a CDCLK frequency other than the minimum, like when
+ * issuing a modeset without actually changing any display after
+ * a system suspend. So grab the PIPE-A domain, which covers
+ * the HW blocks needed for the following programming.
+ */
+ intel_display_power_get(dev_priv, POWER_DOMAIN_PIPE_A);
+
if (cdclk >= 320000) /* jump to highest voltage for 400MHz too */
cmd = 2;
else if (cdclk == 266667)
@@ -549,6 +557,8 @@ static void vlv_set_cdclk(struct drm_i915_private *dev_priv,
intel_update_cdclk(dev_priv);
vlv_program_pfi_credits(dev_priv);
+
+ intel_display_power_put(dev_priv, POWER_DOMAIN_PIPE_A);
}
static void chv_set_cdclk(struct drm_i915_private *dev_priv,
@@ -568,6 +578,14 @@ static void chv_set_cdclk(struct drm_i915_private *dev_priv,
return;
}
+ /* There are cases where we can end up here with power domains
+ * off and a CDCLK frequency other than the minimum, like when
+ * issuing a modeset without actually changing any display after
+ * a system suspend. So grab the PIPE-A domain, which covers
+ * the HW blocks needed for the following programming.
+ */
+ intel_display_power_get(dev_priv, POWER_DOMAIN_PIPE_A);
+
/*
* Specs are full of misinformation, but testing on actual
* hardware has shown that we just need to write the desired
@@ -590,6 +608,8 @@ static void chv_set_cdclk(struct drm_i915_private *dev_priv,
intel_update_cdclk(dev_priv);
vlv_program_pfi_credits(dev_priv);
+
+ intel_display_power_put(dev_priv, POWER_DOMAIN_PIPE_A);
}
static int bdw_calc_cdclk(int max_pixclk)
diff --git a/drivers/gpu/drm/i915/intel_engine_cs.c b/drivers/gpu/drm/i915/intel_engine_cs.c
index a4487c5b7e37..5b4de719bec3 100644
--- a/drivers/gpu/drm/i915/intel_engine_cs.c
+++ b/drivers/gpu/drm/i915/intel_engine_cs.c
@@ -821,9 +821,10 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
I915_WRITE(BDW_SCRATCH1, I915_READ(BDW_SCRATCH1) |
GEN9_LBS_SLA_RETRY_TIMER_DECREMENT_ENABLE);
- /* WaDisableKillLogic:bxt,skl,kbl,cfl */
- I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
- ECOCHK_DIS_TLB);
+ /* WaDisableKillLogic:bxt,skl,kbl */
+ if (!IS_COFFEELAKE(dev_priv))
+ I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
+ ECOCHK_DIS_TLB);
/* WaClearFlowControlGpgpuContextSave:skl,bxt,kbl,glk,cfl */
/* WaDisablePartialInstShootdown:skl,bxt,kbl,glk,cfl */
@@ -894,10 +895,9 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
WA_SET_BIT_MASKED(HDC_CHICKEN0,
HDC_FORCE_NON_COHERENT);
- /* WaDisableHDCInvalidation:skl,bxt,kbl */
- if (!IS_COFFEELAKE(dev_priv))
- I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
- BDW_DISABLE_HDC_INVALIDATION);
+ /* WaDisableHDCInvalidation:skl,bxt,kbl,cfl */
+ I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
+ BDW_DISABLE_HDC_INVALIDATION);
/* WaDisableSamplerPowerBypassForSOPingPong:skl,bxt,kbl,cfl */
if (IS_SKYLAKE(dev_priv) ||
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index 03347c6ae599..0c4cde6b2e6f 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -535,13 +535,14 @@ static void intel_fbdev_destroy(struct intel_fbdev *ifbdev)
drm_fb_helper_fini(&ifbdev->helper);
- if (ifbdev->fb) {
+ if (ifbdev->vma) {
mutex_lock(&ifbdev->helper.dev->struct_mutex);
intel_unpin_fb_vma(ifbdev->vma);
mutex_unlock(&ifbdev->helper.dev->struct_mutex);
+ }
+ if (ifbdev->fb)
drm_framebuffer_remove(&ifbdev->fb->base);
- }
kfree(ifbdev);
}
@@ -765,7 +766,7 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous
struct intel_fbdev *ifbdev = dev_priv->fbdev;
struct fb_info *info;
- if (!ifbdev || !ifbdev->fb)
+ if (!ifbdev || !ifbdev->vma)
return;
info = ifbdev->helper.fbdev;
@@ -812,7 +813,7 @@ void intel_fbdev_output_poll_changed(struct drm_device *dev)
{
struct intel_fbdev *ifbdev = to_i915(dev)->fbdev;
- if (ifbdev && ifbdev->fb)
+ if (ifbdev && ifbdev->vma)
drm_fb_helper_hotplug_event(&ifbdev->helper);
}
@@ -824,7 +825,7 @@ void intel_fbdev_restore_mode(struct drm_device *dev)
return;
intel_fbdev_sync(ifbdev);
- if (!ifbdev->fb)
+ if (!ifbdev->vma)
return;
if (drm_fb_helper_restore_fbdev_mode_unlocked(&ifbdev->helper) == 0)
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/selftests/i915_gem_dmabuf.c
index d15cc9d3a5cd..89dc25a5a53b 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_dmabuf.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_dmabuf.c
@@ -246,9 +246,9 @@ static int igt_dmabuf_export_vmap(void *arg)
i915_gem_object_put(obj);
ptr = dma_buf_vmap(dmabuf);
- if (IS_ERR(ptr)) {
- err = PTR_ERR(ptr);
- pr_err("dma_buf_vmap failed with err=%d\n", err);
+ if (!ptr) {
+ pr_err("dma_buf_vmap failed\n");
+ err = -ENOMEM;
goto out;
}
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index bf2e5be1ab30..e37b55a23a65 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -1,4 +1,5 @@
-mediatek-drm-y := mtk_disp_ovl.o \
+mediatek-drm-y := mtk_disp_color.o \
+ mtk_disp_ovl.o \
mtk_disp_rdma.o \
mtk_drm_crtc.o \
mtk_drm_ddp.o \
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_color.c b/drivers/gpu/drm/mediatek/mtk_disp_color.c
new file mode 100644
index 000000000000..ef79a6d55646
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_disp_color.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2017 MediaTek Inc.
+ *
+ * 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.
+ */
+
+#include <drm/drmP.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#include "mtk_drm_crtc.h"
+#include "mtk_drm_ddp_comp.h"
+
+#define DISP_COLOR_CFG_MAIN 0x0400
+#define DISP_COLOR_START_MT2701 0x0f00
+#define DISP_COLOR_START_MT8173 0x0c00
+#define DISP_COLOR_START(comp) ((comp)->data->color_offset)
+#define DISP_COLOR_WIDTH(comp) (DISP_COLOR_START(comp) + 0x50)
+#define DISP_COLOR_HEIGHT(comp) (DISP_COLOR_START(comp) + 0x54)
+
+#define COLOR_BYPASS_ALL BIT(7)
+#define COLOR_SEQ_SEL BIT(13)
+
+struct mtk_disp_color_data {
+ unsigned int color_offset;
+};
+
+/**
+ * struct mtk_disp_color - DISP_COLOR driver structure
+ * @ddp_comp - structure containing type enum and hardware resources
+ * @crtc - associated crtc to report irq events to
+ */
+struct mtk_disp_color {
+ struct mtk_ddp_comp ddp_comp;
+ struct drm_crtc *crtc;
+ const struct mtk_disp_color_data *data;
+};
+
+static inline struct mtk_disp_color *comp_to_color(struct mtk_ddp_comp *comp)
+{
+ return container_of(comp, struct mtk_disp_color, ddp_comp);
+}
+
+static void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w,
+ unsigned int h, unsigned int vrefresh,
+ unsigned int bpc)
+{
+ struct mtk_disp_color *color = comp_to_color(comp);
+
+ writel(w, comp->regs + DISP_COLOR_WIDTH(color));
+ writel(h, comp->regs + DISP_COLOR_HEIGHT(color));
+}
+
+static void mtk_color_start(struct mtk_ddp_comp *comp)
+{
+ struct mtk_disp_color *color = comp_to_color(comp);
+
+ writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL,
+ comp->regs + DISP_COLOR_CFG_MAIN);
+ writel(0x1, comp->regs + DISP_COLOR_START(color));
+}
+
+static const struct mtk_ddp_comp_funcs mtk_disp_color_funcs = {
+ .config = mtk_color_config,
+ .start = mtk_color_start,
+};
+
+static int mtk_disp_color_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct mtk_disp_color *priv = dev_get_drvdata(dev);
+ struct drm_device *drm_dev = data;
+ int ret;
+
+ ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register component %s: %d\n",
+ dev->of_node->full_name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void mtk_disp_color_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct mtk_disp_color *priv = dev_get_drvdata(dev);
+ struct drm_device *drm_dev = data;
+
+ mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp);
+}
+
+static const struct component_ops mtk_disp_color_component_ops = {
+ .bind = mtk_disp_color_bind,
+ .unbind = mtk_disp_color_unbind,
+};
+
+static int mtk_disp_color_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mtk_disp_color *priv;
+ int comp_id;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_COLOR);
+ if (comp_id < 0) {
+ dev_err(dev, "Failed to identify by alias: %d\n", comp_id);
+ return comp_id;
+ }
+
+ ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id,
+ &mtk_disp_color_funcs);
+ if (ret) {
+ dev_err(dev, "Failed to initialize component: %d\n", ret);
+ return ret;
+ }
+
+ priv->data = of_device_get_match_data(dev);
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = component_add(dev, &mtk_disp_color_component_ops);
+ if (ret)
+ dev_err(dev, "Failed to add component: %d\n", ret);
+
+ return ret;
+}
+
+static int mtk_disp_color_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &mtk_disp_color_component_ops);
+
+ return 0;
+}
+
+static const struct mtk_disp_color_data mt2701_color_driver_data = {
+ .color_offset = DISP_COLOR_START_MT2701,
+};
+
+static const struct mtk_disp_color_data mt8173_color_driver_data = {
+ .color_offset = DISP_COLOR_START_MT8173,
+};
+
+static const struct of_device_id mtk_disp_color_driver_dt_match[] = {
+ { .compatible = "mediatek,mt2701-disp-color",
+ .data = &mt2701_color_driver_data},
+ { .compatible = "mediatek,mt8173-disp-color",
+ .data = &mt8173_color_driver_data},
+ {},
+};
+MODULE_DEVICE_TABLE(of, mtk_disp_color_driver_dt_match);
+
+struct platform_driver mtk_disp_color_driver = {
+ .probe = mtk_disp_color_probe,
+ .remove = mtk_disp_color_remove,
+ .driver = {
+ .name = "mediatek-disp-color",
+ .owner = THIS_MODULE,
+ .of_match_table = mtk_disp_color_driver_dt_match,
+ },
+};
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
index a14d7d64d7b1..35bc5babdbf7 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
@@ -42,9 +42,12 @@
#define OVL_RDMA_MEM_GMC 0x40402020
#define OVL_CON_BYTE_SWAP BIT(24)
+#define OVL_CON_MTX_YUV_TO_RGB (6 << 16)
#define OVL_CON_CLRFMT_RGB (1 << 12)
#define OVL_CON_CLRFMT_RGBA8888 (2 << 12)
#define OVL_CON_CLRFMT_ARGB8888 (3 << 12)
+#define OVL_CON_CLRFMT_UYVY (4 << 12)
+#define OVL_CON_CLRFMT_YUYV (5 << 12)
#define OVL_CON_CLRFMT_RGB565(ovl) ((ovl)->data->fmt_rgb565_is_0 ? \
0 : OVL_CON_CLRFMT_RGB)
#define OVL_CON_CLRFMT_RGB888(ovl) ((ovl)->data->fmt_rgb565_is_0 ? \
@@ -176,6 +179,10 @@ static unsigned int ovl_fmt_convert(struct mtk_disp_ovl *ovl, unsigned int fmt)
case DRM_FORMAT_XBGR8888:
case DRM_FORMAT_ABGR8888:
return OVL_CON_CLRFMT_RGBA8888 | OVL_CON_BYTE_SWAP;
+ case DRM_FORMAT_UYVY:
+ return OVL_CON_CLRFMT_UYVY | OVL_CON_MTX_YUV_TO_RGB;
+ case DRM_FORMAT_YUYV:
+ return OVL_CON_CLRFMT_YUYV | OVL_CON_MTX_YUV_TO_RGB;
}
}
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
index 6582e1f56d37..cb32c9369f3a 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -559,6 +559,8 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev,
mtk_crtc->ddp_comp = devm_kmalloc_array(dev, mtk_crtc->ddp_comp_nr,
sizeof(*mtk_crtc->ddp_comp),
GFP_KERNEL);
+ if (!mtk_crtc->ddp_comp)
+ return -ENOMEM;
mtk_crtc->mutex = mtk_disp_mutex_get(priv->mutex_dev, pipe);
if (IS_ERR(mtk_crtc->mutex)) {
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
index 8b52416b6e41..07d7ea2268ef 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -38,13 +38,6 @@
#define DISP_REG_UFO_START 0x0000
-#define DISP_COLOR_CFG_MAIN 0x0400
-#define DISP_COLOR_START_MT2701 0x0f00
-#define DISP_COLOR_START_MT8173 0x0c00
-#define DISP_COLOR_START(comp) ((comp)->data->color_offset)
-#define DISP_COLOR_WIDTH(comp) (DISP_COLOR_START(comp) + 0x50)
-#define DISP_COLOR_HEIGHT(comp) (DISP_COLOR_START(comp) + 0x54)
-
#define DISP_AAL_EN 0x0000
#define DISP_AAL_SIZE 0x0030
@@ -55,9 +48,6 @@
#define LUT_10BIT_MASK 0x03ff
-#define COLOR_BYPASS_ALL BIT(7)
-#define COLOR_SEQ_SEL BIT(13)
-
#define OD_RELAYMODE BIT(0)
#define UFO_BYPASS BIT(2)
@@ -82,20 +72,6 @@
#define DITHER_ADD_LSHIFT_G(x) (((x) & 0x7) << 4)
#define DITHER_ADD_RSHIFT_G(x) (((x) & 0x7) << 0)
-struct mtk_disp_color_data {
- unsigned int color_offset;
-};
-
-struct mtk_disp_color {
- struct mtk_ddp_comp ddp_comp;
- const struct mtk_disp_color_data *data;
-};
-
-static inline struct mtk_disp_color *comp_to_color(struct mtk_ddp_comp *comp)
-{
- return container_of(comp, struct mtk_disp_color, ddp_comp);
-}
-
void mtk_dither_set(struct mtk_ddp_comp *comp, unsigned int bpc,
unsigned int CFG)
{
@@ -119,25 +95,6 @@ void mtk_dither_set(struct mtk_ddp_comp *comp, unsigned int bpc,
}
}
-static void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w,
- unsigned int h, unsigned int vrefresh,
- unsigned int bpc)
-{
- struct mtk_disp_color *color = comp_to_color(comp);
-
- writel(w, comp->regs + DISP_COLOR_WIDTH(color));
- writel(h, comp->regs + DISP_COLOR_HEIGHT(color));
-}
-
-static void mtk_color_start(struct mtk_ddp_comp *comp)
-{
- struct mtk_disp_color *color = comp_to_color(comp);
-
- writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL,
- comp->regs + DISP_COLOR_CFG_MAIN);
- writel(0x1, comp->regs + DISP_COLOR_START(color));
-}
-
static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w,
unsigned int h, unsigned int vrefresh,
unsigned int bpc)
@@ -229,11 +186,6 @@ static const struct mtk_ddp_comp_funcs ddp_gamma = {
.stop = mtk_gamma_stop,
};
-static const struct mtk_ddp_comp_funcs ddp_color = {
- .config = mtk_color_config,
- .start = mtk_color_start,
-};
-
static const struct mtk_ddp_comp_funcs ddp_od = {
.config = mtk_od_config,
.start = mtk_od_start,
@@ -268,8 +220,8 @@ struct mtk_ddp_comp_match {
static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
[DDP_COMPONENT_AAL] = { MTK_DISP_AAL, 0, &ddp_aal },
[DDP_COMPONENT_BLS] = { MTK_DISP_BLS, 0, NULL },
- [DDP_COMPONENT_COLOR0] = { MTK_DISP_COLOR, 0, &ddp_color },
- [DDP_COMPONENT_COLOR1] = { MTK_DISP_COLOR, 1, &ddp_color },
+ [DDP_COMPONENT_COLOR0] = { MTK_DISP_COLOR, 0, NULL },
+ [DDP_COMPONENT_COLOR1] = { MTK_DISP_COLOR, 1, NULL },
[DDP_COMPONENT_DPI0] = { MTK_DPI, 0, NULL },
[DDP_COMPONENT_DSI0] = { MTK_DSI, 0, NULL },
[DDP_COMPONENT_DSI1] = { MTK_DSI, 1, NULL },
@@ -286,22 +238,6 @@ static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
[DDP_COMPONENT_WDMA1] = { MTK_DISP_WDMA, 1, NULL },
};
-static const struct mtk_disp_color_data mt2701_color_driver_data = {
- .color_offset = DISP_COLOR_START_MT2701,
-};
-
-static const struct mtk_disp_color_data mt8173_color_driver_data = {
- .color_offset = DISP_COLOR_START_MT8173,
-};
-
-static const struct of_device_id mtk_disp_color_driver_dt_match[] = {
- { .compatible = "mediatek,mt2701-disp-color",
- .data = &mt2701_color_driver_data},
- { .compatible = "mediatek,mt8173-disp-color",
- .data = &mt8173_color_driver_data},
- {},
-};
-
int mtk_ddp_comp_get_id(struct device_node *node,
enum mtk_ddp_comp_type comp_type)
{
@@ -324,23 +260,11 @@ int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
enum mtk_ddp_comp_type type;
struct device_node *larb_node;
struct platform_device *larb_pdev;
- const struct of_device_id *match;
- struct mtk_disp_color *color;
if (comp_id < 0 || comp_id >= DDP_COMPONENT_ID_MAX)
return -EINVAL;
type = mtk_ddp_matches[comp_id].type;
- if (type == MTK_DISP_COLOR) {
- devm_kfree(dev, comp);
- color = devm_kzalloc(dev, sizeof(*color), GFP_KERNEL);
- if (!color)
- return -ENOMEM;
-
- match = of_match_node(mtk_disp_color_driver_dt_match, node);
- color->data = match->data;
- comp = &color->ddp_comp;
- }
comp->id = comp_id;
comp->funcs = funcs ?: mtk_ddp_matches[comp_id].funcs;
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index f6c8ec4c7dbc..41d2cffe953e 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -439,11 +439,12 @@ static int mtk_drm_probe(struct platform_device *pdev)
private->comp_node[comp_id] = of_node_get(node);
/*
- * Currently only the OVL, RDMA, DSI, and DPI blocks have
+ * Currently only the COLOR, OVL, RDMA, DSI, and DPI blocks have
* separate component platform drivers and initialize their own
* DDP component structure. The others are initialized here.
*/
- if (comp_type == MTK_DISP_OVL ||
+ if (comp_type == MTK_DISP_COLOR ||
+ comp_type == MTK_DISP_OVL ||
comp_type == MTK_DISP_RDMA ||
comp_type == MTK_DSI ||
comp_type == MTK_DPI) {
@@ -566,6 +567,7 @@ static struct platform_driver mtk_drm_platform_driver = {
static struct platform_driver * const mtk_drm_drivers[] = {
&mtk_ddp_driver,
+ &mtk_disp_color_driver,
&mtk_disp_ovl_driver,
&mtk_disp_rdma_driver,
&mtk_dpi_driver,
@@ -576,33 +578,14 @@ static struct platform_driver * const mtk_drm_drivers[] = {
static int __init mtk_drm_init(void)
{
- int ret;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mtk_drm_drivers); i++) {
- ret = platform_driver_register(mtk_drm_drivers[i]);
- if (ret < 0) {
- pr_err("Failed to register %s driver: %d\n",
- mtk_drm_drivers[i]->driver.name, ret);
- goto err;
- }
- }
-
- return 0;
-
-err:
- while (--i >= 0)
- platform_driver_unregister(mtk_drm_drivers[i]);
-
- return ret;
+ return platform_register_drivers(mtk_drm_drivers,
+ ARRAY_SIZE(mtk_drm_drivers));
}
static void __exit mtk_drm_exit(void)
{
- int i;
-
- for (i = ARRAY_SIZE(mtk_drm_drivers) - 1; i >= 0; i--)
- platform_driver_unregister(mtk_drm_drivers[i]);
+ platform_unregister_drivers(mtk_drm_drivers,
+ ARRAY_SIZE(mtk_drm_drivers));
}
module_init(mtk_drm_init);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
index aef8747d810b..c3378c452c0a 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -59,6 +59,7 @@ struct mtk_drm_private {
};
extern struct platform_driver mtk_ddp_driver;
+extern struct platform_driver mtk_disp_color_driver;
extern struct platform_driver mtk_disp_ovl_driver;
extern struct platform_driver mtk_disp_rdma_driver;
extern struct platform_driver mtk_dpi_driver;
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.c b/drivers/gpu/drm/mediatek/mtk_drm_plane.c
index e405e89ed5e5..1a59b9ab4aa8 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_plane.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.c
@@ -28,6 +28,8 @@ static const u32 formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_RGB565,
+ DRM_FORMAT_UYVY,
+ DRM_FORMAT_YUYV,
};
static void mtk_plane_reset(struct drm_plane *plane)
diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
index b5cc6e12334c..97253c8f813b 100644
--- a/drivers/gpu/drm/mediatek/mtk_dsi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
@@ -930,7 +930,7 @@ static u32 mtk_dsi_recv_cnt(u8 type, u8 *read_data)
DRM_INFO("type is 0x02, try again\n");
break;
default:
- DRM_INFO("type(0x%x) cannot be non-recognite\n", type);
+ DRM_INFO("type(0x%x) not recognized\n", type);
break;
}
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
index 0a4ffd724146..71eb4fbbfc85 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
@@ -1778,33 +1778,14 @@ static struct platform_driver * const mtk_hdmi_drivers[] = {
static int __init mtk_hdmitx_init(void)
{
- int ret;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mtk_hdmi_drivers); i++) {
- ret = platform_driver_register(mtk_hdmi_drivers[i]);
- if (ret < 0) {
- pr_err("Failed to register %s driver: %d\n",
- mtk_hdmi_drivers[i]->driver.name, ret);
- goto err;
- }
- }
-
- return 0;
-
-err:
- while (--i >= 0)
- platform_driver_unregister(mtk_hdmi_drivers[i]);
-
- return ret;
+ return platform_register_drivers(mtk_hdmi_drivers,
+ ARRAY_SIZE(mtk_hdmi_drivers));
}
static void __exit mtk_hdmitx_exit(void)
{
- int i;
-
- for (i = ARRAY_SIZE(mtk_hdmi_drivers) - 1; i >= 0; i--)
- platform_driver_unregister(mtk_hdmi_drivers[i]);
+ platform_unregister_drivers(mtk_hdmi_drivers,
+ ARRAY_SIZE(mtk_hdmi_drivers));
}
module_init(mtk_hdmitx_init);
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c
index fa4f8f008e4d..e67ed383e11b 100644
--- a/drivers/gpu/drm/radeon/atombios_encoders.c
+++ b/drivers/gpu/drm/radeon/atombios_encoders.c
@@ -31,6 +31,7 @@
#include "radeon_asic.h"
#include "atom.h"
#include <linux/backlight.h>
+#include <linux/dmi.h>
extern int atom_debug;
@@ -2184,9 +2185,17 @@ int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder, int fe_idx)
goto assigned;
}
- /* on DCE32 and encoder can driver any block so just crtc id */
+ /*
+ * On DCE32 any encoder can drive any block so usually just use crtc id,
+ * but Apple thinks different at least on iMac10,1, so there use linkb,
+ * otherwise the internal eDP panel will stay dark.
+ */
if (ASIC_IS_DCE32(rdev)) {
- enc_idx = radeon_crtc->crtc_id;
+ if (dmi_match(DMI_PRODUCT_NAME, "iMac10,1"))
+ enc_idx = (dig->linkb) ? 1 : 0;
+ else
+ enc_idx = radeon_crtc->crtc_id;
+
goto assigned;
}
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index 14fa1f8351e8..9b0b0588bbed 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -1195,7 +1195,7 @@ static int cdn_dp_probe(struct platform_device *pdev)
continue;
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
- if (!dp)
+ if (!port)
return -ENOMEM;
port->extcon = extcon;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 47905faf5586..c7e96b82cf63 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -45,13 +45,13 @@ struct rockchip_crtc_state {
*
* @crtc: array of enabled CRTCs, used to map from "pipe" to drm_crtc.
* @num_pipe: number of pipes for this device.
+ * @mm_lock: protect drm_mm on multi-threads.
*/
struct rockchip_drm_private {
struct drm_fb_helper fbdev_helper;
struct drm_gem_object *fbdev_bo;
struct drm_atomic_state *state;
struct iommu_domain *domain;
- /* protect drm_mm on multi-threads */
struct mutex mm_lock;
struct drm_mm mm;
struct list_head psr_list;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
index df9e57064f19..b74ac717e56a 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -29,12 +29,11 @@ static int rockchip_gem_iommu_map(struct rockchip_gem_object *rk_obj)
ssize_t ret;
mutex_lock(&private->mm_lock);
-
ret = drm_mm_insert_node_generic(&private->mm, &rk_obj->mm,
rk_obj->base.size, PAGE_SIZE,
0, 0);
-
mutex_unlock(&private->mm_lock);
+
if (ret < 0) {
DRM_ERROR("out of I/O virtual memory: %zd\n", ret);
return ret;
@@ -56,7 +55,9 @@ static int rockchip_gem_iommu_map(struct rockchip_gem_object *rk_obj)
return 0;
err_remove_node:
+ mutex_lock(&private->mm_lock);
drm_mm_remove_node(&rk_obj->mm);
+ mutex_unlock(&private->mm_lock);
return ret;
}
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index e83a279f1217..5a2d71729b9a 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -155,6 +155,10 @@ config MTD_BCM47XX_PARTS
This provides partitions parser for devices based on BCM47xx
boards.
+menu "Partition parsers"
+source "drivers/mtd/parsers/Kconfig"
+endmenu
+
comment "User Modules And Translation Layers"
#
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 99bb9a1f6e16..151d60df303a 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
+obj-y += parsers/
# 'Users' - code which presents functionality to userspace.
obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o
diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c
index d10fa6c8f074..fe2581d9d882 100644
--- a/drivers/mtd/bcm47xxpart.c
+++ b/drivers/mtd/bcm47xxpart.c
@@ -43,7 +43,8 @@
#define ML_MAGIC2 0x26594131
#define TRX_MAGIC 0x30524448
#define SHSQ_MAGIC 0x71736873 /* shsq (weird ZTE H218N endianness) */
-#define UBI_EC_MAGIC 0x23494255 /* UBI# */
+
+static const char * const trx_types[] = { "trx", NULL };
struct trx_header {
uint32_t magic;
@@ -62,89 +63,6 @@ static void bcm47xxpart_add_part(struct mtd_partition *part, const char *name,
part->mask_flags = mask_flags;
}
-static const char *bcm47xxpart_trx_data_part_name(struct mtd_info *master,
- size_t offset)
-{
- uint32_t buf;
- size_t bytes_read;
- int err;
-
- err = mtd_read(master, offset, sizeof(buf), &bytes_read,
- (uint8_t *)&buf);
- if (err && !mtd_is_bitflip(err)) {
- pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
- offset, err);
- goto out_default;
- }
-
- if (buf == UBI_EC_MAGIC)
- return "ubi";
-
-out_default:
- return "rootfs";
-}
-
-static int bcm47xxpart_parse_trx(struct mtd_info *master,
- struct mtd_partition *trx,
- struct mtd_partition *parts,
- size_t parts_len)
-{
- struct trx_header header;
- size_t bytes_read;
- int curr_part = 0;
- int i, err;
-
- if (parts_len < 3) {
- pr_warn("No enough space to add TRX partitions!\n");
- return -ENOMEM;
- }
-
- err = mtd_read(master, trx->offset, sizeof(header), &bytes_read,
- (uint8_t *)&header);
- if (err && !mtd_is_bitflip(err)) {
- pr_err("mtd_read error while reading TRX header: %d\n", err);
- return err;
- }
-
- i = 0;
-
- /* We have LZMA loader if offset[2] points to sth */
- if (header.offset[2]) {
- bcm47xxpart_add_part(&parts[curr_part++], "loader",
- trx->offset + header.offset[i], 0);
- i++;
- }
-
- if (header.offset[i]) {
- bcm47xxpart_add_part(&parts[curr_part++], "linux",
- trx->offset + header.offset[i], 0);
- i++;
- }
-
- if (header.offset[i]) {
- size_t offset = trx->offset + header.offset[i];
- const char *name = bcm47xxpart_trx_data_part_name(master,
- offset);
-
- bcm47xxpart_add_part(&parts[curr_part++], name, offset, 0);
- i++;
- }
-
- /*
- * Assume that every partition ends at the beginning of the one it is
- * followed by.
- */
- for (i = 0; i < curr_part; i++) {
- u64 next_part_offset = (i < curr_part - 1) ?
- parts[i + 1].offset :
- trx->offset + trx->size;
-
- parts[i].size = next_part_offset - parts[i].offset;
- }
-
- return curr_part;
-}
-
/**
* bcm47xxpart_bootpartition - gets index of TRX partition used by bootloader
*
@@ -362,17 +280,10 @@ static int bcm47xxpart_parse(struct mtd_info *master,
for (i = 0; i < trx_num; i++) {
struct mtd_partition *trx = &parts[trx_parts[i]];
- if (i == bcm47xxpart_bootpartition()) {
- int num_parts;
-
- num_parts = bcm47xxpart_parse_trx(master, trx,
- parts + curr_part,
- BCM47XXPART_MAX_PARTS - curr_part);
- if (num_parts > 0)
- curr_part += num_parts;
- } else {
+ if (i == bcm47xxpart_bootpartition())
+ trx->types = trx_types;
+ else
trx->name = "failsafe";
- }
}
*pparts = parts;
diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c
index 94d3eb42c4d5..7d342965f392 100644
--- a/drivers/mtd/chips/cfi_cmdset_0020.c
+++ b/drivers/mtd/chips/cfi_cmdset_0020.c
@@ -666,7 +666,7 @@ cfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs,
size_t totlen = 0, thislen;
int ret = 0;
size_t buflen = 0;
- static char *buffer;
+ char *buffer;
if (!ECCBUF_SIZE) {
/* We should fall back to a general writev implementation.
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 58329d2dacd1..6def5445e03e 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -95,6 +95,16 @@ config MTD_M25P80
if you want to specify device partitioning or to use a device which
doesn't support the JEDEC ID instruction.
+config MTD_MCHP23K256
+ tristate "Microchip 23K256 SRAM"
+ depends on SPI_MASTER
+ help
+ This enables access to Microchip 23K256 SRAM chips, using SPI.
+
+ Set up your spi devices with the right board-specific
+ platform data, or a device tree description if you want to
+ specify device partitioning
+
config MTD_SPEAR_SMI
tristate "SPEAR MTD NOR Support through SMI controller"
depends on PLAT_SPEAR
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 7912d3a0ee34..f0f767624cc6 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_MTD_LART) += lart.o
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
obj-$(CONFIG_MTD_M25P80) += m25p80.o
+obj-$(CONFIG_MTD_MCHP23K256) += mchp23k256.o
obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o
obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index c4df3b1bded0..00eea6fd379c 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -78,11 +78,17 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
{
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
- struct spi_transfer t[2] = {};
+ unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
+ struct spi_transfer t[3] = {};
struct spi_message m;
int cmd_sz = m25p_cmdsz(nor);
ssize_t ret;
+ /* get transfer protocols. */
+ inst_nbits = spi_nor_get_protocol_inst_nbits(nor->write_proto);
+ addr_nbits = spi_nor_get_protocol_addr_nbits(nor->write_proto);
+ data_nbits = spi_nor_get_protocol_data_nbits(nor->write_proto);
+
spi_message_init(&m);
if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
@@ -92,12 +98,27 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
m25p_addr2cmd(nor, to, flash->command);
t[0].tx_buf = flash->command;
+ t[0].tx_nbits = inst_nbits;
t[0].len = cmd_sz;
spi_message_add_tail(&t[0], &m);
- t[1].tx_buf = buf;
- t[1].len = len;
- spi_message_add_tail(&t[1], &m);
+ /* split the op code and address bytes into two transfers if needed. */
+ data_idx = 1;
+ if (addr_nbits != inst_nbits) {
+ t[0].len = 1;
+
+ t[1].tx_buf = &flash->command[1];
+ t[1].tx_nbits = addr_nbits;
+ t[1].len = cmd_sz - 1;
+ spi_message_add_tail(&t[1], &m);
+
+ data_idx = 2;
+ }
+
+ t[data_idx].tx_buf = buf;
+ t[data_idx].tx_nbits = data_nbits;
+ t[data_idx].len = len;
+ spi_message_add_tail(&t[data_idx], &m);
ret = spi_sync(spi, &m);
if (ret)
@@ -109,18 +130,6 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
return ret;
}
-static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
-{
- switch (nor->flash_read) {
- case SPI_NOR_DUAL:
- return 2;
- case SPI_NOR_QUAD:
- return 4;
- default:
- return 0;
- }
-}
-
/*
* Read an address range from the nor chip. The address range
* may be any size provided it is within the physical boundaries.
@@ -130,13 +139,20 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
{
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
- struct spi_transfer t[2];
+ unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
+ struct spi_transfer t[3];
struct spi_message m;
unsigned int dummy = nor->read_dummy;
ssize_t ret;
+ int cmd_sz;
+
+ /* get transfer protocols. */
+ inst_nbits = spi_nor_get_protocol_inst_nbits(nor->read_proto);
+ addr_nbits = spi_nor_get_protocol_addr_nbits(nor->read_proto);
+ data_nbits = spi_nor_get_protocol_data_nbits(nor->read_proto);
/* convert the dummy cycles to the number of bytes */
- dummy /= 8;
+ dummy = (dummy * addr_nbits) / 8;
if (spi_flash_read_supported(spi)) {
struct spi_flash_read_message msg;
@@ -149,10 +165,9 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
msg.read_opcode = nor->read_opcode;
msg.addr_width = nor->addr_width;
msg.dummy_bytes = dummy;
- /* TODO: Support other combinations */
- msg.opcode_nbits = SPI_NBITS_SINGLE;
- msg.addr_nbits = SPI_NBITS_SINGLE;
- msg.data_nbits = m25p80_rx_nbits(nor);
+ msg.opcode_nbits = inst_nbits;
+ msg.addr_nbits = addr_nbits;
+ msg.data_nbits = data_nbits;
ret = spi_flash_read(spi, &msg);
if (ret < 0)
@@ -167,20 +182,45 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
m25p_addr2cmd(nor, from, flash->command);
t[0].tx_buf = flash->command;
+ t[0].tx_nbits = inst_nbits;
t[0].len = m25p_cmdsz(nor) + dummy;
spi_message_add_tail(&t[0], &m);
- t[1].rx_buf = buf;
- t[1].rx_nbits = m25p80_rx_nbits(nor);
- t[1].len = min3(len, spi_max_transfer_size(spi),
- spi_max_message_size(spi) - t[0].len);
- spi_message_add_tail(&t[1], &m);
+ /*
+ * Set all dummy/mode cycle bits to avoid sending some manufacturer
+ * specific pattern, which might make the memory enter its Continuous
+ * Read mode by mistake.
+ * Based on the different mode cycle bit patterns listed and described
+ * in the JESD216B specification, the 0xff value works for all memories
+ * and all manufacturers.
+ */
+ cmd_sz = t[0].len;
+ memset(flash->command + cmd_sz - dummy, 0xff, dummy);
+
+ /* split the op code and address bytes into two transfers if needed. */
+ data_idx = 1;
+ if (addr_nbits != inst_nbits) {
+ t[0].len = 1;
+
+ t[1].tx_buf = &flash->command[1];
+ t[1].tx_nbits = addr_nbits;
+ t[1].len = cmd_sz - 1;
+ spi_message_add_tail(&t[1], &m);
+
+ data_idx = 2;
+ }
+
+ t[data_idx].rx_buf = buf;
+ t[data_idx].rx_nbits = data_nbits;
+ t[data_idx].len = min3(len, spi_max_transfer_size(spi),
+ spi_max_message_size(spi) - cmd_sz);
+ spi_message_add_tail(&t[data_idx], &m);
ret = spi_sync(spi, &m);
if (ret)
return ret;
- ret = m.actual_length - m25p_cmdsz(nor) - dummy;
+ ret = m.actual_length - cmd_sz;
if (ret < 0)
return -EIO;
return ret;
@@ -196,7 +236,11 @@ static int m25p_probe(struct spi_device *spi)
struct flash_platform_data *data;
struct m25p *flash;
struct spi_nor *nor;
- enum read_mode mode = SPI_NOR_NORMAL;
+ struct spi_nor_hwcaps hwcaps = {
+ .mask = SNOR_HWCAPS_READ |
+ SNOR_HWCAPS_READ_FAST |
+ SNOR_HWCAPS_PP,
+ };
char *flash_name;
int ret;
@@ -221,10 +265,19 @@ static int m25p_probe(struct spi_device *spi)
spi_set_drvdata(spi, flash);
flash->spi = spi;
- if (spi->mode & SPI_RX_QUAD)
- mode = SPI_NOR_QUAD;
- else if (spi->mode & SPI_RX_DUAL)
- mode = SPI_NOR_DUAL;
+ if (spi->mode & SPI_RX_QUAD) {
+ hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
+
+ if (spi->mode & SPI_TX_QUAD)
+ hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 |
+ SNOR_HWCAPS_PP_1_1_4 |
+ SNOR_HWCAPS_PP_1_4_4);
+ } else if (spi->mode & SPI_RX_DUAL) {
+ hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
+
+ if (spi->mode & SPI_TX_DUAL)
+ hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2;
+ }
if (data && data->name)
nor->mtd.name = data->name;
@@ -241,7 +294,7 @@ static int m25p_probe(struct spi_device *spi)
else
flash_name = spi->modalias;
- ret = spi_nor_scan(nor, flash_name, mode);
+ ret = spi_nor_scan(nor, flash_name, &hwcaps);
if (ret)
return ret;
diff --git a/drivers/mtd/devices/mchp23k256.c b/drivers/mtd/devices/mchp23k256.c
new file mode 100644
index 000000000000..8956b7dcc984
--- /dev/null
+++ b/drivers/mtd/devices/mchp23k256.c
@@ -0,0 +1,236 @@
+/*
+ * mchp23k256.c
+ *
+ * Driver for Microchip 23k256 SPI RAM chips
+ *
+ * Copyright © 2016 Andrew Lunn <andrew@lunn.ch>
+ *
+ * This code 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.
+ *
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+#include <linux/spi/flash.h>
+#include <linux/spi/spi.h>
+#include <linux/of_device.h>
+
+#define MAX_CMD_SIZE 4
+
+struct mchp23_caps {
+ u8 addr_width;
+ unsigned int size;
+};
+
+struct mchp23k256_flash {
+ struct spi_device *spi;
+ struct mutex lock;
+ struct mtd_info mtd;
+ const struct mchp23_caps *caps;
+};
+
+#define MCHP23K256_CMD_WRITE_STATUS 0x01
+#define MCHP23K256_CMD_WRITE 0x02
+#define MCHP23K256_CMD_READ 0x03
+#define MCHP23K256_MODE_SEQ BIT(6)
+
+#define to_mchp23k256_flash(x) container_of(x, struct mchp23k256_flash, mtd)
+
+static void mchp23k256_addr2cmd(struct mchp23k256_flash *flash,
+ unsigned int addr, u8 *cmd)
+{
+ int i;
+
+ /*
+ * Address is sent in big endian (MSB first) and we skip
+ * the first entry of the cmd array which contains the cmd
+ * opcode.
+ */
+ for (i = flash->caps->addr_width; i > 0; i--, addr >>= 8)
+ cmd[i] = addr;
+}
+
+static int mchp23k256_cmdsz(struct mchp23k256_flash *flash)
+{
+ return 1 + flash->caps->addr_width;
+}
+
+static int mchp23k256_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const unsigned char *buf)
+{
+ struct mchp23k256_flash *flash = to_mchp23k256_flash(mtd);
+ struct spi_transfer transfer[2] = {};
+ struct spi_message message;
+ unsigned char command[MAX_CMD_SIZE];
+
+ spi_message_init(&message);
+
+ command[0] = MCHP23K256_CMD_WRITE;
+ mchp23k256_addr2cmd(flash, to, command);
+
+ transfer[0].tx_buf = command;
+ transfer[0].len = mchp23k256_cmdsz(flash);
+ spi_message_add_tail(&transfer[0], &message);
+
+ transfer[1].tx_buf = buf;
+ transfer[1].len = len;
+ spi_message_add_tail(&transfer[1], &message);
+
+ mutex_lock(&flash->lock);
+
+ spi_sync(flash->spi, &message);
+
+ if (retlen && message.actual_length > sizeof(command))
+ *retlen += message.actual_length - sizeof(command);
+
+ mutex_unlock(&flash->lock);
+ return 0;
+}
+
+static int mchp23k256_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, unsigned char *buf)
+{
+ struct mchp23k256_flash *flash = to_mchp23k256_flash(mtd);
+ struct spi_transfer transfer[2] = {};
+ struct spi_message message;
+ unsigned char command[MAX_CMD_SIZE];
+
+ spi_message_init(&message);
+
+ memset(&transfer, 0, sizeof(transfer));
+ command[0] = MCHP23K256_CMD_READ;
+ mchp23k256_addr2cmd(flash, from, command);
+
+ transfer[0].tx_buf = command;
+ transfer[0].len = mchp23k256_cmdsz(flash);
+ spi_message_add_tail(&transfer[0], &message);
+
+ transfer[1].rx_buf = buf;
+ transfer[1].len = len;
+ spi_message_add_tail(&transfer[1], &message);
+
+ mutex_lock(&flash->lock);
+
+ spi_sync(flash->spi, &message);
+
+ if (retlen && message.actual_length > sizeof(command))
+ *retlen += message.actual_length - sizeof(command);
+
+ mutex_unlock(&flash->lock);
+ return 0;
+}
+
+/*
+ * Set the device into sequential mode. This allows read/writes to the
+ * entire SRAM in a single operation
+ */
+static int mchp23k256_set_mode(struct spi_device *spi)
+{
+ struct spi_transfer transfer = {};
+ struct spi_message message;
+ unsigned char command[2];
+
+ spi_message_init(&message);
+
+ command[0] = MCHP23K256_CMD_WRITE_STATUS;
+ command[1] = MCHP23K256_MODE_SEQ;
+
+ transfer.tx_buf = command;
+ transfer.len = sizeof(command);
+ spi_message_add_tail(&transfer, &message);
+
+ return spi_sync(spi, &message);
+}
+
+static const struct mchp23_caps mchp23k256_caps = {
+ .size = SZ_32K,
+ .addr_width = 2,
+};
+
+static const struct mchp23_caps mchp23lcv1024_caps = {
+ .size = SZ_128K,
+ .addr_width = 3,
+};
+
+static int mchp23k256_probe(struct spi_device *spi)
+{
+ struct mchp23k256_flash *flash;
+ struct flash_platform_data *data;
+ int err;
+
+ flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
+ if (!flash)
+ return -ENOMEM;
+
+ flash->spi = spi;
+ mutex_init(&flash->lock);
+ spi_set_drvdata(spi, flash);
+
+ err = mchp23k256_set_mode(spi);
+ if (err)
+ return err;
+
+ data = dev_get_platdata(&spi->dev);
+
+ flash->caps = of_device_get_match_data(&spi->dev);
+ if (!flash->caps)
+ flash->caps = &mchp23k256_caps;
+
+ mtd_set_of_node(&flash->mtd, spi->dev.of_node);
+ flash->mtd.dev.parent = &spi->dev;
+ flash->mtd.type = MTD_RAM;
+ flash->mtd.flags = MTD_CAP_RAM;
+ flash->mtd.writesize = 1;
+ flash->mtd.size = flash->caps->size;
+ flash->mtd._read = mchp23k256_read;
+ flash->mtd._write = mchp23k256_write;
+
+ err = mtd_device_register(&flash->mtd, data ? data->parts : NULL,
+ data ? data->nr_parts : 0);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int mchp23k256_remove(struct spi_device *spi)
+{
+ struct mchp23k256_flash *flash = spi_get_drvdata(spi);
+
+ return mtd_device_unregister(&flash->mtd);
+}
+
+static const struct of_device_id mchp23k256_of_table[] = {
+ {
+ .compatible = "microchip,mchp23k256",
+ .data = &mchp23k256_caps,
+ },
+ {
+ .compatible = "microchip,mchp23lcv1024",
+ .data = &mchp23lcv1024_caps,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mchp23k256_of_table);
+
+static struct spi_driver mchp23k256_driver = {
+ .driver = {
+ .name = "mchp23k256",
+ .of_match_table = of_match_ptr(mchp23k256_of_table),
+ },
+ .probe = mchp23k256_probe,
+ .remove = mchp23k256_remove,
+};
+
+module_spi_driver(mchp23k256_driver);
+
+MODULE_DESCRIPTION("MTD SPI driver for MCHP23K256 RAM chips");
+MODULE_AUTHOR("Andrew Lunn <andre@lunn.ch>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:mchp23k256");
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index f9e9bd1cfaa0..5dc8bd042cc5 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -82,9 +82,13 @@
#define OP_WRITE_SECURITY_REVC 0x9A
#define OP_WRITE_SECURITY 0x9B /* revision D */
+#define CFI_MFR_ATMEL 0x1F
+
+#define DATAFLASH_SHIFT_EXTID 24
+#define DATAFLASH_SHIFT_ID 40
struct dataflash {
- uint8_t command[4];
+ u8 command[4];
char name[24];
unsigned short page_offset; /* offset in flash address */
@@ -129,8 +133,7 @@ static int dataflash_waitready(struct spi_device *spi)
for (;;) {
status = dataflash_status(spi);
if (status < 0) {
- pr_debug("%s: status %d?\n",
- dev_name(&spi->dev), status);
+ dev_dbg(&spi->dev, "status %d?\n", status);
status = 0;
}
@@ -153,12 +156,11 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
struct spi_transfer x = { };
struct spi_message msg;
unsigned blocksize = priv->page_size << 3;
- uint8_t *command;
- uint32_t rem;
+ u8 *command;
+ u32 rem;
- pr_debug("%s: erase addr=0x%llx len 0x%llx\n",
- dev_name(&spi->dev), (long long)instr->addr,
- (long long)instr->len);
+ dev_dbg(&spi->dev, "erase addr=0x%llx len 0x%llx\n",
+ (long long)instr->addr, (long long)instr->len);
div_u64_rem(instr->len, priv->page_size, &rem);
if (rem)
@@ -187,11 +189,11 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
pageaddr = pageaddr << priv->page_offset;
command[0] = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE;
- command[1] = (uint8_t)(pageaddr >> 16);
- command[2] = (uint8_t)(pageaddr >> 8);
+ command[1] = (u8)(pageaddr >> 16);
+ command[2] = (u8)(pageaddr >> 8);
command[3] = 0;
- pr_debug("ERASE %s: (%x) %x %x %x [%i]\n",
+ dev_dbg(&spi->dev, "ERASE %s: (%x) %x %x %x [%i]\n",
do_block ? "block" : "page",
command[0], command[1], command[2], command[3],
pageaddr);
@@ -200,8 +202,8 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
(void) dataflash_waitready(spi);
if (status < 0) {
- printk(KERN_ERR "%s: erase %x, err %d\n",
- dev_name(&spi->dev), pageaddr, status);
+ dev_err(&spi->dev, "erase %x, err %d\n",
+ pageaddr, status);
/* REVISIT: can retry instr->retries times; or
* giveup and instr->fail_addr = instr->addr;
*/
@@ -239,11 +241,11 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
struct spi_transfer x[2] = { };
struct spi_message msg;
unsigned int addr;
- uint8_t *command;
+ u8 *command;
int status;
- pr_debug("%s: read 0x%x..0x%x\n", dev_name(&priv->spi->dev),
- (unsigned)from, (unsigned)(from + len));
+ dev_dbg(&priv->spi->dev, "read 0x%x..0x%x\n",
+ (unsigned int)from, (unsigned int)(from + len));
/* Calculate flash page/byte address */
addr = (((unsigned)from / priv->page_size) << priv->page_offset)
@@ -251,7 +253,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
command = priv->command;
- pr_debug("READ: (%x) %x %x %x\n",
+ dev_dbg(&priv->spi->dev, "READ: (%x) %x %x %x\n",
command[0], command[1], command[2], command[3]);
spi_message_init(&msg);
@@ -271,9 +273,9 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
* fewer "don't care" bytes. Both buffers stay unchanged.
*/
command[0] = OP_READ_CONTINUOUS;
- command[1] = (uint8_t)(addr >> 16);
- command[2] = (uint8_t)(addr >> 8);
- command[3] = (uint8_t)(addr >> 0);
+ command[1] = (u8)(addr >> 16);
+ command[2] = (u8)(addr >> 8);
+ command[3] = (u8)(addr >> 0);
/* plus 4 "don't care" bytes */
status = spi_sync(priv->spi, &msg);
@@ -283,8 +285,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
*retlen = msg.actual_length - 8;
status = 0;
} else
- pr_debug("%s: read %x..%x --> %d\n",
- dev_name(&priv->spi->dev),
+ dev_dbg(&priv->spi->dev, "read %x..%x --> %d\n",
(unsigned)from, (unsigned)(from + len),
status);
return status;
@@ -308,10 +309,10 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t remaining = len;
u_char *writebuf = (u_char *) buf;
int status = -EINVAL;
- uint8_t *command;
+ u8 *command;
- pr_debug("%s: write 0x%x..0x%x\n",
- dev_name(&spi->dev), (unsigned)to, (unsigned)(to + len));
+ dev_dbg(&spi->dev, "write 0x%x..0x%x\n",
+ (unsigned int)to, (unsigned int)(to + len));
spi_message_init(&msg);
@@ -328,7 +329,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
mutex_lock(&priv->lock);
while (remaining > 0) {
- pr_debug("write @ %i:%i len=%i\n",
+ dev_dbg(&spi->dev, "write @ %i:%i len=%i\n",
pageaddr, offset, writelen);
/* REVISIT:
@@ -356,13 +357,13 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
command[2] = (addr & 0x0000FF00) >> 8;
command[3] = 0;
- pr_debug("TRANSFER: (%x) %x %x %x\n",
+ dev_dbg(&spi->dev, "TRANSFER: (%x) %x %x %x\n",
command[0], command[1], command[2], command[3]);
status = spi_sync(spi, &msg);
if (status < 0)
- pr_debug("%s: xfer %u -> %d\n",
- dev_name(&spi->dev), addr, status);
+ dev_dbg(&spi->dev, "xfer %u -> %d\n",
+ addr, status);
(void) dataflash_waitready(priv->spi);
}
@@ -374,7 +375,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
command[2] = (addr & 0x0000FF00) >> 8;
command[3] = (addr & 0x000000FF);
- pr_debug("PROGRAM: (%x) %x %x %x\n",
+ dev_dbg(&spi->dev, "PROGRAM: (%x) %x %x %x\n",
command[0], command[1], command[2], command[3]);
x[1].tx_buf = writebuf;
@@ -383,8 +384,8 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
status = spi_sync(spi, &msg);
spi_transfer_del(x + 1);
if (status < 0)
- pr_debug("%s: pgm %u/%u -> %d\n",
- dev_name(&spi->dev), addr, writelen, status);
+ dev_dbg(&spi->dev, "pgm %u/%u -> %d\n",
+ addr, writelen, status);
(void) dataflash_waitready(priv->spi);
@@ -398,20 +399,20 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
command[2] = (addr & 0x0000FF00) >> 8;
command[3] = 0;
- pr_debug("COMPARE: (%x) %x %x %x\n",
+ dev_dbg(&spi->dev, "COMPARE: (%x) %x %x %x\n",
command[0], command[1], command[2], command[3]);
status = spi_sync(spi, &msg);
if (status < 0)
- pr_debug("%s: compare %u -> %d\n",
- dev_name(&spi->dev), addr, status);
+ dev_dbg(&spi->dev, "compare %u -> %d\n",
+ addr, status);
status = dataflash_waitready(priv->spi);
/* Check result of the compare operation */
if (status & (1 << 6)) {
- printk(KERN_ERR "%s: compare page %u, err %d\n",
- dev_name(&spi->dev), pageaddr, status);
+ dev_err(&spi->dev, "compare page %u, err %d\n",
+ pageaddr, status);
remaining = 0;
status = -EIO;
break;
@@ -455,11 +456,11 @@ static int dataflash_get_otp_info(struct mtd_info *mtd, size_t len,
}
static ssize_t otp_read(struct spi_device *spi, unsigned base,
- uint8_t *buf, loff_t off, size_t len)
+ u8 *buf, loff_t off, size_t len)
{
struct spi_message m;
size_t l;
- uint8_t *scratch;
+ u8 *scratch;
struct spi_transfer t;
int status;
@@ -538,7 +539,7 @@ static int dataflash_write_user_otp(struct mtd_info *mtd,
{
struct spi_message m;
const size_t l = 4 + 64;
- uint8_t *scratch;
+ u8 *scratch;
struct spi_transfer t;
struct dataflash *priv = mtd->priv;
int status;
@@ -689,14 +690,15 @@ struct flash_info {
/* JEDEC id has a high byte of zero plus three data bytes:
* the manufacturer id, then a two byte device id.
*/
- uint32_t jedec_id;
+ u64 jedec_id;
/* The size listed here is what works with OP_ERASE_PAGE. */
unsigned nr_pages;
- uint16_t pagesize;
- uint16_t pageoffset;
+ u16 pagesize;
+ u16 pageoffset;
- uint16_t flags;
+ u16 flags;
+#define SUP_EXTID 0x0004 /* supports extended ID data */
#define SUP_POW2PS 0x0002 /* supports 2^N byte pages */
#define IS_POW2PS 0x0001 /* uses 2^N byte pages */
};
@@ -734,54 +736,32 @@ 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},
};
-static struct flash_info *jedec_probe(struct spi_device *spi)
+static struct flash_info *jedec_lookup(struct spi_device *spi,
+ u64 jedec, bool use_extid)
{
- int tmp;
- uint8_t code = OP_READ_ID;
- uint8_t id[3];
- uint32_t jedec;
- struct flash_info *info;
+ struct flash_info *info;
int status;
- /* JEDEC also defines an optional "extended device information"
- * string for after vendor-specific data, after the three bytes
- * we use here. Supporting some chips might require using it.
- *
- * If the vendor ID isn't Atmel's (0x1f), assume this call failed.
- * That's not an error; only rev C and newer chips handle it, and
- * only Atmel sells these chips.
- */
- tmp = spi_write_then_read(spi, &code, 1, id, 3);
- if (tmp < 0) {
- pr_debug("%s: error %d reading JEDEC ID\n",
- dev_name(&spi->dev), tmp);
- return ERR_PTR(tmp);
- }
- if (id[0] != 0x1f)
- return NULL;
-
- jedec = id[0];
- jedec = jedec << 8;
- jedec |= id[1];
- jedec = jedec << 8;
- jedec |= id[2];
+ for (info = dataflash_data;
+ info < dataflash_data + ARRAY_SIZE(dataflash_data);
+ info++) {
+ if (use_extid && !(info->flags & SUP_EXTID))
+ continue;
- for (tmp = 0, info = dataflash_data;
- tmp < ARRAY_SIZE(dataflash_data);
- tmp++, info++) {
if (info->jedec_id == jedec) {
- pr_debug("%s: OTP, sector protect%s\n",
- dev_name(&spi->dev),
- (info->flags & SUP_POW2PS)
- ? ", binary pagesize" : ""
- );
+ dev_dbg(&spi->dev, "OTP, sector protect%s\n",
+ (info->flags & SUP_POW2PS) ?
+ ", binary pagesize" : "");
if (info->flags & SUP_POW2PS) {
status = dataflash_status(spi);
if (status < 0) {
- pr_debug("%s: status error %d\n",
- dev_name(&spi->dev), status);
+ dev_dbg(&spi->dev, "status error %d\n",
+ status);
return ERR_PTR(status);
}
if (status & 0x1) {
@@ -796,12 +776,58 @@ static struct flash_info *jedec_probe(struct spi_device *spi)
}
}
+ return ERR_PTR(-ENODEV);
+}
+
+static struct flash_info *jedec_probe(struct spi_device *spi)
+{
+ int ret;
+ u8 code = OP_READ_ID;
+ u64 jedec;
+ u8 id[sizeof(jedec)] = {0};
+ const unsigned int id_size = 5;
+ struct flash_info *info;
+
+ /*
+ * JEDEC also defines an optional "extended device information"
+ * string for after vendor-specific data, after the three bytes
+ * we use here. Supporting some chips might require using it.
+ *
+ * If the vendor ID isn't Atmel's (0x1f), assume this call failed.
+ * That's not an error; only rev C and newer chips handle it, and
+ * only Atmel sells these chips.
+ */
+ ret = spi_write_then_read(spi, &code, 1, id, id_size);
+ if (ret < 0) {
+ dev_dbg(&spi->dev, "error %d reading JEDEC ID\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ if (id[0] != CFI_MFR_ATMEL)
+ return NULL;
+
+ jedec = be64_to_cpup((__be64 *)id);
+
+ /*
+ * First, try to match device using extended device
+ * information
+ */
+ info = jedec_lookup(spi, jedec >> DATAFLASH_SHIFT_EXTID, true);
+ if (!IS_ERR(info))
+ return info;
+ /*
+ * If that fails, make another pass using regular ID
+ * information
+ */
+ info = jedec_lookup(spi, jedec >> DATAFLASH_SHIFT_ID, false);
+ if (!IS_ERR(info))
+ return info;
/*
* Treat other chips as errors ... we won't know the right page
* size (it might be binary) even when we can tell which density
* class is involved (legacy chip id scheme).
*/
- dev_warn(&spi->dev, "JEDEC id %06x not handled\n", jedec);
+ dev_warn(&spi->dev, "JEDEC id %016llx not handled\n", jedec);
return ERR_PTR(-ENODEV);
}
@@ -845,8 +871,7 @@ static int dataflash_probe(struct spi_device *spi)
*/
status = dataflash_status(spi);
if (status <= 0 || status == 0xff) {
- pr_debug("%s: status error %d\n",
- dev_name(&spi->dev), status);
+ dev_dbg(&spi->dev, "status error %d\n", status);
if (status == 0 || status == 0xff)
status = -ENODEV;
return status;
@@ -887,8 +912,7 @@ static int dataflash_probe(struct spi_device *spi)
}
if (status < 0)
- pr_debug("%s: add_dataflash --> %d\n", dev_name(&spi->dev),
- status);
+ dev_dbg(&spi->dev, "add_dataflash --> %d\n", status);
return status;
}
@@ -898,7 +922,7 @@ static int dataflash_remove(struct spi_device *spi)
struct dataflash *flash = spi_get_drvdata(spi);
int status;
- pr_debug("%s: remove\n", dev_name(&spi->dev));
+ dev_dbg(&spi->dev, "remove\n");
status = mtd_device_unregister(&flash->mtd);
if (status == 0)
diff --git a/drivers/mtd/devices/serial_flash_cmds.h b/drivers/mtd/devices/serial_flash_cmds.h
index 8b81e15105dd..eba125c9f23f 100644
--- a/drivers/mtd/devices/serial_flash_cmds.h
+++ b/drivers/mtd/devices/serial_flash_cmds.h
@@ -13,7 +13,6 @@
#define _MTD_SERIAL_FLASH_CMDS_H
/* Generic Flash Commands/OPCODEs */
-#define SPINOR_OP_RDSR2 0x35
#define SPINOR_OP_WRVCR 0x81
#define SPINOR_OP_RDVCR 0x85
diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c
index 804313a33f2b..21afd94cd904 100644
--- a/drivers/mtd/devices/st_spi_fsm.c
+++ b/drivers/mtd/devices/st_spi_fsm.c
@@ -1445,7 +1445,7 @@ static int stfsm_s25fl_config(struct stfsm *fsm)
}
/* Check status of 'QE' bit, update if required. */
- stfsm_read_status(fsm, SPINOR_OP_RDSR2, &cr1, 1);
+ stfsm_read_status(fsm, SPINOR_OP_RDCR, &cr1, 1);
data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
if (data_pads == 4) {
if (!(cr1 & STFSM_S25FL_CONFIG_QE)) {
@@ -1490,7 +1490,7 @@ static int stfsm_w25q_config(struct stfsm *fsm)
return ret;
/* Check status of 'QE' bit, update if required. */
- stfsm_read_status(fsm, SPINOR_OP_RDSR2, &sr2, 1);
+ stfsm_read_status(fsm, SPINOR_OP_RDCR, &sr2, 1);
data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
if (data_pads == 4) {
if (!(sr2 & W25Q_STATUS_QE)) {
diff --git a/drivers/mtd/maps/physmap_of_gemini.c b/drivers/mtd/maps/physmap_of_gemini.c
index 9d371cd728ea..05b286b5289f 100644
--- a/drivers/mtd/maps/physmap_of_gemini.c
+++ b/drivers/mtd/maps/physmap_of_gemini.c
@@ -59,7 +59,7 @@ int of_flash_probe_gemini(struct platform_device *pdev,
struct device_node *np,
struct map_info *map)
{
- static struct regmap *rmap;
+ struct regmap *rmap;
struct device *dev = &pdev->dev;
u32 val;
int ret;
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 1517da3ddd7d..956382cea256 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -991,7 +991,7 @@ EXPORT_SYMBOL_GPL(mtd_point);
/* We probably shouldn't allow XIP if the unpoint isn't a NULL */
int mtd_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
{
- if (!mtd->_point)
+ if (!mtd->_unpoint)
return -EOPNOTSUPP;
if (from < 0 || from >= mtd->size || len > mtd->size - from)
return -EINVAL;
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index ea5e5307f667..5736b0c90b33 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -37,10 +37,16 @@
static LIST_HEAD(mtd_partitions);
static DEFINE_MUTEX(mtd_partitions_mutex);
-/* Our partition node structure */
+/**
+ * struct mtd_part - our partition node structure
+ *
+ * @mtd: struct holding partition details
+ * @parent: parent mtd - flash device or another partition
+ * @offset: partition offset relative to the *flash device*
+ */
struct mtd_part {
struct mtd_info mtd;
- struct mtd_info *master;
+ struct mtd_info *parent;
uint64_t offset;
struct list_head list;
};
@@ -67,15 +73,15 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
struct mtd_ecc_stats stats;
int res;
- stats = part->master->ecc_stats;
- res = part->master->_read(part->master, from + part->offset, len,
+ stats = part->parent->ecc_stats;
+ res = part->parent->_read(part->parent, from + part->offset, len,
retlen, buf);
if (unlikely(mtd_is_eccerr(res)))
mtd->ecc_stats.failed +=
- part->master->ecc_stats.failed - stats.failed;
+ part->parent->ecc_stats.failed - stats.failed;
else
mtd->ecc_stats.corrected +=
- part->master->ecc_stats.corrected - stats.corrected;
+ part->parent->ecc_stats.corrected - stats.corrected;
return res;
}
@@ -84,7 +90,7 @@ static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_point(part->master, from + part->offset, len,
+ return part->parent->_point(part->parent, from + part->offset, len,
retlen, virt, phys);
}
@@ -92,7 +98,7 @@ static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_unpoint(part->master, from + part->offset, len);
+ return part->parent->_unpoint(part->parent, from + part->offset, len);
}
static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
@@ -103,7 +109,7 @@ static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
struct mtd_part *part = mtd_to_part(mtd);
offset += part->offset;
- return part->master->_get_unmapped_area(part->master, len, offset,
+ return part->parent->_get_unmapped_area(part->parent, len, offset,
flags);
}
@@ -132,7 +138,7 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,
return -EINVAL;
}
- res = part->master->_read_oob(part->master, from + part->offset, ops);
+ res = part->parent->_read_oob(part->parent, from + part->offset, ops);
if (unlikely(res)) {
if (mtd_is_bitflip(res))
mtd->ecc_stats.corrected++;
@@ -146,7 +152,7 @@ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_read_user_prot_reg(part->master, from, len,
+ return part->parent->_read_user_prot_reg(part->parent, from, len,
retlen, buf);
}
@@ -154,7 +160,7 @@ static int part_get_user_prot_info(struct mtd_info *mtd, size_t len,
size_t *retlen, struct otp_info *buf)
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_get_user_prot_info(part->master, len, retlen,
+ return part->parent->_get_user_prot_info(part->parent, len, retlen,
buf);
}
@@ -162,7 +168,7 @@ static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_read_fact_prot_reg(part->master, from, len,
+ return part->parent->_read_fact_prot_reg(part->parent, from, len,
retlen, buf);
}
@@ -170,7 +176,7 @@ static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len,
size_t *retlen, struct otp_info *buf)
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_get_fact_prot_info(part->master, len, retlen,
+ return part->parent->_get_fact_prot_info(part->parent, len, retlen,
buf);
}
@@ -178,7 +184,7 @@ static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_write(part->master, to + part->offset, len,
+ return part->parent->_write(part->parent, to + part->offset, len,
retlen, buf);
}
@@ -186,7 +192,7 @@ static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_panic_write(part->master, to + part->offset, len,
+ return part->parent->_panic_write(part->parent, to + part->offset, len,
retlen, buf);
}
@@ -199,14 +205,14 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to,
return -EINVAL;
if (ops->datbuf && to + ops->len > mtd->size)
return -EINVAL;
- return part->master->_write_oob(part->master, to + part->offset, ops);
+ return part->parent->_write_oob(part->parent, to + part->offset, ops);
}
static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_write_user_prot_reg(part->master, from, len,
+ return part->parent->_write_user_prot_reg(part->parent, from, len,
retlen, buf);
}
@@ -214,14 +220,14 @@ static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len)
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_lock_user_prot_reg(part->master, from, len);
+ return part->parent->_lock_user_prot_reg(part->parent, from, len);
}
static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
unsigned long count, loff_t to, size_t *retlen)
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_writev(part->master, vecs, count,
+ return part->parent->_writev(part->parent, vecs, count,
to + part->offset, retlen);
}
@@ -231,7 +237,7 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
int ret;
instr->addr += part->offset;
- ret = part->master->_erase(part->master, instr);
+ ret = part->parent->_erase(part->parent, instr);
if (ret) {
if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
instr->fail_addr -= part->offset;
@@ -257,51 +263,51 @@ EXPORT_SYMBOL_GPL(mtd_erase_callback);
static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_lock(part->master, ofs + part->offset, len);
+ return part->parent->_lock(part->parent, ofs + part->offset, len);
}
static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_unlock(part->master, ofs + part->offset, len);
+ return part->parent->_unlock(part->parent, ofs + part->offset, len);
}
static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_is_locked(part->master, ofs + part->offset, len);
+ return part->parent->_is_locked(part->parent, ofs + part->offset, len);
}
static void part_sync(struct mtd_info *mtd)
{
struct mtd_part *part = mtd_to_part(mtd);
- part->master->_sync(part->master);
+ part->parent->_sync(part->parent);
}
static int part_suspend(struct mtd_info *mtd)
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_suspend(part->master);
+ return part->parent->_suspend(part->parent);
}
static void part_resume(struct mtd_info *mtd)
{
struct mtd_part *part = mtd_to_part(mtd);
- part->master->_resume(part->master);
+ part->parent->_resume(part->parent);
}
static int part_block_isreserved(struct mtd_info *mtd, loff_t ofs)
{
struct mtd_part *part = mtd_to_part(mtd);
ofs += part->offset;
- return part->master->_block_isreserved(part->master, ofs);
+ return part->parent->_block_isreserved(part->parent, ofs);
}
static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
{
struct mtd_part *part = mtd_to_part(mtd);
ofs += part->offset;
- return part->master->_block_isbad(part->master, ofs);
+ return part->parent->_block_isbad(part->parent, ofs);
}
static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
@@ -310,7 +316,7 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
int res;
ofs += part->offset;
- res = part->master->_block_markbad(part->master, ofs);
+ res = part->parent->_block_markbad(part->parent, ofs);
if (!res)
mtd->ecc_stats.badblocks++;
return res;
@@ -319,13 +325,13 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
static int part_get_device(struct mtd_info *mtd)
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_get_device(part->master);
+ return part->parent->_get_device(part->parent);
}
static void part_put_device(struct mtd_info *mtd)
{
struct mtd_part *part = mtd_to_part(mtd);
- part->master->_put_device(part->master);
+ part->parent->_put_device(part->parent);
}
static int part_ooblayout_ecc(struct mtd_info *mtd, int section,
@@ -333,7 +339,7 @@ static int part_ooblayout_ecc(struct mtd_info *mtd, int section,
{
struct mtd_part *part = mtd_to_part(mtd);
- return mtd_ooblayout_ecc(part->master, section, oobregion);
+ return mtd_ooblayout_ecc(part->parent, section, oobregion);
}
static int part_ooblayout_free(struct mtd_info *mtd, int section,
@@ -341,7 +347,7 @@ static int part_ooblayout_free(struct mtd_info *mtd, int section,
{
struct mtd_part *part = mtd_to_part(mtd);
- return mtd_ooblayout_free(part->master, section, oobregion);
+ return mtd_ooblayout_free(part->parent, section, oobregion);
}
static const struct mtd_ooblayout_ops part_ooblayout_ops = {
@@ -353,7 +359,7 @@ static int part_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len)
{
struct mtd_part *part = mtd_to_part(mtd);
- return part->master->_max_bad_blocks(part->master,
+ return part->parent->_max_bad_blocks(part->parent,
ofs + part->offset, len);
}
@@ -363,63 +369,70 @@ static inline void free_partition(struct mtd_part *p)
kfree(p);
}
-/*
- * This function unregisters and destroy all slave MTD objects which are
- * attached to the given master MTD object.
+/**
+ * mtd_parse_part - parse MTD partition looking for subpartitions
+ *
+ * @slave: part that is supposed to be a container and should be parsed
+ * @types: NULL-terminated array with names of partition parsers to try
+ *
+ * Some partitions are kind of containers with extra subpartitions (volumes).
+ * There can be various formats of such containers. This function tries to use
+ * specified parsers to analyze given partition and registers found
+ * subpartitions on success.
*/
-
-int del_mtd_partitions(struct mtd_info *master)
+static int mtd_parse_part(struct mtd_part *slave, const char *const *types)
{
- struct mtd_part *slave, *next;
- int ret, err = 0;
+ struct mtd_partitions parsed;
+ int err;
- mutex_lock(&mtd_partitions_mutex);
- list_for_each_entry_safe(slave, next, &mtd_partitions, list)
- if (slave->master == master) {
- ret = del_mtd_device(&slave->mtd);
- if (ret < 0) {
- err = ret;
- continue;
- }
- list_del(&slave->list);
- free_partition(slave);
- }
- mutex_unlock(&mtd_partitions_mutex);
+ err = parse_mtd_partitions(&slave->mtd, types, &parsed, NULL);
+ if (err)
+ return err;
+ else if (!parsed.nr_parts)
+ return -ENOENT;
+
+ err = add_mtd_partitions(&slave->mtd, parsed.parts, parsed.nr_parts);
+
+ mtd_part_parser_cleanup(&parsed);
return err;
}
-static struct mtd_part *allocate_partition(struct mtd_info *master,
+static struct mtd_part *allocate_partition(struct mtd_info *parent,
const struct mtd_partition *part, int partno,
uint64_t cur_offset)
{
+ int wr_alignment = (parent->flags & MTD_NO_ERASE) ? parent->writesize :
+ parent->erasesize;
struct mtd_part *slave;
+ u32 remainder;
char *name;
+ u64 tmp;
/* allocate the partition structure */
slave = kzalloc(sizeof(*slave), GFP_KERNEL);
name = kstrdup(part->name, GFP_KERNEL);
if (!name || !slave) {
printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
- master->name);
+ parent->name);
kfree(name);
kfree(slave);
return ERR_PTR(-ENOMEM);
}
/* set up the MTD object for this partition */
- slave->mtd.type = master->type;
- slave->mtd.flags = master->flags & ~part->mask_flags;
+ slave->mtd.type = parent->type;
+ slave->mtd.flags = parent->flags & ~part->mask_flags;
slave->mtd.size = part->size;
- slave->mtd.writesize = master->writesize;
- slave->mtd.writebufsize = master->writebufsize;
- slave->mtd.oobsize = master->oobsize;
- slave->mtd.oobavail = master->oobavail;
- slave->mtd.subpage_sft = master->subpage_sft;
- slave->mtd.pairing = master->pairing;
+ slave->mtd.writesize = parent->writesize;
+ slave->mtd.writebufsize = parent->writebufsize;
+ slave->mtd.oobsize = parent->oobsize;
+ slave->mtd.oobavail = parent->oobavail;
+ slave->mtd.subpage_sft = parent->subpage_sft;
+ slave->mtd.pairing = parent->pairing;
slave->mtd.name = name;
- slave->mtd.owner = master->owner;
+ slave->mtd.owner = parent->owner;
/* NOTE: Historically, we didn't arrange MTDs as a tree out of
* concern for showing the same data in multiple partitions.
@@ -429,80 +442,81 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
* parent conditional on that option. Note, this is a way to
* distinguish between the master and the partition in sysfs.
*/
- slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) ?
- &master->dev :
- master->dev.parent;
+ slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) || mtd_is_partition(parent) ?
+ &parent->dev :
+ parent->dev.parent;
slave->mtd.dev.of_node = part->of_node;
slave->mtd._read = part_read;
slave->mtd._write = part_write;
- if (master->_panic_write)
+ if (parent->_panic_write)
slave->mtd._panic_write = part_panic_write;
- if (master->_point && master->_unpoint) {
+ if (parent->_point && parent->_unpoint) {
slave->mtd._point = part_point;
slave->mtd._unpoint = part_unpoint;
}
- if (master->_get_unmapped_area)
+ if (parent->_get_unmapped_area)
slave->mtd._get_unmapped_area = part_get_unmapped_area;
- if (master->_read_oob)
+ if (parent->_read_oob)
slave->mtd._read_oob = part_read_oob;
- if (master->_write_oob)
+ if (parent->_write_oob)
slave->mtd._write_oob = part_write_oob;
- if (master->_read_user_prot_reg)
+ if (parent->_read_user_prot_reg)
slave->mtd._read_user_prot_reg = part_read_user_prot_reg;
- if (master->_read_fact_prot_reg)
+ if (parent->_read_fact_prot_reg)
slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg;
- if (master->_write_user_prot_reg)
+ if (parent->_write_user_prot_reg)
slave->mtd._write_user_prot_reg = part_write_user_prot_reg;
- if (master->_lock_user_prot_reg)
+ if (parent->_lock_user_prot_reg)
slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg;
- if (master->_get_user_prot_info)
+ if (parent->_get_user_prot_info)
slave->mtd._get_user_prot_info = part_get_user_prot_info;
- if (master->_get_fact_prot_info)
+ if (parent->_get_fact_prot_info)
slave->mtd._get_fact_prot_info = part_get_fact_prot_info;
- if (master->_sync)
+ if (parent->_sync)
slave->mtd._sync = part_sync;
- if (!partno && !master->dev.class && master->_suspend &&
- master->_resume) {
- slave->mtd._suspend = part_suspend;
- slave->mtd._resume = part_resume;
+ if (!partno && !parent->dev.class && parent->_suspend &&
+ parent->_resume) {
+ slave->mtd._suspend = part_suspend;
+ slave->mtd._resume = part_resume;
}
- if (master->_writev)
+ if (parent->_writev)
slave->mtd._writev = part_writev;
- if (master->_lock)
+ if (parent->_lock)
slave->mtd._lock = part_lock;
- if (master->_unlock)
+ if (parent->_unlock)
slave->mtd._unlock = part_unlock;
- if (master->_is_locked)
+ if (parent->_is_locked)
slave->mtd._is_locked = part_is_locked;
- if (master->_block_isreserved)
+ if (parent->_block_isreserved)
slave->mtd._block_isreserved = part_block_isreserved;
- if (master->_block_isbad)
+ if (parent->_block_isbad)
slave->mtd._block_isbad = part_block_isbad;
- if (master->_block_markbad)
+ if (parent->_block_markbad)
slave->mtd._block_markbad = part_block_markbad;
- if (master->_max_bad_blocks)
+ if (parent->_max_bad_blocks)
slave->mtd._max_bad_blocks = part_max_bad_blocks;
- if (master->_get_device)
+ if (parent->_get_device)
slave->mtd._get_device = part_get_device;
- if (master->_put_device)
+ if (parent->_put_device)
slave->mtd._put_device = part_put_device;
slave->mtd._erase = part_erase;
- slave->master = master;
+ slave->parent = parent;
slave->offset = part->offset;
if (slave->offset == MTDPART_OFS_APPEND)
slave->offset = cur_offset;
if (slave->offset == MTDPART_OFS_NXTBLK) {
+ tmp = cur_offset;
slave->offset = cur_offset;
- if (mtd_mod_by_eb(cur_offset, master) != 0) {
- /* Round up to next erasesize */
- slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
+ remainder = do_div(tmp, wr_alignment);
+ if (remainder) {
+ slave->offset += wr_alignment - remainder;
printk(KERN_NOTICE "Moving partition %d: "
"0x%012llx -> 0x%012llx\n", partno,
(unsigned long long)cur_offset, (unsigned long long)slave->offset);
@@ -510,25 +524,25 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
}
if (slave->offset == MTDPART_OFS_RETAIN) {
slave->offset = cur_offset;
- if (master->size - slave->offset >= slave->mtd.size) {
- slave->mtd.size = master->size - slave->offset
+ if (parent->size - slave->offset >= slave->mtd.size) {
+ slave->mtd.size = parent->size - slave->offset
- slave->mtd.size;
} else {
printk(KERN_ERR "mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n",
- part->name, master->size - slave->offset,
+ part->name, parent->size - slave->offset,
slave->mtd.size);
/* register to preserve ordering */
goto out_register;
}
}
if (slave->mtd.size == MTDPART_SIZ_FULL)
- slave->mtd.size = master->size - slave->offset;
+ slave->mtd.size = parent->size - slave->offset;
printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
(unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
/* let's do some sanity checks */
- if (slave->offset >= master->size) {
+ if (slave->offset >= parent->size) {
/* let's register it anyway to preserve ordering */
slave->offset = 0;
slave->mtd.size = 0;
@@ -536,16 +550,16 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
part->name);
goto out_register;
}
- if (slave->offset + slave->mtd.size > master->size) {
- slave->mtd.size = master->size - slave->offset;
+ if (slave->offset + slave->mtd.size > parent->size) {
+ slave->mtd.size = parent->size - slave->offset;
printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
- part->name, master->name, (unsigned long long)slave->mtd.size);
+ part->name, parent->name, (unsigned long long)slave->mtd.size);
}
- if (master->numeraseregions > 1) {
+ if (parent->numeraseregions > 1) {
/* Deal with variable erase size stuff */
- int i, max = master->numeraseregions;
+ int i, max = parent->numeraseregions;
u64 end = slave->offset + slave->mtd.size;
- struct mtd_erase_region_info *regions = master->eraseregions;
+ struct mtd_erase_region_info *regions = parent->eraseregions;
/* Find the first erase regions which is part of this
* partition. */
@@ -564,37 +578,40 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
BUG_ON(slave->mtd.erasesize == 0);
} else {
/* Single erase size */
- slave->mtd.erasesize = master->erasesize;
+ slave->mtd.erasesize = parent->erasesize;
}
- if ((slave->mtd.flags & MTD_WRITEABLE) &&
- mtd_mod_by_eb(slave->offset, &slave->mtd)) {
+ tmp = slave->offset;
+ remainder = do_div(tmp, wr_alignment);
+ if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) {
/* Doesn't start on a boundary of major erase size */
/* FIXME: Let it be writable if it is on a boundary of
* _minor_ erase size though */
slave->mtd.flags &= ~MTD_WRITEABLE;
- printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
+ printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
part->name);
}
- if ((slave->mtd.flags & MTD_WRITEABLE) &&
- mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
+
+ tmp = slave->mtd.size;
+ remainder = do_div(tmp, wr_alignment);
+ if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) {
slave->mtd.flags &= ~MTD_WRITEABLE;
- printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
+ printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
part->name);
}
mtd_set_ooblayout(&slave->mtd, &part_ooblayout_ops);
- slave->mtd.ecc_step_size = master->ecc_step_size;
- slave->mtd.ecc_strength = master->ecc_strength;
- slave->mtd.bitflip_threshold = master->bitflip_threshold;
+ slave->mtd.ecc_step_size = parent->ecc_step_size;
+ slave->mtd.ecc_strength = parent->ecc_strength;
+ slave->mtd.bitflip_threshold = parent->bitflip_threshold;
- if (master->_block_isbad) {
+ if (parent->_block_isbad) {
uint64_t offs = 0;
while (offs < slave->mtd.size) {
- if (mtd_block_isreserved(master, offs + slave->offset))
+ if (mtd_block_isreserved(parent, offs + slave->offset))
slave->mtd.ecc_stats.bbtblocks++;
- else if (mtd_block_isbad(master, offs + slave->offset))
+ else if (mtd_block_isbad(parent, offs + slave->offset))
slave->mtd.ecc_stats.badblocks++;
offs += slave->mtd.erasesize;
}
@@ -628,7 +645,7 @@ static int mtd_add_partition_attrs(struct mtd_part *new)
return ret;
}
-int mtd_add_partition(struct mtd_info *master, const char *name,
+int mtd_add_partition(struct mtd_info *parent, const char *name,
long long offset, long long length)
{
struct mtd_partition part;
@@ -641,7 +658,7 @@ int mtd_add_partition(struct mtd_info *master, const char *name,
return -EINVAL;
if (length == MTDPART_SIZ_FULL)
- length = master->size - offset;
+ length = parent->size - offset;
if (length <= 0)
return -EINVAL;
@@ -651,7 +668,7 @@ int mtd_add_partition(struct mtd_info *master, const char *name,
part.size = length;
part.offset = offset;
- new = allocate_partition(master, &part, -1, offset);
+ new = allocate_partition(parent, &part, -1, offset);
if (IS_ERR(new))
return PTR_ERR(new);
@@ -667,23 +684,69 @@ int mtd_add_partition(struct mtd_info *master, const char *name,
}
EXPORT_SYMBOL_GPL(mtd_add_partition);
-int mtd_del_partition(struct mtd_info *master, int partno)
+/**
+ * __mtd_del_partition - delete MTD partition
+ *
+ * @priv: internal MTD struct for partition to be deleted
+ *
+ * This function must be called with the partitions mutex locked.
+ */
+static int __mtd_del_partition(struct mtd_part *priv)
+{
+ struct mtd_part *child, *next;
+ int err;
+
+ list_for_each_entry_safe(child, next, &mtd_partitions, list) {
+ if (child->parent == &priv->mtd) {
+ err = __mtd_del_partition(child);
+ if (err)
+ return err;
+ }
+ }
+
+ sysfs_remove_files(&priv->mtd.dev.kobj, mtd_partition_attrs);
+
+ err = del_mtd_device(&priv->mtd);
+ if (err)
+ return err;
+
+ list_del(&priv->list);
+ free_partition(priv);
+
+ return 0;
+}
+
+/*
+ * This function unregisters and destroy all slave MTD objects which are
+ * attached to the given MTD object.
+ */
+int del_mtd_partitions(struct mtd_info *mtd)
{
struct mtd_part *slave, *next;
- int ret = -EINVAL;
+ int ret, err = 0;
mutex_lock(&mtd_partitions_mutex);
list_for_each_entry_safe(slave, next, &mtd_partitions, list)
- if ((slave->master == master) &&
- (slave->mtd.index == partno)) {
- sysfs_remove_files(&slave->mtd.dev.kobj,
- mtd_partition_attrs);
- ret = del_mtd_device(&slave->mtd);
+ if (slave->parent == mtd) {
+ ret = __mtd_del_partition(slave);
if (ret < 0)
- break;
+ err = ret;
+ }
+ mutex_unlock(&mtd_partitions_mutex);
+
+ return err;
+}
+
+int mtd_del_partition(struct mtd_info *mtd, int partno)
+{
+ struct mtd_part *slave, *next;
+ int ret = -EINVAL;
- list_del(&slave->list);
- free_partition(slave);
+ mutex_lock(&mtd_partitions_mutex);
+ list_for_each_entry_safe(slave, next, &mtd_partitions, list)
+ if ((slave->parent == mtd) &&
+ (slave->mtd.index == partno)) {
+ ret = __mtd_del_partition(slave);
break;
}
mutex_unlock(&mtd_partitions_mutex);
@@ -724,6 +787,8 @@ int add_mtd_partitions(struct mtd_info *master,
add_mtd_device(&slave->mtd);
mtd_add_partition_attrs(slave);
+ if (parts[i].types)
+ mtd_parse_part(slave, parts[i].types);
cur_offset = slave->offset + slave->mtd.size;
}
@@ -799,6 +864,27 @@ static const char * const default_mtd_part_types[] = {
NULL
};
+static int mtd_part_do_parse(struct mtd_part_parser *parser,
+ struct mtd_info *master,
+ struct mtd_partitions *pparts,
+ struct mtd_part_parser_data *data)
+{
+ int ret;
+
+ ret = (*parser->parse_fn)(master, &pparts->parts, data);
+ pr_debug("%s: parser %s: %i\n", master->name, parser->name, ret);
+ if (ret <= 0)
+ return ret;
+
+ pr_notice("%d %s partitions found on MTD device %s\n", ret,
+ parser->name, master->name);
+
+ pparts->nr_parts = ret;
+ pparts->parser = parser;
+
+ return ret;
+}
+
/**
* parse_mtd_partitions - parse MTD partitions
* @master: the master partition (describes whole MTD device)
@@ -839,16 +925,10 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
parser ? parser->name : NULL);
if (!parser)
continue;
- ret = (*parser->parse_fn)(master, &pparts->parts, data);
- pr_debug("%s: parser %s: %i\n",
- master->name, parser->name, ret);
- if (ret > 0) {
- printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
- ret, parser->name, master->name);
- pparts->nr_parts = ret;
- pparts->parser = parser;
+ ret = mtd_part_do_parse(parser, master, pparts, data);
+ /* Found partitions! */
+ if (ret > 0)
return 0;
- }
mtd_part_parser_put(parser);
/*
* Stash the first error we see; only report it if no parser
@@ -899,6 +979,6 @@ uint64_t mtd_get_device_size(const struct mtd_info *mtd)
if (!mtd_is_partition(mtd))
return mtd->size;
- return mtd_to_part(mtd)->master->size;
+ return mtd_get_device_size(mtd_to_part(mtd)->parent);
}
EXPORT_SYMBOL_GPL(mtd_get_device_size);
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index c3029528063b..dbfa72d61d5a 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -308,6 +308,7 @@ config MTD_NAND_CS553X
config MTD_NAND_ATMEL
tristate "Support for NAND Flash / SmartMedia on AT91"
depends on ARCH_AT91
+ select MFD_ATMEL_SMC
help
Enables support for NAND Flash / Smart Media Card interface
on Atmel AT91 processors.
@@ -542,6 +543,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_DMA
help
Enables support for NAND controller on Hisilicon SoC Hip04.
@@ -555,6 +557,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_DMA
help
Enables support for NAND controller on MTK SoCs.
diff --git a/drivers/mtd/nand/atmel/nand-controller.c b/drivers/mtd/nand/atmel/nand-controller.c
index 3b2446896147..d922a88e407f 100644
--- a/drivers/mtd/nand/atmel/nand-controller.c
+++ b/drivers/mtd/nand/atmel/nand-controller.c
@@ -57,6 +57,7 @@
#include <linux/interrupt.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/atmel-matrix.h>
+#include <linux/mfd/syscon/atmel-smc.h>
#include <linux/module.h>
#include <linux/mtd/nand.h>
#include <linux/of_address.h>
@@ -64,7 +65,6 @@
#include <linux/of_platform.h>
#include <linux/iopoll.h>
#include <linux/platform_device.h>
-#include <linux/platform_data/atmel.h>
#include <linux/regmap.h>
#include "pmecc.h"
@@ -151,6 +151,8 @@ struct atmel_nand_cs {
void __iomem *virt;
dma_addr_t dma;
} io;
+
+ struct atmel_smc_cs_conf smcconf;
};
struct atmel_nand {
@@ -196,6 +198,8 @@ struct atmel_nand_controller_ops {
void (*nand_init)(struct atmel_nand_controller *nc,
struct atmel_nand *nand);
int (*ecc_init)(struct atmel_nand *nand);
+ int (*setup_data_interface)(struct atmel_nand *nand, int csline,
+ const struct nand_data_interface *conf);
};
struct atmel_nand_controller_caps {
@@ -912,7 +916,7 @@ static int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip,
struct mtd_info *mtd = nand_to_mtd(chip);
struct atmel_nand *nand = to_atmel_nand(chip);
struct atmel_hsmc_nand_controller *nc;
- int ret;
+ int ret, status;
nc = to_hsmc_nand_controller(chip->controller);
@@ -954,6 +958,10 @@ static int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip,
dev_err(nc->base.dev, "Failed to program NAND page (err = %d)\n",
ret);
+ status = chip->waitfunc(mtd, chip);
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+
return ret;
}
@@ -1175,6 +1183,295 @@ static int atmel_hsmc_nand_ecc_init(struct atmel_nand *nand)
return 0;
}
+static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
+ const struct nand_data_interface *conf,
+ struct atmel_smc_cs_conf *smcconf)
+{
+ u32 ncycles, totalcycles, timeps, mckperiodps;
+ struct atmel_nand_controller *nc;
+ int ret;
+
+ nc = to_nand_controller(nand->base.controller);
+
+ /* DDR interface not supported. */
+ if (conf->type != NAND_SDR_IFACE)
+ return -ENOTSUPP;
+
+ /*
+ * tRC < 30ns implies EDO mode. This controller does not support this
+ * mode.
+ */
+ if (conf->timings.sdr.tRC_min < 30)
+ return -ENOTSUPP;
+
+ atmel_smc_cs_conf_init(smcconf);
+
+ mckperiodps = NSEC_PER_SEC / clk_get_rate(nc->mck);
+ mckperiodps *= 1000;
+
+ /*
+ * Set write pulse timing. This one is easy to extract:
+ *
+ * NWE_PULSE = tWP
+ */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tWP_min, mckperiodps);
+ totalcycles = ncycles;
+ ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NWE_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * The write setup timing depends on the operation done on the NAND.
+ * All operations goes through the same data bus, but the operation
+ * type depends on the address we are writing to (ALE/CLE address
+ * lines).
+ * Since we have no way to differentiate the different operations at
+ * the SMC level, we must consider the worst case (the biggest setup
+ * time among all operation types):
+ *
+ * NWE_SETUP = max(tCLS, tCS, tALS, tDS) - NWE_PULSE
+ */
+ timeps = max3(conf->timings.sdr.tCLS_min, conf->timings.sdr.tCS_min,
+ conf->timings.sdr.tALS_min);
+ timeps = max(timeps, conf->timings.sdr.tDS_min);
+ ncycles = DIV_ROUND_UP(timeps, mckperiodps);
+ ncycles = ncycles > totalcycles ? ncycles - totalcycles : 0;
+ totalcycles += ncycles;
+ ret = atmel_smc_cs_conf_set_setup(smcconf, ATMEL_SMC_NWE_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * As for the write setup timing, the write hold timing depends on the
+ * operation done on the NAND:
+ *
+ * NWE_HOLD = max(tCLH, tCH, tALH, tDH, tWH)
+ */
+ timeps = max3(conf->timings.sdr.tCLH_min, conf->timings.sdr.tCH_min,
+ conf->timings.sdr.tALH_min);
+ timeps = max3(timeps, conf->timings.sdr.tDH_min,
+ conf->timings.sdr.tWH_min);
+ ncycles = DIV_ROUND_UP(timeps, mckperiodps);
+ totalcycles += ncycles;
+
+ /*
+ * The write cycle timing is directly matching tWC, but is also
+ * dependent on the other timings on the setup and hold timings we
+ * calculated earlier, which gives:
+ *
+ * NWE_CYCLE = max(tWC, NWE_SETUP + NWE_PULSE + NWE_HOLD)
+ */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tWC_min, mckperiodps);
+ ncycles = max(totalcycles, ncycles);
+ ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NWE_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * We don't want the CS line to be toggled between each byte/word
+ * transfer to the NAND. The only way to guarantee that is to have the
+ * NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means:
+ *
+ * NCS_WR_PULSE = NWE_CYCLE
+ */
+ ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_WR_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * As for the write setup timing, the read hold timing depends on the
+ * operation done on the NAND:
+ *
+ * NRD_HOLD = max(tREH, tRHOH)
+ */
+ timeps = max(conf->timings.sdr.tREH_min, conf->timings.sdr.tRHOH_min);
+ ncycles = DIV_ROUND_UP(timeps, mckperiodps);
+ totalcycles = ncycles;
+
+ /*
+ * TDF = tRHZ - NRD_HOLD
+ */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tRHZ_max, mckperiodps);
+ ncycles -= totalcycles;
+
+ /*
+ * In ONFI 4.0 specs, tRHZ has been increased to support EDO NANDs and
+ * we might end up with a config that does not fit in the TDF field.
+ * Just take the max value in this case and hope that the NAND is more
+ * tolerant than advertised.
+ */
+ if (ncycles > ATMEL_SMC_MODE_TDF_MAX)
+ ncycles = ATMEL_SMC_MODE_TDF_MAX;
+ else if (ncycles < ATMEL_SMC_MODE_TDF_MIN)
+ ncycles = ATMEL_SMC_MODE_TDF_MIN;
+
+ smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles) |
+ ATMEL_SMC_MODE_TDFMODE_OPTIMIZED;
+
+ /*
+ * Read pulse timing directly matches tRP:
+ *
+ * NRD_PULSE = tRP
+ */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tRP_min, mckperiodps);
+ totalcycles += ncycles;
+ ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NRD_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * The write cycle timing is directly matching tWC, but is also
+ * dependent on the setup and hold timings we calculated earlier,
+ * which gives:
+ *
+ * NRD_CYCLE = max(tRC, NRD_PULSE + NRD_HOLD)
+ *
+ * NRD_SETUP is always 0.
+ */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tRC_min, mckperiodps);
+ ncycles = max(totalcycles, ncycles);
+ ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NRD_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /*
+ * We don't want the CS line to be toggled between each byte/word
+ * transfer from the NAND. The only way to guarantee that is to have
+ * the NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means:
+ *
+ * NCS_RD_PULSE = NRD_CYCLE
+ */
+ ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_RD_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /* Txxx timings are directly matching tXXX ones. */
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tCLR_min, mckperiodps);
+ ret = atmel_smc_cs_conf_set_timing(smcconf,
+ ATMEL_HSMC_TIMINGS_TCLR_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tADL_min, mckperiodps);
+ ret = atmel_smc_cs_conf_set_timing(smcconf,
+ ATMEL_HSMC_TIMINGS_TADL_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tAR_min, mckperiodps);
+ ret = atmel_smc_cs_conf_set_timing(smcconf,
+ ATMEL_HSMC_TIMINGS_TAR_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tRR_min, mckperiodps);
+ ret = atmel_smc_cs_conf_set_timing(smcconf,
+ ATMEL_HSMC_TIMINGS_TRR_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ ncycles = DIV_ROUND_UP(conf->timings.sdr.tWB_max, mckperiodps);
+ ret = atmel_smc_cs_conf_set_timing(smcconf,
+ ATMEL_HSMC_TIMINGS_TWB_SHIFT,
+ ncycles);
+ if (ret)
+ return ret;
+
+ /* Attach the CS line to the NFC logic. */
+ smcconf->timings |= ATMEL_HSMC_TIMINGS_NFSEL;
+
+ /* Set the appropriate data bus width. */
+ if (nand->base.options & NAND_BUSWIDTH_16)
+ smcconf->mode |= ATMEL_SMC_MODE_DBW_16;
+
+ /* Operate in NRD/NWE READ/WRITEMODE. */
+ smcconf->mode |= ATMEL_SMC_MODE_READMODE_NRD |
+ ATMEL_SMC_MODE_WRITEMODE_NWE;
+
+ return 0;
+}
+
+static int atmel_smc_nand_setup_data_interface(struct atmel_nand *nand,
+ int csline,
+ const struct nand_data_interface *conf)
+{
+ struct atmel_nand_controller *nc;
+ struct atmel_smc_cs_conf smcconf;
+ struct atmel_nand_cs *cs;
+ int ret;
+
+ nc = to_nand_controller(nand->base.controller);
+
+ ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf);
+ if (ret)
+ return ret;
+
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ cs = &nand->cs[csline];
+ cs->smcconf = smcconf;
+ atmel_smc_cs_conf_apply(nc->smc, cs->id, &cs->smcconf);
+
+ return 0;
+}
+
+static int atmel_hsmc_nand_setup_data_interface(struct atmel_nand *nand,
+ int csline,
+ const struct nand_data_interface *conf)
+{
+ struct atmel_nand_controller *nc;
+ struct atmel_smc_cs_conf smcconf;
+ struct atmel_nand_cs *cs;
+ int ret;
+
+ nc = to_nand_controller(nand->base.controller);
+
+ ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf);
+ if (ret)
+ return ret;
+
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ cs = &nand->cs[csline];
+ cs->smcconf = smcconf;
+
+ if (cs->rb.type == ATMEL_NAND_NATIVE_RB)
+ cs->smcconf.timings |= ATMEL_HSMC_TIMINGS_RBNSEL(cs->rb.id);
+
+ atmel_hsmc_cs_conf_apply(nc->smc, cs->id, &cs->smcconf);
+
+ return 0;
+}
+
+static int atmel_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 atmel_nand *nand = to_atmel_nand(chip);
+ struct atmel_nand_controller *nc;
+
+ nc = to_nand_controller(nand->base.controller);
+
+ if (csline >= nand->numcs ||
+ (csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY))
+ return -EINVAL;
+
+ return nc->caps->ops->setup_data_interface(nand, csline, conf);
+}
+
static void atmel_nand_init(struct atmel_nand_controller *nc,
struct atmel_nand *nand)
{
@@ -1192,6 +1489,9 @@ static void atmel_nand_init(struct atmel_nand_controller *nc,
chip->write_buf = atmel_nand_write_buf;
chip->select_chip = atmel_nand_select_chip;
+ if (nc->mck && nc->caps->ops->setup_data_interface)
+ chip->setup_data_interface = atmel_nand_setup_data_interface;
+
/* Some NANDs require a longer delay than the default one (20us). */
chip->chip_delay = 40;
@@ -1677,6 +1977,12 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
if (nc->caps->legacy_of_bindings)
return 0;
+ nc->mck = of_clk_get(dev->parent->of_node, 0);
+ if (IS_ERR(nc->mck)) {
+ dev_err(dev, "Failed to retrieve MCK clk\n");
+ return PTR_ERR(nc->mck);
+ }
+
np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0);
if (!np) {
dev_err(dev, "Missing or invalid atmel,smc property\n");
@@ -1983,6 +2289,7 @@ static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = {
.remove = atmel_hsmc_nand_controller_remove,
.ecc_init = atmel_hsmc_nand_ecc_init,
.nand_init = atmel_hsmc_nand_init,
+ .setup_data_interface = atmel_hsmc_nand_setup_data_interface,
};
static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = {
@@ -2037,7 +2344,14 @@ atmel_smc_nand_controller_remove(struct atmel_nand_controller *nc)
return 0;
}
-static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
+/*
+ * The SMC reg layout of at91rm9200 is completely different which prevents us
+ * from re-using atmel_smc_nand_setup_data_interface() for the
+ * ->setup_data_interface() hook.
+ * At this point, there's no support for the at91rm9200 SMC IP, so we leave
+ * ->setup_data_interface() unassigned.
+ */
+static const struct atmel_nand_controller_ops at91rm9200_nc_ops = {
.probe = atmel_smc_nand_controller_probe,
.remove = atmel_smc_nand_controller_remove,
.ecc_init = atmel_nand_ecc_init,
@@ -2047,6 +2361,20 @@ static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
static const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = {
.ale_offs = BIT(21),
.cle_offs = BIT(22),
+ .ops = &at91rm9200_nc_ops,
+};
+
+static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
+ .probe = atmel_smc_nand_controller_probe,
+ .remove = atmel_smc_nand_controller_remove,
+ .ecc_init = atmel_nand_ecc_init,
+ .nand_init = atmel_smc_nand_init,
+ .setup_data_interface = atmel_smc_nand_setup_data_interface,
+};
+
+static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = {
+ .ale_offs = BIT(21),
+ .cle_offs = BIT(22),
.ops = &atmel_smc_nc_ops,
};
@@ -2093,7 +2421,7 @@ static const struct of_device_id atmel_nand_controller_of_ids[] = {
},
{
.compatible = "atmel,at91sam9260-nand-controller",
- .data = &atmel_rm9200_nc_caps,
+ .data = &atmel_sam9260_nc_caps,
},
{
.compatible = "atmel,at91sam9261-nand-controller",
@@ -2181,6 +2509,24 @@ static int atmel_nand_controller_remove(struct platform_device *pdev)
return nc->caps->ops->remove(nc);
}
+static __maybe_unused int atmel_nand_controller_resume(struct device *dev)
+{
+ struct atmel_nand_controller *nc = dev_get_drvdata(dev);
+ struct atmel_nand *nand;
+
+ list_for_each_entry(nand, &nc->chips, node) {
+ int i;
+
+ for (i = 0; i < nand->numcs; i++)
+ nand_reset(&nand->base, i);
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(atmel_nand_controller_pm_ops, NULL,
+ atmel_nand_controller_resume);
+
static struct platform_driver atmel_nand_controller_driver = {
.driver = {
.name = "atmel-nand-controller",
diff --git a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c
index f1da4ea88f2c..54bac5b73f0a 100644
--- a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c
+++ b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c
@@ -392,6 +392,8 @@ int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n)
b47n->nand_chip.read_byte = bcm47xxnflash_ops_bcm4706_read_byte;
b47n->nand_chip.read_buf = bcm47xxnflash_ops_bcm4706_read_buf;
b47n->nand_chip.write_buf = bcm47xxnflash_ops_bcm4706_write_buf;
+ b47n->nand_chip.onfi_set_features = nand_onfi_get_set_features_notsupp;
+ b47n->nand_chip.onfi_get_features = nand_onfi_get_set_features_notsupp;
nand_chip->chip_delay = 50;
b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH;
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c
index d40c32d311d8..2fd733eba0a3 100644
--- a/drivers/mtd/nand/cafe_nand.c
+++ b/drivers/mtd/nand/cafe_nand.c
@@ -654,6 +654,8 @@ static int cafe_nand_probe(struct pci_dev *pdev,
cafe->nand.read_buf = cafe_read_buf;
cafe->nand.write_buf = cafe_write_buf;
cafe->nand.select_chip = cafe_select_chip;
+ cafe->nand.onfi_set_features = nand_onfi_get_set_features_notsupp;
+ cafe->nand.onfi_get_features = nand_onfi_get_set_features_notsupp;
cafe->nand.chip_delay = 0;
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index 531c51991e57..7b26e53b95b1 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -771,11 +771,14 @@ static int nand_davinci_probe(struct platform_device *pdev)
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;
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 16634df2e39a..d723be352148 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -23,50 +23,43 @@
#include <linux/mutex.h>
#include <linux/mtd/mtd.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include "denali.h"
MODULE_LICENSE("GPL");
-/*
- * We define a module parameter that allows the user to override
- * the hardware and decide what timing mode should be used.
- */
-#define NAND_DEFAULT_TIMINGS -1
+#define DENALI_NAND_NAME "denali-nand"
-static int onfi_timing_mode = NAND_DEFAULT_TIMINGS;
-module_param(onfi_timing_mode, int, S_IRUGO);
-MODULE_PARM_DESC(onfi_timing_mode,
- "Overrides default ONFI setting. -1 indicates use default timings");
+/* Host Data/Command Interface */
+#define DENALI_HOST_ADDR 0x00
+#define DENALI_HOST_DATA 0x10
-#define DENALI_NAND_NAME "denali-nand"
+#define DENALI_MAP00 (0 << 26) /* direct access to buffer */
+#define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */
+#define DENALI_MAP10 (2 << 26) /* high-level control plane */
+#define DENALI_MAP11 (3 << 26) /* direct controller access */
-/*
- * We define a macro here that combines all interrupts this driver uses into
- * a single constant value, for convenience.
- */
-#define DENALI_IRQ_ALL (INTR__DMA_CMD_COMP | \
- INTR__ECC_TRANSACTION_DONE | \
- INTR__ECC_ERR | \
- INTR__PROGRAM_FAIL | \
- INTR__LOAD_COMP | \
- INTR__PROGRAM_COMP | \
- INTR__TIME_OUT | \
- INTR__ERASE_FAIL | \
- INTR__RST_COMP | \
- INTR__ERASE_COMP)
+/* MAP11 access cycle type */
+#define DENALI_MAP11_CMD ((DENALI_MAP11) | 0) /* command cycle */
+#define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1) /* address cycle */
+#define DENALI_MAP11_DATA ((DENALI_MAP11) | 2) /* data cycle */
-/*
- * indicates whether or not the internal value for the flash bank is
- * valid or not
- */
-#define CHIP_SELECT_INVALID -1
+/* MAP10 commands */
+#define DENALI_ERASE 0x01
+
+#define DENALI_BANK(denali) ((denali)->active_bank << 24)
+
+#define DENALI_INVALID_BANK -1
+#define DENALI_NR_BANKS 4
/*
- * This macro divides two integers and rounds fractional values up
- * to the nearest integer value.
+ * 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 CEIL_DIV(X, Y) (((X)%(Y)) ? ((X)/(Y)+1) : ((X)/(Y)))
+#define DENALI_CLK_X_MULT 6
/*
* this macro allows us to convert from an MTD structure to our own
@@ -77,339 +70,11 @@ 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);
}
-/*
- * These constants are defined by the driver to enable common driver
- * configuration options.
- */
-#define SPARE_ACCESS 0x41
-#define MAIN_ACCESS 0x42
-#define MAIN_SPARE_ACCESS 0x43
-
-#define DENALI_READ 0
-#define DENALI_WRITE 0x100
-
-/*
- * this is a helper macro that allows us to
- * format the bank into the proper bits for the controller
- */
-#define BANK(x) ((x) << 24)
-
-/* forward declarations */
-static void clear_interrupts(struct denali_nand_info *denali);
-static uint32_t wait_for_irq(struct denali_nand_info *denali,
- uint32_t irq_mask);
-static void denali_irq_enable(struct denali_nand_info *denali,
- uint32_t int_mask);
-static uint32_t read_interrupt_status(struct denali_nand_info *denali);
-
-/*
- * Certain operations for the denali NAND controller use an indexed mode to
- * read/write data. The operation is performed by writing the address value
- * of the command to the device memory followed by the data. This function
- * abstracts this common operation.
- */
-static void index_addr(struct denali_nand_info *denali,
- uint32_t address, uint32_t data)
-{
- iowrite32(address, denali->flash_mem);
- iowrite32(data, denali->flash_mem + 0x10);
-}
-
-/* Perform an indexed read of the device */
-static void index_addr_read_data(struct denali_nand_info *denali,
- uint32_t address, uint32_t *pdata)
-{
- iowrite32(address, denali->flash_mem);
- *pdata = ioread32(denali->flash_mem + 0x10);
-}
-
-/*
- * We need to buffer some data for some of the NAND core routines.
- * The operations manage buffering that data.
- */
-static void reset_buf(struct denali_nand_info *denali)
-{
- denali->buf.head = denali->buf.tail = 0;
-}
-
-static void write_byte_to_buf(struct denali_nand_info *denali, uint8_t byte)
-{
- denali->buf.buf[denali->buf.tail++] = byte;
-}
-
-/* reads the status of the device */
-static void read_status(struct denali_nand_info *denali)
-{
- uint32_t cmd;
-
- /* initialize the data buffer to store status */
- reset_buf(denali);
-
- cmd = ioread32(denali->flash_reg + WRITE_PROTECT);
- if (cmd)
- write_byte_to_buf(denali, NAND_STATUS_WP);
- else
- write_byte_to_buf(denali, 0);
-}
-
-/* resets a specific device connected to the core */
-static void reset_bank(struct denali_nand_info *denali)
-{
- uint32_t irq_status;
- uint32_t irq_mask = INTR__RST_COMP | INTR__TIME_OUT;
-
- clear_interrupts(denali);
-
- iowrite32(1 << denali->flash_bank, denali->flash_reg + DEVICE_RESET);
-
- irq_status = wait_for_irq(denali, irq_mask);
-
- if (irq_status & INTR__TIME_OUT)
- dev_err(denali->dev, "reset bank failed.\n");
-}
-
-/* Reset the flash controller */
-static uint16_t denali_nand_reset(struct denali_nand_info *denali)
-{
- int i;
-
- for (i = 0; i < denali->max_banks; i++)
- iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
- denali->flash_reg + INTR_STATUS(i));
-
- for (i = 0; i < denali->max_banks; i++) {
- iowrite32(1 << i, denali->flash_reg + DEVICE_RESET);
- while (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
- (INTR__RST_COMP | INTR__TIME_OUT)))
- cpu_relax();
- if (ioread32(denali->flash_reg + INTR_STATUS(i)) &
- INTR__TIME_OUT)
- dev_dbg(denali->dev,
- "NAND Reset operation timed out on bank %d\n", i);
- }
-
- for (i = 0; i < denali->max_banks; i++)
- iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
- denali->flash_reg + INTR_STATUS(i));
-
- return PASS;
-}
-
-/*
- * this routine calculates the ONFI timing values for a given mode and
- * programs the clocking register accordingly. The mode is determined by
- * the get_onfi_nand_para routine.
- */
-static void nand_onfi_timing_set(struct denali_nand_info *denali,
- uint16_t mode)
-{
- uint16_t Trea[6] = {40, 30, 25, 20, 20, 16};
- uint16_t Trp[6] = {50, 25, 17, 15, 12, 10};
- uint16_t Treh[6] = {30, 15, 15, 10, 10, 7};
- uint16_t Trc[6] = {100, 50, 35, 30, 25, 20};
- uint16_t Trhoh[6] = {0, 15, 15, 15, 15, 15};
- uint16_t Trloh[6] = {0, 0, 0, 0, 5, 5};
- uint16_t Tcea[6] = {100, 45, 30, 25, 25, 25};
- uint16_t Tadl[6] = {200, 100, 100, 100, 70, 70};
- uint16_t Trhw[6] = {200, 100, 100, 100, 100, 100};
- uint16_t Trhz[6] = {200, 100, 100, 100, 100, 100};
- uint16_t Twhr[6] = {120, 80, 80, 60, 60, 60};
- uint16_t Tcs[6] = {70, 35, 25, 25, 20, 15};
-
- uint16_t data_invalid_rhoh, data_invalid_rloh, data_invalid;
- uint16_t dv_window = 0;
- uint16_t en_lo, en_hi;
- uint16_t acc_clks;
- uint16_t addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt;
-
- en_lo = CEIL_DIV(Trp[mode], CLK_X);
- en_hi = CEIL_DIV(Treh[mode], CLK_X);
-#if ONFI_BLOOM_TIME
- if ((en_hi * CLK_X) < (Treh[mode] + 2))
- en_hi++;
-#endif
-
- if ((en_lo + en_hi) * CLK_X < Trc[mode])
- en_lo += CEIL_DIV((Trc[mode] - (en_lo + en_hi) * CLK_X), CLK_X);
-
- if ((en_lo + en_hi) < CLK_MULTI)
- en_lo += CLK_MULTI - en_lo - en_hi;
-
- while (dv_window < 8) {
- data_invalid_rhoh = en_lo * CLK_X + Trhoh[mode];
-
- data_invalid_rloh = (en_lo + en_hi) * CLK_X + Trloh[mode];
-
- data_invalid = data_invalid_rhoh < data_invalid_rloh ?
- data_invalid_rhoh : data_invalid_rloh;
-
- dv_window = data_invalid - Trea[mode];
-
- if (dv_window < 8)
- en_lo++;
- }
-
- acc_clks = CEIL_DIV(Trea[mode], CLK_X);
-
- while (acc_clks * CLK_X - Trea[mode] < 3)
- acc_clks++;
-
- if (data_invalid - acc_clks * CLK_X < 2)
- dev_warn(denali->dev, "%s, Line %d: Warning!\n",
- __FILE__, __LINE__);
-
- addr_2_data = CEIL_DIV(Tadl[mode], CLK_X);
- re_2_we = CEIL_DIV(Trhw[mode], CLK_X);
- re_2_re = CEIL_DIV(Trhz[mode], CLK_X);
- we_2_re = CEIL_DIV(Twhr[mode], CLK_X);
- cs_cnt = CEIL_DIV((Tcs[mode] - Trp[mode]), CLK_X);
- if (cs_cnt == 0)
- cs_cnt = 1;
-
- if (Tcea[mode]) {
- while (cs_cnt * CLK_X + Trea[mode] < Tcea[mode])
- cs_cnt++;
- }
-
-#if MODE5_WORKAROUND
- if (mode == 5)
- acc_clks = 5;
-#endif
-
- /* Sighting 3462430: Temporary hack for MT29F128G08CJABAWP:B */
- if (ioread32(denali->flash_reg + MANUFACTURER_ID) == 0 &&
- ioread32(denali->flash_reg + DEVICE_ID) == 0x88)
- acc_clks = 6;
-
- iowrite32(acc_clks, denali->flash_reg + ACC_CLKS);
- iowrite32(re_2_we, denali->flash_reg + RE_2_WE);
- iowrite32(re_2_re, denali->flash_reg + RE_2_RE);
- iowrite32(we_2_re, denali->flash_reg + WE_2_RE);
- iowrite32(addr_2_data, denali->flash_reg + ADDR_2_DATA);
- iowrite32(en_lo, denali->flash_reg + RDWR_EN_LO_CNT);
- iowrite32(en_hi, denali->flash_reg + RDWR_EN_HI_CNT);
- iowrite32(cs_cnt, denali->flash_reg + CS_SETUP_CNT);
-}
-
-/* queries the NAND device to see what ONFI modes it supports. */
-static uint16_t get_onfi_nand_para(struct denali_nand_info *denali)
+static void denali_host_write(struct denali_nand_info *denali,
+ uint32_t addr, uint32_t data)
{
- int i;
-
- /*
- * we needn't to do a reset here because driver has already
- * reset all the banks before
- */
- if (!(ioread32(denali->flash_reg + ONFI_TIMING_MODE) &
- ONFI_TIMING_MODE__VALUE))
- return FAIL;
-
- for (i = 5; i > 0; i--) {
- if (ioread32(denali->flash_reg + ONFI_TIMING_MODE) &
- (0x01 << i))
- break;
- }
-
- nand_onfi_timing_set(denali, i);
-
- /*
- * By now, all the ONFI devices we know support the page cache
- * rw feature. So here we enable the pipeline_rw_ahead feature
- */
- /* iowrite32(1, denali->flash_reg + CACHE_WRITE_ENABLE); */
- /* iowrite32(1, denali->flash_reg + CACHE_READ_ENABLE); */
-
- return PASS;
-}
-
-static void get_samsung_nand_para(struct denali_nand_info *denali,
- uint8_t device_id)
-{
- if (device_id == 0xd3) { /* Samsung K9WAG08U1A */
- /* Set timing register values according to datasheet */
- iowrite32(5, denali->flash_reg + ACC_CLKS);
- iowrite32(20, denali->flash_reg + RE_2_WE);
- iowrite32(12, denali->flash_reg + WE_2_RE);
- iowrite32(14, denali->flash_reg + ADDR_2_DATA);
- iowrite32(3, denali->flash_reg + RDWR_EN_LO_CNT);
- iowrite32(2, denali->flash_reg + RDWR_EN_HI_CNT);
- iowrite32(2, denali->flash_reg + CS_SETUP_CNT);
- }
-}
-
-static void get_toshiba_nand_para(struct denali_nand_info *denali)
-{
- /*
- * Workaround to fix a controller bug which reports a wrong
- * spare area size for some kind of Toshiba NAND device
- */
- if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) == 4096) &&
- (ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) == 64))
- iowrite32(216, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
-}
-
-static void get_hynix_nand_para(struct denali_nand_info *denali,
- uint8_t device_id)
-{
- switch (device_id) {
- case 0xD5: /* Hynix H27UAG8T2A, H27UBG8U5A or H27UCG8VFA */
- case 0xD7: /* Hynix H27UDG8VEM, H27UCG8UDM or H27UCG8V5A */
- iowrite32(128, denali->flash_reg + PAGES_PER_BLOCK);
- iowrite32(4096, denali->flash_reg + DEVICE_MAIN_AREA_SIZE);
- iowrite32(224, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
- iowrite32(0, denali->flash_reg + DEVICE_WIDTH);
- break;
- default:
- dev_warn(denali->dev,
- "Unknown Hynix NAND (Device ID: 0x%x).\n"
- "Will use default parameter values instead.\n",
- device_id);
- }
-}
-
-/*
- * determines how many NAND chips are connected to the controller. Note for
- * Intel CE4100 devices we don't support more than one device.
- */
-static void find_valid_banks(struct denali_nand_info *denali)
-{
- uint32_t id[denali->max_banks];
- int i;
-
- denali->total_used_banks = 1;
- for (i = 0; i < denali->max_banks; i++) {
- index_addr(denali, MODE_11 | (i << 24) | 0, 0x90);
- index_addr(denali, MODE_11 | (i << 24) | 1, 0);
- index_addr_read_data(denali, MODE_11 | (i << 24) | 2, &id[i]);
-
- dev_dbg(denali->dev,
- "Return 1st ID for bank[%d]: %x\n", i, id[i]);
-
- if (i == 0) {
- if (!(id[i] & 0x0ff))
- break; /* WTF? */
- } else {
- if ((id[i] & 0x0ff) == (id[0] & 0x0ff))
- denali->total_used_banks++;
- else
- break;
- }
- }
-
- if (denali->platform == INTEL_CE4100) {
- /*
- * Platform limitations of the CE4100 device limit
- * users to a single chip solution for NAND.
- * Multichip support is not enabled.
- */
- if (denali->total_used_banks != 1) {
- dev_err(denali->dev,
- "Sorry, Intel CE4100 only supports a single NAND device.\n");
- BUG();
- }
- }
- dev_dbg(denali->dev,
- "denali->total_used_banks: %d\n", denali->total_used_banks);
+ iowrite32(addr, denali->host + DENALI_HOST_ADDR);
+ iowrite32(data, denali->host + DENALI_HOST_DATA);
}
/*
@@ -418,7 +83,7 @@ static void find_valid_banks(struct denali_nand_info *denali)
*/
static void detect_max_banks(struct denali_nand_info *denali)
{
- uint32_t features = ioread32(denali->flash_reg + FEATURES);
+ uint32_t features = ioread32(denali->reg + FEATURES);
denali->max_banks = 1 << (features & FEATURES__N_BANKS);
@@ -427,227 +92,120 @@ static void detect_max_banks(struct denali_nand_info *denali)
denali->max_banks <<= 1;
}
-static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
+static void denali_enable_irq(struct denali_nand_info *denali)
{
- uint16_t status = PASS;
- uint32_t id_bytes[8], addr;
- uint8_t maf_id, device_id;
int i;
- /*
- * Use read id method to get device ID and other params.
- * For some NAND chips, controller can't report the correct
- * device ID by reading from DEVICE_ID register
- */
- addr = MODE_11 | BANK(denali->flash_bank);
- index_addr(denali, addr | 0, 0x90);
- index_addr(denali, addr | 1, 0);
- for (i = 0; i < 8; i++)
- index_addr_read_data(denali, addr | 2, &id_bytes[i]);
- maf_id = id_bytes[0];
- device_id = id_bytes[1];
-
- if (ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_LUNS) &
- ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE) { /* ONFI 1.0 NAND */
- if (FAIL == get_onfi_nand_para(denali))
- return FAIL;
- } else if (maf_id == 0xEC) { /* Samsung NAND */
- get_samsung_nand_para(denali, device_id);
- } else if (maf_id == 0x98) { /* Toshiba NAND */
- get_toshiba_nand_para(denali);
- } else if (maf_id == 0xAD) { /* Hynix NAND */
- get_hynix_nand_para(denali, device_id);
- }
-
- dev_info(denali->dev,
- "Dump timing register values:\n"
- "acc_clks: %d, re_2_we: %d, re_2_re: %d\n"
- "we_2_re: %d, addr_2_data: %d, rdwr_en_lo_cnt: %d\n"
- "rdwr_en_hi_cnt: %d, cs_setup_cnt: %d\n",
- ioread32(denali->flash_reg + ACC_CLKS),
- ioread32(denali->flash_reg + RE_2_WE),
- ioread32(denali->flash_reg + RE_2_RE),
- ioread32(denali->flash_reg + WE_2_RE),
- ioread32(denali->flash_reg + ADDR_2_DATA),
- ioread32(denali->flash_reg + RDWR_EN_LO_CNT),
- ioread32(denali->flash_reg + RDWR_EN_HI_CNT),
- ioread32(denali->flash_reg + CS_SETUP_CNT));
-
- find_valid_banks(denali);
-
- /*
- * If the user specified to override the default timings
- * with a specific ONFI mode, we apply those changes here.
- */
- if (onfi_timing_mode != NAND_DEFAULT_TIMINGS)
- nand_onfi_timing_set(denali, onfi_timing_mode);
-
- return status;
+ for (i = 0; i < DENALI_NR_BANKS; i++)
+ iowrite32(U32_MAX, denali->reg + INTR_EN(i));
+ iowrite32(GLOBAL_INT_EN_FLAG, denali->reg + GLOBAL_INT_ENABLE);
}
-static void denali_set_intr_modes(struct denali_nand_info *denali,
- uint16_t INT_ENABLE)
+static void denali_disable_irq(struct denali_nand_info *denali)
{
- if (INT_ENABLE)
- iowrite32(1, denali->flash_reg + GLOBAL_INT_ENABLE);
- else
- iowrite32(0, denali->flash_reg + GLOBAL_INT_ENABLE);
-}
-
-/*
- * validation function to verify that the controlling software is making
- * a valid request
- */
-static inline bool is_flash_bank_valid(int flash_bank)
-{
- return flash_bank >= 0 && flash_bank < 4;
-}
-
-static void denali_irq_init(struct denali_nand_info *denali)
-{
- uint32_t int_mask;
int i;
- /* Disable global interrupts */
- denali_set_intr_modes(denali, false);
-
- int_mask = DENALI_IRQ_ALL;
-
- /* Clear all status bits */
- for (i = 0; i < denali->max_banks; ++i)
- iowrite32(0xFFFF, denali->flash_reg + INTR_STATUS(i));
-
- denali_irq_enable(denali, int_mask);
+ for (i = 0; i < DENALI_NR_BANKS; i++)
+ iowrite32(0, denali->reg + INTR_EN(i));
+ iowrite32(0, denali->reg + GLOBAL_INT_ENABLE);
}
-static void denali_irq_cleanup(int irqnum, struct denali_nand_info *denali)
+static void denali_clear_irq(struct denali_nand_info *denali,
+ int bank, uint32_t irq_status)
{
- denali_set_intr_modes(denali, false);
+ /* write one to clear bits */
+ iowrite32(irq_status, denali->reg + INTR_STATUS(bank));
}
-static void denali_irq_enable(struct denali_nand_info *denali,
- uint32_t int_mask)
+static void denali_clear_irq_all(struct denali_nand_info *denali)
{
int i;
- for (i = 0; i < denali->max_banks; ++i)
- iowrite32(int_mask, denali->flash_reg + INTR_EN(i));
+ for (i = 0; i < DENALI_NR_BANKS; i++)
+ denali_clear_irq(denali, i, U32_MAX);
}
-/*
- * This function only returns when an interrupt that this driver cares about
- * occurs. This is to reduce the overhead of servicing interrupts
- */
-static inline uint32_t denali_irq_detected(struct denali_nand_info *denali)
+static irqreturn_t denali_isr(int irq, void *dev_id)
{
- return read_interrupt_status(denali) & DENALI_IRQ_ALL;
-}
+ struct denali_nand_info *denali = dev_id;
+ irqreturn_t ret = IRQ_NONE;
+ uint32_t irq_status;
+ int i;
-/* Interrupts are cleared by writing a 1 to the appropriate status bit */
-static inline void clear_interrupt(struct denali_nand_info *denali,
- uint32_t irq_mask)
-{
- uint32_t intr_status_reg;
+ spin_lock(&denali->irq_lock);
- intr_status_reg = INTR_STATUS(denali->flash_bank);
+ for (i = 0; i < DENALI_NR_BANKS; i++) {
+ irq_status = ioread32(denali->reg + INTR_STATUS(i));
+ if (irq_status)
+ ret = IRQ_HANDLED;
- iowrite32(irq_mask, denali->flash_reg + intr_status_reg);
-}
+ denali_clear_irq(denali, i, irq_status);
-static void clear_interrupts(struct denali_nand_info *denali)
-{
- uint32_t status;
+ if (i != denali->active_bank)
+ continue;
- spin_lock_irq(&denali->irq_lock);
+ denali->irq_status |= irq_status;
- status = read_interrupt_status(denali);
- clear_interrupt(denali, status);
+ if (denali->irq_status & denali->irq_mask)
+ complete(&denali->complete);
+ }
+
+ spin_unlock(&denali->irq_lock);
- denali->irq_status = 0x0;
- spin_unlock_irq(&denali->irq_lock);
+ return ret;
}
-static uint32_t read_interrupt_status(struct denali_nand_info *denali)
+static void denali_reset_irq(struct denali_nand_info *denali)
{
- uint32_t intr_status_reg;
-
- intr_status_reg = INTR_STATUS(denali->flash_bank);
+ unsigned long flags;
- return ioread32(denali->flash_reg + intr_status_reg);
+ spin_lock_irqsave(&denali->irq_lock, flags);
+ denali->irq_status = 0;
+ denali->irq_mask = 0;
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
}
-/*
- * This is the interrupt service routine. It handles all interrupts
- * sent to this device. Note that on CE4100, this is a shared interrupt.
- */
-static irqreturn_t denali_isr(int irq, void *dev_id)
+static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
+ uint32_t irq_mask)
{
- struct denali_nand_info *denali = dev_id;
+ unsigned long time_left, flags;
uint32_t irq_status;
- irqreturn_t result = IRQ_NONE;
- spin_lock(&denali->irq_lock);
+ spin_lock_irqsave(&denali->irq_lock, flags);
- /* check to see if a valid NAND chip has been selected. */
- if (is_flash_bank_valid(denali->flash_bank)) {
- /*
- * check to see if controller generated the interrupt,
- * since this is a shared interrupt
- */
- irq_status = denali_irq_detected(denali);
- if (irq_status != 0) {
- /* handle interrupt */
- /* first acknowledge it */
- clear_interrupt(denali, irq_status);
- /*
- * store the status in the device context for someone
- * to read
- */
- denali->irq_status |= irq_status;
- /* notify anyone who cares that it happened */
- complete(&denali->complete);
- /* tell the OS that we've handled this */
- result = IRQ_HANDLED;
- }
+ irq_status = denali->irq_status;
+
+ if (irq_mask & irq_status) {
+ /* return immediately if the IRQ has already happened. */
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
+ return irq_status;
}
- spin_unlock(&denali->irq_lock);
- return result;
-}
-static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask)
-{
- unsigned long comp_res;
- uint32_t intr_status;
- unsigned long timeout = msecs_to_jiffies(1000);
+ denali->irq_mask = irq_mask;
+ reinit_completion(&denali->complete);
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
- do {
- comp_res =
- wait_for_completion_timeout(&denali->complete, timeout);
- spin_lock_irq(&denali->irq_lock);
- intr_status = denali->irq_status;
-
- if (intr_status & irq_mask) {
- denali->irq_status &= ~irq_mask;
- spin_unlock_irq(&denali->irq_lock);
- /* our interrupt was detected */
- break;
- }
+ time_left = wait_for_completion_timeout(&denali->complete,
+ msecs_to_jiffies(1000));
+ if (!time_left) {
+ dev_err(denali->dev, "timeout while waiting for irq 0x%x\n",
+ denali->irq_mask);
+ return 0;
+ }
- /*
- * these are not the interrupts you are looking for -
- * need to wait again
- */
- spin_unlock_irq(&denali->irq_lock);
- } while (comp_res != 0);
+ return denali->irq_status;
+}
+
+static uint32_t denali_check_irq(struct denali_nand_info *denali)
+{
+ unsigned long flags;
+ uint32_t irq_status;
- if (comp_res == 0) {
- /* timeout */
- pr_err("timeout occurred, status = 0x%x, mask = 0x%x\n",
- intr_status, irq_mask);
+ spin_lock_irqsave(&denali->irq_lock, flags);
+ irq_status = denali->irq_status;
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
- intr_status = 0;
- }
- return intr_status;
+ return irq_status;
}
/*
@@ -664,153 +222,111 @@ static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en,
transfer_spare_flag = transfer_spare ? TRANSFER_SPARE_REG__FLAG : 0;
/* Enable spare area/ECC per user's request. */
- iowrite32(ecc_en_flag, denali->flash_reg + ECC_ENABLE);
- iowrite32(transfer_spare_flag, denali->flash_reg + TRANSFER_SPARE_REG);
+ iowrite32(ecc_en_flag, denali->reg + ECC_ENABLE);
+ iowrite32(transfer_spare_flag, denali->reg + TRANSFER_SPARE_REG);
}
-/*
- * sends a pipeline command operation to the controller. See the Denali NAND
- * controller's user guide for more information (section 4.2.3.6).
- */
-static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
- bool ecc_en, bool transfer_spare,
- int access_type, int op)
+static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
- int status = PASS;
- uint32_t addr, cmd;
-
- setup_ecc_for_xfer(denali, ecc_en, transfer_spare);
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ int i;
- clear_interrupts(denali);
+ iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali),
+ denali->host + DENALI_HOST_ADDR);
- addr = BANK(denali->flash_bank) | denali->page;
+ for (i = 0; i < len; i++)
+ buf[i] = ioread32(denali->host + DENALI_HOST_DATA);
+}
- if (op == DENALI_WRITE && access_type != SPARE_ACCESS) {
- cmd = MODE_01 | addr;
- iowrite32(cmd, denali->flash_mem);
- } else if (op == DENALI_WRITE && access_type == SPARE_ACCESS) {
- /* read spare area */
- cmd = MODE_10 | addr;
- index_addr(denali, cmd, access_type);
+static void denali_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ int i;
- cmd = MODE_01 | addr;
- iowrite32(cmd, denali->flash_mem);
- } else if (op == DENALI_READ) {
- /* setup page read request for access type */
- cmd = MODE_10 | addr;
- index_addr(denali, cmd, access_type);
+ iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali),
+ denali->host + DENALI_HOST_ADDR);
- cmd = MODE_01 | addr;
- iowrite32(cmd, denali->flash_mem);
- }
- return status;
+ for (i = 0; i < len; i++)
+ iowrite32(buf[i], denali->host + DENALI_HOST_DATA);
}
-/* helper function that simply writes a buffer to the flash */
-static int write_data_to_flash_mem(struct denali_nand_info *denali,
- const uint8_t *buf, int len)
+static void denali_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
{
- uint32_t *buf32;
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ uint16_t *buf16 = (uint16_t *)buf;
int i;
- /*
- * verify that the len is a multiple of 4.
- * see comment in read_data_from_flash_mem()
- */
- BUG_ON((len % 4) != 0);
+ iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali),
+ denali->host + DENALI_HOST_ADDR);
- /* write the data to the flash memory */
- buf32 = (uint32_t *)buf;
- for (i = 0; i < len / 4; i++)
- iowrite32(*buf32++, denali->flash_mem + 0x10);
- return i * 4; /* intent is to return the number of bytes read */
+ for (i = 0; i < len / 2; i++)
+ buf16[i] = ioread32(denali->host + DENALI_HOST_DATA);
}
-/* helper function that simply reads a buffer from the flash */
-static int read_data_from_flash_mem(struct denali_nand_info *denali,
- uint8_t *buf, int len)
+static void denali_write_buf16(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
{
- uint32_t *buf32;
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ const uint16_t *buf16 = (const uint16_t *)buf;
int i;
- /*
- * we assume that len will be a multiple of 4, if not it would be nice
- * to know about it ASAP rather than have random failures...
- * This assumption is based on the fact that this function is designed
- * to be used to read flash pages, which are typically multiples of 4.
- */
- BUG_ON((len % 4) != 0);
+ iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali),
+ denali->host + DENALI_HOST_ADDR);
- /* transfer the data from the flash */
- buf32 = (uint32_t *)buf;
- for (i = 0; i < len / 4; i++)
- *buf32++ = ioread32(denali->flash_mem + 0x10);
- return i * 4; /* intent is to return the number of bytes read */
+ for (i = 0; i < len / 2; i++)
+ iowrite32(buf16[i], denali->host + DENALI_HOST_DATA);
}
-/* writes OOB data to the device */
-static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
+static uint8_t denali_read_byte(struct mtd_info *mtd)
{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- uint32_t irq_status;
- uint32_t irq_mask = INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL;
- int status = 0;
+ uint8_t byte;
- denali->page = page;
+ denali_read_buf(mtd, &byte, 1);
- if (denali_send_pipeline_cmd(denali, false, false, SPARE_ACCESS,
- DENALI_WRITE) == PASS) {
- write_data_to_flash_mem(denali, buf, mtd->oobsize);
+ return byte;
+}
- /* wait for operation to complete */
- irq_status = wait_for_irq(denali, irq_mask);
+static void denali_write_byte(struct mtd_info *mtd, uint8_t byte)
+{
+ denali_write_buf(mtd, &byte, 1);
+}
- if (irq_status == 0) {
- dev_err(denali->dev, "OOB write failed\n");
- status = -EIO;
- }
- } else {
- dev_err(denali->dev, "unable to send pipeline command\n");
- status = -EIO;
- }
- return status;
+static uint16_t denali_read_word(struct mtd_info *mtd)
+{
+ uint16_t word;
+
+ denali_read_buf16(mtd, (uint8_t *)&word, 2);
+
+ return word;
}
-/* reads OOB data from the device */
-static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
+static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- uint32_t irq_mask = INTR__LOAD_COMP;
- uint32_t irq_status, addr, cmd;
+ uint32_t type;
- denali->page = page;
+ if (ctrl & NAND_CLE)
+ type = DENALI_MAP11_CMD;
+ else if (ctrl & NAND_ALE)
+ type = DENALI_MAP11_ADDR;
+ else
+ return;
- if (denali_send_pipeline_cmd(denali, false, true, SPARE_ACCESS,
- DENALI_READ) == PASS) {
- read_data_from_flash_mem(denali, buf, mtd->oobsize);
+ /*
+ * Some commands are followed by chip->dev_ready or chip->waitfunc.
+ * irq_status must be cleared here to catch the R/B# interrupt later.
+ */
+ if (ctrl & NAND_CTRL_CHANGE)
+ denali_reset_irq(denali);
- /*
- * wait for command to be accepted
- * can always use status0 bit as the
- * mask is identical for each bank.
- */
- irq_status = wait_for_irq(denali, irq_mask);
+ denali_host_write(denali, DENALI_BANK(denali) | type, dat);
+}
- if (irq_status == 0)
- dev_err(denali->dev, "page on OOB timeout %d\n",
- denali->page);
+static int denali_dev_ready(struct mtd_info *mtd)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
- /*
- * We set the device back to MAIN_ACCESS here as I observed
- * instability with the controller if you do a block erase
- * and the last transaction was a SPARE_ACCESS. Block erase
- * is reliable (according to the MTD test infrastructure)
- * if you are in MAIN_ACCESS.
- */
- addr = BANK(denali->flash_bank) | denali->page;
- cmd = MODE_10 | addr;
- index_addr(denali, cmd, MAIN_ACCESS);
- }
+ return !!(denali_check_irq(denali) & INTR__INT_ACT);
}
static int denali_check_erased_page(struct mtd_info *mtd,
@@ -856,11 +372,11 @@ static int denali_hw_ecc_fixup(struct mtd_info *mtd,
unsigned long *uncor_ecc_flags)
{
struct nand_chip *chip = mtd_to_nand(mtd);
- int bank = denali->flash_bank;
+ int bank = denali->active_bank;
uint32_t ecc_cor;
unsigned int max_bitflips;
- ecc_cor = ioread32(denali->flash_reg + ECC_COR_INFO(bank));
+ ecc_cor = ioread32(denali->reg + ECC_COR_INFO(bank));
ecc_cor >>= ECC_COR_INFO__SHIFT(bank);
if (ecc_cor & ECC_COR_INFO__UNCOR_ERR) {
@@ -886,8 +402,6 @@ static int denali_hw_ecc_fixup(struct mtd_info *mtd,
return max_bitflips;
}
-#define ECC_SECTOR_SIZE 512
-
#define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12)
#define ECC_BYTE(x) (((x) & ECC_ERROR_ADDRESS__OFFSET))
#define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK)
@@ -899,22 +413,23 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
struct denali_nand_info *denali,
unsigned long *uncor_ecc_flags, uint8_t *buf)
{
+ unsigned int ecc_size = denali->nand.ecc.size;
unsigned int bitflips = 0;
unsigned int max_bitflips = 0;
uint32_t err_addr, err_cor_info;
unsigned int err_byte, err_sector, err_device;
uint8_t err_cor_value;
unsigned int prev_sector = 0;
+ uint32_t irq_status;
- /* read the ECC errors. we'll ignore them for now */
- denali_set_intr_modes(denali, false);
+ denali_reset_irq(denali);
do {
- err_addr = ioread32(denali->flash_reg + ECC_ERROR_ADDRESS);
+ err_addr = ioread32(denali->reg + ECC_ERROR_ADDRESS);
err_sector = ECC_SECTOR(err_addr);
err_byte = ECC_BYTE(err_addr);
- err_cor_info = ioread32(denali->flash_reg + ERR_CORRECTION_INFO);
+ err_cor_info = ioread32(denali->reg + ERR_CORRECTION_INFO);
err_cor_value = ECC_CORRECTION_VALUE(err_cor_info);
err_device = ECC_ERR_DEVICE(err_cor_info);
@@ -928,9 +443,9 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
* an erased sector.
*/
*uncor_ecc_flags |= BIT(err_sector);
- } else if (err_byte < ECC_SECTOR_SIZE) {
+ } else if (err_byte < ecc_size) {
/*
- * If err_byte is larger than ECC_SECTOR_SIZE, means error
+ * If err_byte is larger than ecc_size, means error
* happened in OOB, so we ignore it. It's no need for
* us to correct it err_device is represented the NAND
* error bits are happened in if there are more than
@@ -939,8 +454,8 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
int offset;
unsigned int flips_in_byte;
- offset = (err_sector * ECC_SECTOR_SIZE + err_byte) *
- denali->devnum + err_device;
+ offset = (err_sector * ecc_size + err_byte) *
+ denali->devs_per_cs + err_device;
/* correct the ECC error */
flips_in_byte = hweight8(buf[offset] ^ err_cor_value);
@@ -959,10 +474,9 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
* ECC_TRANSACTION_DONE interrupt, so here just wait for
* a while for this interrupt
*/
- while (!(read_interrupt_status(denali) & INTR__ECC_TRANSACTION_DONE))
- cpu_relax();
- clear_interrupts(denali);
- denali_set_intr_modes(denali, true);
+ irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE);
+ if (!(irq_status & INTR__ECC_TRANSACTION_DONE))
+ return -EIO;
return max_bitflips;
}
@@ -970,17 +484,17 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
/* programs the controller to either enable/disable DMA transfers */
static void denali_enable_dma(struct denali_nand_info *denali, bool en)
{
- iowrite32(en ? DMA_ENABLE__FLAG : 0, denali->flash_reg + DMA_ENABLE);
- ioread32(denali->flash_reg + DMA_ENABLE);
+ iowrite32(en ? DMA_ENABLE__FLAG : 0, denali->reg + DMA_ENABLE);
+ ioread32(denali->reg + DMA_ENABLE);
}
-static void denali_setup_dma64(struct denali_nand_info *denali, int op)
+static void denali_setup_dma64(struct denali_nand_info *denali,
+ dma_addr_t dma_addr, int page, int write)
{
uint32_t mode;
const int page_count = 1;
- uint64_t addr = denali->buf.dma_buf;
- mode = MODE_10 | BANK(denali->flash_bank) | denali->page;
+ mode = DENALI_MAP10 | DENALI_BANK(denali) | page;
/* DMA is a three step process */
@@ -988,191 +502,354 @@ static void denali_setup_dma64(struct denali_nand_info *denali, int op)
* 1. setup transfer type, interrupt when complete,
* burst len = 64 bytes, the number of pages
*/
- index_addr(denali, mode, 0x01002000 | (64 << 16) | op | page_count);
+ denali_host_write(denali, mode,
+ 0x01002000 | (64 << 16) | (write << 8) | page_count);
/* 2. set memory low address */
- index_addr(denali, mode, addr);
+ denali_host_write(denali, mode, dma_addr);
/* 3. set memory high address */
- index_addr(denali, mode, addr >> 32);
+ denali_host_write(denali, mode, (uint64_t)dma_addr >> 32);
}
-static void denali_setup_dma32(struct denali_nand_info *denali, int op)
+static void denali_setup_dma32(struct denali_nand_info *denali,
+ dma_addr_t dma_addr, int page, int write)
{
uint32_t mode;
const int page_count = 1;
- uint32_t addr = denali->buf.dma_buf;
- mode = MODE_10 | BANK(denali->flash_bank);
+ mode = DENALI_MAP10 | DENALI_BANK(denali);
/* DMA is a four step process */
/* 1. setup transfer type and # of pages */
- index_addr(denali, mode | denali->page, 0x2000 | op | page_count);
+ denali_host_write(denali, mode | page,
+ 0x2000 | (write << 8) | page_count);
/* 2. set memory high address bits 23:8 */
- index_addr(denali, mode | ((addr >> 16) << 8), 0x2200);
+ denali_host_write(denali, mode | ((dma_addr >> 16) << 8), 0x2200);
/* 3. set memory low address bits 23:8 */
- index_addr(denali, mode | ((addr & 0xffff) << 8), 0x2300);
+ denali_host_write(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300);
/* 4. interrupt when complete, burst len = 64 bytes */
- index_addr(denali, mode | 0x14000, 0x2400);
+ denali_host_write(denali, mode | 0x14000, 0x2400);
}
-static void denali_setup_dma(struct denali_nand_info *denali, int op)
+static void denali_setup_dma(struct denali_nand_info *denali,
+ dma_addr_t dma_addr, int page, int write)
{
if (denali->caps & DENALI_CAP_DMA_64BIT)
- denali_setup_dma64(denali, op);
+ denali_setup_dma64(denali, dma_addr, page, write);
else
- denali_setup_dma32(denali, op);
+ denali_setup_dma32(denali, dma_addr, page, write);
}
-/*
- * writes a page. user specifies type, and this function handles the
- * configuration details.
- */
-static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, bool raw_xfer)
+static int denali_pio_read(struct denali_nand_info *denali, void *buf,
+ size_t size, int page, int raw)
{
- struct denali_nand_info *denali = mtd_to_denali(mtd);
- dma_addr_t addr = denali->buf.dma_buf;
- size_t size = mtd->writesize + mtd->oobsize;
+ uint32_t addr = DENALI_BANK(denali) | page;
+ uint32_t *buf32 = (uint32_t *)buf;
+ uint32_t irq_status, ecc_err_mask;
+ int i;
+
+ if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
+ ecc_err_mask = INTR__ECC_UNCOR_ERR;
+ else
+ ecc_err_mask = INTR__ECC_ERR;
+
+ denali_reset_irq(denali);
+
+ iowrite32(DENALI_MAP01 | addr, denali->host + DENALI_HOST_ADDR);
+ for (i = 0; i < size / 4; i++)
+ *buf32++ = ioread32(denali->host + DENALI_HOST_DATA);
+
+ irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC);
+ if (!(irq_status & INTR__PAGE_XFER_INC))
+ return -EIO;
+
+ if (irq_status & INTR__ERASED_PAGE)
+ memset(buf, 0xff, size);
+
+ return irq_status & ecc_err_mask ? -EBADMSG : 0;
+}
+
+static int denali_pio_write(struct denali_nand_info *denali,
+ const void *buf, size_t size, int page, int raw)
+{
+ uint32_t addr = DENALI_BANK(denali) | page;
+ const uint32_t *buf32 = (uint32_t *)buf;
uint32_t irq_status;
- uint32_t irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
+ int i;
- /*
- * if it is a raw xfer, we want to disable ecc and send the spare area.
- * !raw_xfer - enable ecc
- * raw_xfer - transfer spare
- */
- setup_ecc_for_xfer(denali, !raw_xfer, raw_xfer);
+ denali_reset_irq(denali);
- /* copy buffer into DMA buffer */
- memcpy(denali->buf.buf, buf, mtd->writesize);
+ iowrite32(DENALI_MAP01 | addr, denali->host + DENALI_HOST_ADDR);
+ for (i = 0; i < size / 4; i++)
+ iowrite32(*buf32++, denali->host + DENALI_HOST_DATA);
- if (raw_xfer) {
- /* transfer the data to the spare area */
- memcpy(denali->buf.buf + mtd->writesize,
- chip->oob_poi,
- mtd->oobsize);
+ irq_status = denali_wait_for_irq(denali,
+ INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL);
+ if (!(irq_status & INTR__PROGRAM_COMP))
+ return -EIO;
+
+ return 0;
+}
+
+static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
+ size_t size, int page, int raw, int write)
+{
+ if (write)
+ return denali_pio_write(denali, buf, size, page, raw);
+ else
+ return denali_pio_read(denali, buf, size, page, raw);
+}
+
+static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
+ size_t size, int page, int raw, int write)
+{
+ dma_addr_t dma_addr;
+ uint32_t irq_mask, irq_status, ecc_err_mask;
+ enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+ int ret = 0;
+
+ dma_addr = dma_map_single(denali->dev, buf, size, dir);
+ if (dma_mapping_error(denali->dev, dma_addr)) {
+ dev_dbg(denali->dev, "Failed to DMA-map buffer. Trying PIO.\n");
+ return denali_pio_xfer(denali, buf, size, page, raw, write);
}
- dma_sync_single_for_device(denali->dev, addr, size, DMA_TO_DEVICE);
+ if (write) {
+ /*
+ * INTR__PROGRAM_COMP is never asserted for the DMA transfer.
+ * We can use INTR__DMA_CMD_COMP instead. This flag is asserted
+ * when the page program is completed.
+ */
+ irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
+ ecc_err_mask = 0;
+ } else if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) {
+ irq_mask = INTR__DMA_CMD_COMP;
+ ecc_err_mask = INTR__ECC_UNCOR_ERR;
+ } else {
+ irq_mask = INTR__DMA_CMD_COMP;
+ ecc_err_mask = INTR__ECC_ERR;
+ }
- clear_interrupts(denali);
denali_enable_dma(denali, true);
- denali_setup_dma(denali, DENALI_WRITE);
+ denali_reset_irq(denali);
+ denali_setup_dma(denali, dma_addr, page, write);
/* wait for operation to complete */
- irq_status = wait_for_irq(denali, irq_mask);
-
- if (irq_status == 0) {
- dev_err(denali->dev, "timeout on write_page (type = %d)\n",
- raw_xfer);
- denali->status = NAND_STATUS_FAIL;
- }
+ irq_status = denali_wait_for_irq(denali, irq_mask);
+ if (!(irq_status & INTR__DMA_CMD_COMP))
+ ret = -EIO;
+ else if (irq_status & ecc_err_mask)
+ ret = -EBADMSG;
denali_enable_dma(denali, false);
- dma_sync_single_for_cpu(denali->dev, addr, size, DMA_TO_DEVICE);
+ dma_unmap_single(denali->dev, dma_addr, size, dir);
- return 0;
-}
+ if (irq_status & INTR__ERASED_PAGE)
+ memset(buf, 0xff, size);
-/* NAND core entry points */
+ return ret;
+}
-/*
- * this is the callback that the NAND core calls to write a page. Since
- * writing a page with ECC or without is similar, all the work is done
- * by write_page above.
- */
-static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required, int page)
+static int denali_data_xfer(struct denali_nand_info *denali, void *buf,
+ size_t size, int page, int raw, int write)
{
- /*
- * for regular page writes, we let HW handle all the ECC
- * data written to the device.
- */
- return write_page(mtd, chip, buf, false);
+ setup_ecc_for_xfer(denali, !raw, raw);
+
+ if (denali->dma_avail)
+ return denali_dma_xfer(denali, buf, size, page, raw, write);
+ else
+ return denali_pio_xfer(denali, buf, size, page, raw, write);
}
-/*
- * This is the callback that the NAND core calls to write a page without ECC.
- * raw access is similar to ECC page writes, so all the work is done in the
- * write_page() function above.
- */
-static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required,
- int page)
+static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int write)
{
- /*
- * for raw page writes, we want to disable ECC and simply write
- * whatever data is in the buffer.
- */
- return write_page(mtd, chip, buf, true);
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ unsigned int start_cmd = write ? NAND_CMD_SEQIN : NAND_CMD_READ0;
+ unsigned int rnd_cmd = write ? NAND_CMD_RNDIN : NAND_CMD_RNDOUT;
+ int writesize = mtd->writesize;
+ int oobsize = mtd->oobsize;
+ uint8_t *bufpoi = chip->oob_poi;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ int oob_skip = denali->oob_skip_bytes;
+ size_t size = writesize + oobsize;
+ int i, pos, len;
+
+ /* BBM at the beginning of the OOB area */
+ chip->cmdfunc(mtd, start_cmd, writesize, page);
+ if (write)
+ chip->write_buf(mtd, bufpoi, oob_skip);
+ else
+ chip->read_buf(mtd, bufpoi, oob_skip);
+ bufpoi += oob_skip;
+
+ /* OOB ECC */
+ for (i = 0; i < ecc_steps; i++) {
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
+ len = ecc_bytes;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ chip->cmdfunc(mtd, rnd_cmd, pos, -1);
+ if (write)
+ chip->write_buf(mtd, bufpoi, len);
+ else
+ chip->read_buf(mtd, bufpoi, len);
+ bufpoi += len;
+ if (len < ecc_bytes) {
+ len = ecc_bytes - len;
+ chip->cmdfunc(mtd, rnd_cmd, writesize + oob_skip, -1);
+ if (write)
+ chip->write_buf(mtd, bufpoi, len);
+ else
+ chip->read_buf(mtd, bufpoi, len);
+ bufpoi += len;
+ }
+ }
+
+ /* OOB free */
+ len = oobsize - (bufpoi - chip->oob_poi);
+ chip->cmdfunc(mtd, rnd_cmd, size - len, -1);
+ if (write)
+ chip->write_buf(mtd, bufpoi, len);
+ else
+ chip->read_buf(mtd, bufpoi, len);
}
-static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
- int page)
+static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
{
- return write_oob_data(mtd, chip->oob_poi, page);
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ int writesize = mtd->writesize;
+ int oobsize = mtd->oobsize;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ void *dma_buf = denali->buf;
+ int oob_skip = denali->oob_skip_bytes;
+ size_t size = writesize + oobsize;
+ int ret, i, pos, len;
+
+ ret = denali_data_xfer(denali, dma_buf, size, page, 1, 0);
+ if (ret)
+ return ret;
+
+ /* Arrange the buffer for syndrome payload/ecc layout */
+ if (buf) {
+ for (i = 0; i < ecc_steps; i++) {
+ pos = i * (ecc_size + ecc_bytes);
+ len = ecc_size;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(buf, dma_buf + pos, len);
+ buf += len;
+ if (len < ecc_size) {
+ len = ecc_size - len;
+ memcpy(buf, dma_buf + writesize + oob_skip,
+ len);
+ buf += len;
+ }
+ }
+ }
+
+ if (oob_required) {
+ uint8_t *oob = chip->oob_poi;
+
+ /* BBM at the beginning of the OOB area */
+ memcpy(oob, dma_buf + writesize, oob_skip);
+ oob += oob_skip;
+
+ /* OOB ECC */
+ for (i = 0; i < ecc_steps; i++) {
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
+ len = ecc_bytes;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(oob, dma_buf + pos, len);
+ oob += len;
+ if (len < ecc_bytes) {
+ len = ecc_bytes - len;
+ memcpy(oob, dma_buf + writesize + oob_skip,
+ len);
+ oob += len;
+ }
+ }
+
+ /* OOB free */
+ len = oobsize - (oob - chip->oob_poi);
+ memcpy(oob, dma_buf + size - len, len);
+ }
+
+ return 0;
}
static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
- read_oob_data(mtd, chip->oob_poi, page);
+ denali_oob_xfer(mtd, chip, page, 0);
return 0;
}
-static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
+static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- dma_addr_t addr = denali->buf.dma_buf;
- size_t size = mtd->writesize + mtd->oobsize;
- uint32_t irq_status;
- uint32_t irq_mask = denali->caps & DENALI_CAP_HW_ECC_FIXUP ?
- INTR__DMA_CMD_COMP | INTR__ECC_UNCOR_ERR :
- INTR__ECC_TRANSACTION_DONE | INTR__ECC_ERR;
- unsigned long uncor_ecc_flags = 0;
- int stat = 0;
+ int status;
- if (page != denali->page) {
- dev_err(denali->dev,
- "IN %s: page %d is not equal to denali->page %d",
- __func__, page, denali->page);
- BUG();
- }
+ denali_reset_irq(denali);
- setup_ecc_for_xfer(denali, true, false);
+ denali_oob_xfer(mtd, chip, page, 1);
- denali_enable_dma(denali, true);
- dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
- clear_interrupts(denali);
- denali_setup_dma(denali, DENALI_READ);
-
- /* wait for operation to complete */
- irq_status = wait_for_irq(denali, irq_mask);
+ return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
- dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE);
+static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ unsigned long uncor_ecc_flags = 0;
+ int stat = 0;
+ int ret;
- memcpy(buf, denali->buf.buf, mtd->writesize);
+ ret = denali_data_xfer(denali, buf, mtd->writesize, page, 0, 0);
+ if (ret && ret != -EBADMSG)
+ return ret;
if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags);
- else if (irq_status & INTR__ECC_ERR)
+ else if (ret == -EBADMSG)
stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf);
- denali_enable_dma(denali, false);
if (stat < 0)
return stat;
if (uncor_ecc_flags) {
- read_oob_data(mtd, chip->oob_poi, denali->page);
+ ret = denali_read_oob(mtd, chip, page);
+ if (ret)
+ return ret;
stat = denali_check_erased_page(mtd, chip, buf,
uncor_ecc_flags, stat);
@@ -1181,137 +858,266 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
return stat;
}
-static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
+static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- dma_addr_t addr = denali->buf.dma_buf;
- size_t size = mtd->writesize + mtd->oobsize;
- uint32_t irq_mask = INTR__DMA_CMD_COMP;
-
- if (page != denali->page) {
- dev_err(denali->dev,
- "IN %s: page %d is not equal to denali->page %d",
- __func__, page, denali->page);
- BUG();
- }
-
- setup_ecc_for_xfer(denali, false, true);
- denali_enable_dma(denali, true);
-
- dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
-
- clear_interrupts(denali);
- denali_setup_dma(denali, DENALI_READ);
-
- /* wait for operation to complete */
- wait_for_irq(denali, irq_mask);
+ int writesize = mtd->writesize;
+ int oobsize = mtd->oobsize;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ void *dma_buf = denali->buf;
+ int oob_skip = denali->oob_skip_bytes;
+ size_t size = writesize + oobsize;
+ int i, pos, len;
- dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE);
+ /*
+ * Fill the buffer with 0xff first except the full page transfer.
+ * This simplifies the logic.
+ */
+ if (!buf || !oob_required)
+ memset(dma_buf, 0xff, size);
+
+ /* Arrange the buffer for syndrome payload/ecc layout */
+ if (buf) {
+ for (i = 0; i < ecc_steps; i++) {
+ pos = i * (ecc_size + ecc_bytes);
+ len = ecc_size;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(dma_buf + pos, buf, len);
+ buf += len;
+ if (len < ecc_size) {
+ len = ecc_size - len;
+ memcpy(dma_buf + writesize + oob_skip, buf,
+ len);
+ buf += len;
+ }
+ }
+ }
- denali_enable_dma(denali, false);
+ if (oob_required) {
+ const uint8_t *oob = chip->oob_poi;
+
+ /* BBM at the beginning of the OOB area */
+ memcpy(dma_buf + writesize, oob, oob_skip);
+ oob += oob_skip;
+
+ /* OOB ECC */
+ for (i = 0; i < ecc_steps; i++) {
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
+ len = ecc_bytes;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(dma_buf + pos, oob, len);
+ oob += len;
+ if (len < ecc_bytes) {
+ len = ecc_bytes - len;
+ memcpy(dma_buf + writesize + oob_skip, oob,
+ len);
+ oob += len;
+ }
+ }
- memcpy(buf, denali->buf.buf, mtd->writesize);
- memcpy(chip->oob_poi, denali->buf.buf + mtd->writesize, mtd->oobsize);
+ /* OOB free */
+ len = oobsize - (oob - chip->oob_poi);
+ memcpy(dma_buf + size - len, oob, len);
+ }
- return 0;
+ return denali_data_xfer(denali, dma_buf, size, page, 1, 1);
}
-static uint8_t denali_read_byte(struct mtd_info *mtd)
+static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- uint8_t result = 0xff;
-
- if (denali->buf.head < denali->buf.tail)
- result = denali->buf.buf[denali->buf.head++];
- return result;
+ return denali_data_xfer(denali, (void *)buf, mtd->writesize,
+ page, 0, 1);
}
static void denali_select_chip(struct mtd_info *mtd, int chip)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- spin_lock_irq(&denali->irq_lock);
- denali->flash_bank = chip;
- spin_unlock_irq(&denali->irq_lock);
+ denali->active_bank = chip;
}
static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- int status = denali->status;
+ uint32_t irq_status;
- denali->status = 0;
+ /* R/B# pin transitioned from low to high? */
+ irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
- return status;
+ return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL;
}
static int denali_erase(struct mtd_info *mtd, int page)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
+ uint32_t irq_status;
- uint32_t cmd, irq_status;
-
- clear_interrupts(denali);
+ denali_reset_irq(denali);
- /* setup page read request for access type */
- cmd = MODE_10 | BANK(denali->flash_bank) | page;
- index_addr(denali, cmd, 0x1);
+ denali_host_write(denali, DENALI_MAP10 | DENALI_BANK(denali) | page,
+ DENALI_ERASE);
/* wait for erase to complete or failure to occur */
- irq_status = wait_for_irq(denali, INTR__ERASE_COMP | INTR__ERASE_FAIL);
+ irq_status = denali_wait_for_irq(denali,
+ INTR__ERASE_COMP | INTR__ERASE_FAIL);
- return irq_status & INTR__ERASE_FAIL ? NAND_STATUS_FAIL : PASS;
+ return irq_status & INTR__ERASE_COMP ? 0 : NAND_STATUS_FAIL;
}
-static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
- int page)
+#define DIV_ROUND_DOWN_ULL(ll, d) \
+ ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; })
+
+static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
+ const struct nand_data_interface *conf)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
- uint32_t addr, id;
+ const struct nand_sdr_timings *timings;
+ unsigned long t_clk;
+ 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;
+ uint32_t tmp;
+
+ timings = nand_get_sdr_timings(conf);
+ if (IS_ERR(timings))
+ return PTR_ERR(timings);
+
+ /* clk_x period in picoseconds */
+ t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate);
+ if (!t_clk)
+ 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 = min_t(int, acc_clks, ACC_CLKS__VALUE);
+
+ tmp = ioread32(denali->reg + ACC_CLKS);
+ tmp &= ~ACC_CLKS__VALUE;
+ tmp |= acc_clks;
+ iowrite32(tmp, denali->reg + ACC_CLKS);
+
+ /* tRWH -> RE_2_WE */
+ re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk);
+ re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE);
+
+ tmp = ioread32(denali->reg + RE_2_WE);
+ tmp &= ~RE_2_WE__VALUE;
+ tmp |= re_2_we;
+ 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 = min_t(int, re_2_re, RE_2_RE__VALUE);
+
+ tmp = ioread32(denali->reg + RE_2_RE);
+ tmp &= ~RE_2_RE__VALUE;
+ tmp |= re_2_re;
+ iowrite32(tmp, denali->reg + RE_2_RE);
+
+ /* tWHR -> WE_2_RE */
+ we_2_re = DIV_ROUND_UP(timings->tWHR_min, t_clk);
+ 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);
+ tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE;
+ tmp |= we_2_re;
+ iowrite32(tmp, denali->reg + TWHR2_AND_WE_2_RE);
+
+ /* tADL -> ADDR_2_DATA */
+
+ /* for older versions, ADDR_2_DATA is only 6 bit wide */
+ addr_2_data_mask = TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA;
+ if (denali->revision < 0x0501)
+ addr_2_data_mask >>= 1;
+
+ addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk);
+ addr_2_data = min_t(int, addr_2_data, addr_2_data_mask);
+
+ tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA);
+ tmp &= ~addr_2_data_mask;
+ tmp |= addr_2_data;
+ iowrite32(tmp, denali->reg + TCWAW_AND_ADDR_2_DATA);
+
+ /* tREH, tWH -> RDWR_EN_HI_CNT */
+ rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min),
+ t_clk);
+ rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE);
+
+ tmp = ioread32(denali->reg + RDWR_EN_HI_CNT);
+ tmp &= ~RDWR_EN_HI_CNT__VALUE;
+ tmp |= rdwr_en_hi;
+ 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_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);
+ 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);
+
+ tmp = ioread32(denali->reg + RDWR_EN_LO_CNT);
+ tmp &= ~RDWR_EN_LO_CNT__VALUE;
+ tmp |= rdwr_en_lo;
+ 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,
+ 0);
+ cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE);
+
+ tmp = ioread32(denali->reg + CS_SETUP_CNT);
+ tmp &= ~CS_SETUP_CNT__VALUE;
+ tmp |= cs_setup;
+ iowrite32(tmp, denali->reg + CS_SETUP_CNT);
+
+ return 0;
+}
+
+static void denali_reset_banks(struct denali_nand_info *denali)
+{
+ u32 irq_status;
int i;
- switch (cmd) {
- case NAND_CMD_PAGEPROG:
- break;
- case NAND_CMD_STATUS:
- read_status(denali);
- break;
- case NAND_CMD_READID:
- case NAND_CMD_PARAM:
- reset_buf(denali);
- /*
- * sometimes ManufactureId read from register is not right
- * e.g. some of Micron MT29F32G08QAA MLC NAND chips
- * So here we send READID cmd to NAND insteand
- */
- addr = MODE_11 | BANK(denali->flash_bank);
- index_addr(denali, addr | 0, 0x90);
- index_addr(denali, addr | 1, col);
- for (i = 0; i < 8; i++) {
- index_addr_read_data(denali, addr | 2, &id);
- write_byte_to_buf(denali, id);
- }
- break;
- case NAND_CMD_READ0:
- case NAND_CMD_SEQIN:
- denali->page = page;
- break;
- case NAND_CMD_RESET:
- reset_bank(denali);
- break;
- case NAND_CMD_READOOB:
- /* TODO: Read OOB data */
- break;
- default:
- pr_err(": unsupported command received 0x%x\n", cmd);
- break;
+ for (i = 0; i < denali->max_banks; i++) {
+ denali->active_bank = i;
+
+ denali_reset_irq(denali);
+
+ iowrite32(DEVICE_RESET__BANK(i),
+ denali->reg + DEVICE_RESET);
+
+ irq_status = denali_wait_for_irq(denali,
+ INTR__RST_COMP | INTR__INT_ACT | INTR__TIME_OUT);
+ if (!(irq_status & INTR__INT_ACT))
+ break;
}
+
+ dev_dbg(denali->dev, "%d chips connected\n", i);
+ denali->max_banks = i;
}
-/* end NAND core entry points */
-/* Initialization code to bring the device up to a known good state */
static void denali_hw_init(struct denali_nand_info *denali)
{
/*
@@ -1319,8 +1125,7 @@ static void denali_hw_init(struct denali_nand_info *denali)
* override it.
*/
if (!denali->revision)
- denali->revision =
- swab16(ioread32(denali->flash_reg + REVISION));
+ denali->revision = swab16(ioread32(denali->reg + REVISION));
/*
* tell driver how many bit controller will skip before
@@ -1328,30 +1133,51 @@ static void denali_hw_init(struct denali_nand_info *denali)
* set by firmware. So we read this value out.
* if this value is 0, just let it be.
*/
- denali->bbtskipbytes = ioread32(denali->flash_reg +
- SPARE_AREA_SKIP_BYTES);
+ denali->oob_skip_bytes = ioread32(denali->reg + SPARE_AREA_SKIP_BYTES);
detect_max_banks(denali);
- denali_nand_reset(denali);
- iowrite32(0x0F, denali->flash_reg + RB_PIN_ENABLED);
- iowrite32(CHIP_EN_DONT_CARE__FLAG,
- denali->flash_reg + CHIP_ENABLE_DONT_CARE);
+ iowrite32(0x0F, denali->reg + RB_PIN_ENABLED);
+ iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE);
- iowrite32(0xffff, denali->flash_reg + SPARE_AREA_MARKER);
+ iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER);
/* Should set value for these registers when init */
- iowrite32(0, denali->flash_reg + TWO_ROW_ADDR_CYCLES);
- iowrite32(1, denali->flash_reg + ECC_ENABLE);
- denali_nand_timing_set(denali);
- denali_irq_init(denali);
+ iowrite32(0, denali->reg + TWO_ROW_ADDR_CYCLES);
+ iowrite32(1, denali->reg + ECC_ENABLE);
}
-/*
- * Althogh controller spec said SLC ECC is forceb to be 4bit,
- * but denali controller in MRST only support 15bit and 8bit ECC
- * correction
- */
-#define ECC_8BITS 14
-#define ECC_15BITS 26
+int denali_calc_ecc_bytes(int step_size, int strength)
+{
+ /* BCH code. Denali requires ecc.bytes to be multiple of 2 */
+ return DIV_ROUND_UP(strength * fls(step_size * 8), 16) * 2;
+}
+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)
@@ -1362,7 +1188,7 @@ static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
if (section)
return -ERANGE;
- oobregion->offset = denali->bbtskipbytes;
+ oobregion->offset = denali->oob_skip_bytes;
oobregion->length = chip->ecc.total;
return 0;
@@ -1377,7 +1203,7 @@ static int denali_ooblayout_free(struct mtd_info *mtd, int section,
if (section)
return -ERANGE;
- oobregion->offset = chip->ecc.total + denali->bbtskipbytes;
+ oobregion->offset = chip->ecc.total + denali->oob_skip_bytes;
oobregion->length = mtd->oobsize - oobregion->offset;
return 0;
@@ -1388,29 +1214,6 @@ static const struct mtd_ooblayout_ops denali_ooblayout_ops = {
.free = denali_ooblayout_free,
};
-static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
-static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
-
-static struct nand_bbt_descr bbt_main_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
- .offs = 8,
- .len = 4,
- .veroffs = 12,
- .maxblocks = 4,
- .pattern = bbt_pattern,
-};
-
-static struct nand_bbt_descr bbt_mirror_descr = {
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
- | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
- .offs = 8,
- .len = 4,
- .veroffs = 12,
- .maxblocks = 4,
- .pattern = mirror_pattern,
-};
-
/* initialize driver data structures */
static void denali_drv_init(struct denali_nand_info *denali)
{
@@ -1425,12 +1228,6 @@ static void denali_drv_init(struct denali_nand_info *denali)
* element that might be access shared data (interrupt status)
*/
spin_lock_init(&denali->irq_lock);
-
- /* indicate that MTD has not selected a valid bank yet */
- denali->flash_bank = CHIP_SELECT_INVALID;
-
- /* initialize our irq_status variable to indicate no interrupts */
- denali->irq_status = 0;
}
static int denali_multidev_fixup(struct denali_nand_info *denali)
@@ -1445,23 +1242,23 @@ static int denali_multidev_fixup(struct denali_nand_info *denali)
* In this case, the core framework knows nothing about this fact,
* so we should tell it the _logical_ pagesize and anything necessary.
*/
- denali->devnum = ioread32(denali->flash_reg + DEVICES_CONNECTED);
+ denali->devs_per_cs = ioread32(denali->reg + DEVICES_CONNECTED);
/*
* On some SoCs, DEVICES_CONNECTED is not auto-detected.
* For those, DEVICES_CONNECTED is left to 0. Set 1 if it is the case.
*/
- if (denali->devnum == 0) {
- denali->devnum = 1;
- iowrite32(1, denali->flash_reg + DEVICES_CONNECTED);
+ if (denali->devs_per_cs == 0) {
+ denali->devs_per_cs = 1;
+ iowrite32(1, denali->reg + DEVICES_CONNECTED);
}
- if (denali->devnum == 1)
+ if (denali->devs_per_cs == 1)
return 0;
- if (denali->devnum != 2) {
+ if (denali->devs_per_cs != 2) {
dev_err(denali->dev, "unsupported number of devices %d\n",
- denali->devnum);
+ denali->devs_per_cs);
return -EINVAL;
}
@@ -1479,7 +1276,7 @@ static int denali_multidev_fixup(struct denali_nand_info *denali)
chip->ecc.size <<= 1;
chip->ecc.bytes <<= 1;
chip->ecc.strength <<= 1;
- denali->bbtskipbytes <<= 1;
+ denali->oob_skip_bytes <<= 1;
return 0;
}
@@ -1490,27 +1287,12 @@ int denali_init(struct denali_nand_info *denali)
struct mtd_info *mtd = nand_to_mtd(chip);
int ret;
- if (denali->platform == INTEL_CE4100) {
- /*
- * Due to a silicon limitation, we can only support
- * ONFI timing mode 1 and below.
- */
- if (onfi_timing_mode < -1 || onfi_timing_mode > 1) {
- pr_err("Intel CE4100 only supports ONFI timing mode 1 or below\n");
- return -EINVAL;
- }
- }
-
- /* allocate a temporary buffer for nand_scan_ident() */
- denali->buf.buf = devm_kzalloc(denali->dev, PAGE_SIZE,
- GFP_DMA | GFP_KERNEL);
- if (!denali->buf.buf)
- return -ENOMEM;
-
mtd->dev.parent = denali->dev;
denali_hw_init(denali);
denali_drv_init(denali);
+ denali_clear_irq_all(denali);
+
/* Request IRQ after all the hardware initialization is finished */
ret = devm_request_irq(denali->dev, denali->irq, denali_isr,
IRQF_SHARED, DENALI_NAND_NAME, denali);
@@ -1519,8 +1301,11 @@ int denali_init(struct denali_nand_info *denali)
return ret;
}
- /* now that our ISR is registered, we can enable interrupts */
- denali_set_intr_modes(denali, true);
+ 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)
@@ -1528,10 +1313,17 @@ int denali_init(struct denali_nand_info *denali)
/* register the driver with the NAND core subsystem */
chip->select_chip = denali_select_chip;
- chip->cmdfunc = denali_cmdfunc;
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;
+ /* clk rate info is needed for setup_data_interface */
+ if (denali->clk_x_rate)
+ chip->setup_data_interface = denali_setup_data_interface;
+
/*
* scan for NAND devices attached to the controller
* this is the first stage in a two step process to register
@@ -1539,33 +1331,25 @@ int denali_init(struct denali_nand_info *denali)
*/
ret = nand_scan_ident(mtd, denali->max_banks, NULL);
if (ret)
- goto failed_req_irq;
-
- /* allocate the right size buffer now */
- devm_kfree(denali->dev, denali->buf.buf);
- denali->buf.buf = devm_kzalloc(denali->dev,
- mtd->writesize + mtd->oobsize,
- GFP_KERNEL);
- if (!denali->buf.buf) {
- ret = -ENOMEM;
- goto failed_req_irq;
- }
+ goto disable_irq;
- ret = dma_set_mask(denali->dev,
- DMA_BIT_MASK(denali->caps & DENALI_CAP_DMA_64BIT ?
- 64 : 32));
- if (ret) {
- dev_err(denali->dev, "No usable DMA configuration\n");
- goto failed_req_irq;
+ if (ioread32(denali->reg + FEATURES) & FEATURES__DMA)
+ denali->dma_avail = 1;
+
+ if (denali->dma_avail) {
+ int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32;
+
+ ret = dma_set_mask(denali->dev, DMA_BIT_MASK(dma_bit));
+ if (ret) {
+ dev_info(denali->dev,
+ "Failed to set DMA mask. Disabling DMA.\n");
+ denali->dma_avail = 0;
+ }
}
- denali->buf.dma_buf = dma_map_single(denali->dev, denali->buf.buf,
- mtd->writesize + mtd->oobsize,
- DMA_BIDIRECTIONAL);
- if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) {
- dev_err(denali->dev, "Failed to map DMA buffer\n");
- ret = -EIO;
- goto failed_req_irq;
+ if (denali->dma_avail) {
+ chip->options |= NAND_USE_BOUNCE_BUFFER;
+ chip->buf_align = 16;
}
/*
@@ -1574,46 +1358,49 @@ int denali_init(struct denali_nand_info *denali)
* bad block management.
*/
- /* Bad block management */
- chip->bbt_td = &bbt_main_descr;
- chip->bbt_md = &bbt_mirror_descr;
-
- /* skip the scan for now until we have OOB read and write support */
chip->bbt_options |= NAND_BBT_USE_FLASH;
- chip->options |= NAND_SKIP_BBTSCAN;
+ chip->bbt_options |= NAND_BBT_NO_OOB;
+
chip->ecc.mode = NAND_ECC_HW_SYNDROME;
/* no subpage writes on denali */
chip->options |= NAND_NO_SUBPAGE_WRITE;
- /*
- * Denali Controller only support 15bit and 8bit ECC in MRST,
- * so just let controller do 15bit ECC for MLC and 8bit ECC for
- * SLC if possible.
- * */
- if (!nand_is_slc(chip) &&
- (mtd->oobsize > (denali->bbtskipbytes +
- ECC_15BITS * (mtd->writesize /
- ECC_SECTOR_SIZE)))) {
- /* if MLC OOB size is large enough, use 15bit ECC*/
- chip->ecc.strength = 15;
- chip->ecc.bytes = ECC_15BITS;
- iowrite32(15, denali->flash_reg + ECC_CORRECTION);
- } else if (mtd->oobsize < (denali->bbtskipbytes +
- ECC_8BITS * (mtd->writesize /
- ECC_SECTOR_SIZE))) {
- pr_err("Your NAND chip OOB is not large enough to contain 8bit ECC correction codes");
- goto failed_req_irq;
- } else {
- chip->ecc.strength = 8;
- chip->ecc.bytes = ECC_8BITS;
- iowrite32(8, denali->flash_reg + ECC_CORRECTION);
+ ret = denali_ecc_setup(mtd, chip, denali);
+ if (ret) {
+ dev_err(denali->dev, "Failed to setup ECC settings.\n");
+ goto disable_irq;
}
+ dev_dbg(denali->dev,
+ "chosen ECC settings: step=%d, strength=%d, bytes=%d\n",
+ chip->ecc.size, chip->ecc.strength, chip->ecc.bytes);
+
+ iowrite32(MAKE_ECC_CORRECTION(chip->ecc.strength, 1),
+ denali->reg + ECC_CORRECTION);
+ iowrite32(mtd->erasesize / mtd->writesize,
+ denali->reg + PAGES_PER_BLOCK);
+ iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0,
+ denali->reg + DEVICE_WIDTH);
+ iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE);
+ iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE);
+
+ iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE);
+ iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE);
+ /* chip->ecc.steps is set by nand_scan_tail(); not available here */
+ iowrite32(mtd->writesize / chip->ecc.size,
+ denali->reg + CFG_NUM_DATA_BLOCKS);
+
mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
- /* override the default read operations */
- chip->ecc.size = ECC_SECTOR_SIZE;
+ if (chip->options & NAND_BUSWIDTH_16) {
+ chip->read_buf = denali_read_buf16;
+ chip->write_buf = denali_write_buf16;
+ } else {
+ chip->read_buf = denali_read_buf;
+ chip->write_buf = denali_write_buf;
+ }
+ chip->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS;
chip->ecc.read_page = denali_read_page;
chip->ecc.read_page_raw = denali_read_page_raw;
chip->ecc.write_page = denali_write_page;
@@ -1624,21 +1411,34 @@ int denali_init(struct denali_nand_info *denali)
ret = denali_multidev_fixup(denali);
if (ret)
- goto failed_req_irq;
+ goto disable_irq;
+
+ /*
+ * This buffer is DMA-mapped by denali_{read,write}_page_raw. Do not
+ * use devm_kmalloc() because the memory allocated by devm_ does not
+ * guarantee DMA-safe alignment.
+ */
+ denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+ if (!denali->buf) {
+ ret = -ENOMEM;
+ goto disable_irq;
+ }
ret = nand_scan_tail(mtd);
if (ret)
- goto failed_req_irq;
+ goto free_buf;
ret = mtd_device_register(mtd, NULL, 0);
if (ret) {
dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
- goto failed_req_irq;
+ goto free_buf;
}
return 0;
-failed_req_irq:
- denali_irq_cleanup(denali->irq, denali);
+free_buf:
+ kfree(denali->buf);
+disable_irq:
+ denali_disable_irq(denali);
return ret;
}
@@ -1648,16 +1448,9 @@ EXPORT_SYMBOL(denali_init);
void denali_remove(struct denali_nand_info *denali)
{
struct mtd_info *mtd = nand_to_mtd(&denali->nand);
- /*
- * Pre-compute DMA buffer size to avoid any problems in case
- * nand_release() ever changes in a way that mtd->writesize and
- * mtd->oobsize are not reliable after this call.
- */
- int bufsize = mtd->writesize + mtd->oobsize;
nand_release(mtd);
- denali_irq_cleanup(denali->irq, denali);
- dma_unmap_single(denali->dev, denali->buf.dma_buf, bufsize,
- DMA_BIDIRECTIONAL);
+ kfree(denali->buf);
+ denali_disable_irq(denali);
}
EXPORT_SYMBOL(denali_remove);
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index ec004850652a..237cc706b0fb 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -24,330 +24,315 @@
#include <linux/mtd/nand.h>
#define DEVICE_RESET 0x0
-#define DEVICE_RESET__BANK0 0x0001
-#define DEVICE_RESET__BANK1 0x0002
-#define DEVICE_RESET__BANK2 0x0004
-#define DEVICE_RESET__BANK3 0x0008
+#define DEVICE_RESET__BANK(bank) BIT(bank)
#define TRANSFER_SPARE_REG 0x10
-#define TRANSFER_SPARE_REG__FLAG 0x0001
+#define TRANSFER_SPARE_REG__FLAG BIT(0)
#define LOAD_WAIT_CNT 0x20
-#define LOAD_WAIT_CNT__VALUE 0xffff
+#define LOAD_WAIT_CNT__VALUE GENMASK(15, 0)
#define PROGRAM_WAIT_CNT 0x30
-#define PROGRAM_WAIT_CNT__VALUE 0xffff
+#define PROGRAM_WAIT_CNT__VALUE GENMASK(15, 0)
#define ERASE_WAIT_CNT 0x40
-#define ERASE_WAIT_CNT__VALUE 0xffff
+#define ERASE_WAIT_CNT__VALUE GENMASK(15, 0)
#define INT_MON_CYCCNT 0x50
-#define INT_MON_CYCCNT__VALUE 0xffff
+#define INT_MON_CYCCNT__VALUE GENMASK(15, 0)
#define RB_PIN_ENABLED 0x60
-#define RB_PIN_ENABLED__BANK0 0x0001
-#define RB_PIN_ENABLED__BANK1 0x0002
-#define RB_PIN_ENABLED__BANK2 0x0004
-#define RB_PIN_ENABLED__BANK3 0x0008
+#define RB_PIN_ENABLED__BANK(bank) BIT(bank)
#define MULTIPLANE_OPERATION 0x70
-#define MULTIPLANE_OPERATION__FLAG 0x0001
+#define MULTIPLANE_OPERATION__FLAG BIT(0)
#define MULTIPLANE_READ_ENABLE 0x80
-#define MULTIPLANE_READ_ENABLE__FLAG 0x0001
+#define MULTIPLANE_READ_ENABLE__FLAG BIT(0)
#define COPYBACK_DISABLE 0x90
-#define COPYBACK_DISABLE__FLAG 0x0001
+#define COPYBACK_DISABLE__FLAG BIT(0)
#define CACHE_WRITE_ENABLE 0xa0
-#define CACHE_WRITE_ENABLE__FLAG 0x0001
+#define CACHE_WRITE_ENABLE__FLAG BIT(0)
#define CACHE_READ_ENABLE 0xb0
-#define CACHE_READ_ENABLE__FLAG 0x0001
+#define CACHE_READ_ENABLE__FLAG BIT(0)
#define PREFETCH_MODE 0xc0
-#define PREFETCH_MODE__PREFETCH_EN 0x0001
-#define PREFETCH_MODE__PREFETCH_BURST_LENGTH 0xfff0
+#define PREFETCH_MODE__PREFETCH_EN BIT(0)
+#define PREFETCH_MODE__PREFETCH_BURST_LENGTH GENMASK(15, 4)
#define CHIP_ENABLE_DONT_CARE 0xd0
-#define CHIP_EN_DONT_CARE__FLAG 0x01
+#define CHIP_EN_DONT_CARE__FLAG BIT(0)
#define ECC_ENABLE 0xe0
-#define ECC_ENABLE__FLAG 0x0001
+#define ECC_ENABLE__FLAG BIT(0)
#define GLOBAL_INT_ENABLE 0xf0
-#define GLOBAL_INT_EN_FLAG 0x01
+#define GLOBAL_INT_EN_FLAG BIT(0)
-#define WE_2_RE 0x100
-#define WE_2_RE__VALUE 0x003f
+#define TWHR2_AND_WE_2_RE 0x100
+#define TWHR2_AND_WE_2_RE__WE_2_RE GENMASK(5, 0)
+#define TWHR2_AND_WE_2_RE__TWHR2 GENMASK(13, 8)
-#define ADDR_2_DATA 0x110
-#define ADDR_2_DATA__VALUE 0x003f
+#define TCWAW_AND_ADDR_2_DATA 0x110
+/* The width of ADDR_2_DATA is 6 bit for old IP, 7 bit for new IP */
+#define TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA GENMASK(6, 0)
+#define TCWAW_AND_ADDR_2_DATA__TCWAW GENMASK(13, 8)
#define RE_2_WE 0x120
-#define RE_2_WE__VALUE 0x003f
+#define RE_2_WE__VALUE GENMASK(5, 0)
#define ACC_CLKS 0x130
-#define ACC_CLKS__VALUE 0x000f
+#define ACC_CLKS__VALUE GENMASK(3, 0)
#define NUMBER_OF_PLANES 0x140
-#define NUMBER_OF_PLANES__VALUE 0x0007
+#define NUMBER_OF_PLANES__VALUE GENMASK(2, 0)
#define PAGES_PER_BLOCK 0x150
-#define PAGES_PER_BLOCK__VALUE 0xffff
+#define PAGES_PER_BLOCK__VALUE GENMASK(15, 0)
#define DEVICE_WIDTH 0x160
-#define DEVICE_WIDTH__VALUE 0x0003
+#define DEVICE_WIDTH__VALUE GENMASK(1, 0)
#define DEVICE_MAIN_AREA_SIZE 0x170
-#define DEVICE_MAIN_AREA_SIZE__VALUE 0xffff
+#define DEVICE_MAIN_AREA_SIZE__VALUE GENMASK(15, 0)
#define DEVICE_SPARE_AREA_SIZE 0x180
-#define DEVICE_SPARE_AREA_SIZE__VALUE 0xffff
+#define DEVICE_SPARE_AREA_SIZE__VALUE GENMASK(15, 0)
#define TWO_ROW_ADDR_CYCLES 0x190
-#define TWO_ROW_ADDR_CYCLES__FLAG 0x0001
+#define TWO_ROW_ADDR_CYCLES__FLAG BIT(0)
#define MULTIPLANE_ADDR_RESTRICT 0x1a0
-#define MULTIPLANE_ADDR_RESTRICT__FLAG 0x0001
+#define MULTIPLANE_ADDR_RESTRICT__FLAG BIT(0)
#define ECC_CORRECTION 0x1b0
-#define ECC_CORRECTION__VALUE 0x001f
+#define ECC_CORRECTION__VALUE GENMASK(4, 0)
+#define ECC_CORRECTION__ERASE_THRESHOLD GENMASK(31, 16)
+#define MAKE_ECC_CORRECTION(val, thresh) \
+ (((val) & (ECC_CORRECTION__VALUE)) | \
+ (((thresh) << 16) & (ECC_CORRECTION__ERASE_THRESHOLD)))
#define READ_MODE 0x1c0
-#define READ_MODE__VALUE 0x000f
+#define READ_MODE__VALUE GENMASK(3, 0)
#define WRITE_MODE 0x1d0
-#define WRITE_MODE__VALUE 0x000f
+#define WRITE_MODE__VALUE GENMASK(3, 0)
#define COPYBACK_MODE 0x1e0
-#define COPYBACK_MODE__VALUE 0x000f
+#define COPYBACK_MODE__VALUE GENMASK(3, 0)
#define RDWR_EN_LO_CNT 0x1f0
-#define RDWR_EN_LO_CNT__VALUE 0x001f
+#define RDWR_EN_LO_CNT__VALUE GENMASK(4, 0)
#define RDWR_EN_HI_CNT 0x200
-#define RDWR_EN_HI_CNT__VALUE 0x001f
+#define RDWR_EN_HI_CNT__VALUE GENMASK(4, 0)
#define MAX_RD_DELAY 0x210
-#define MAX_RD_DELAY__VALUE 0x000f
+#define MAX_RD_DELAY__VALUE GENMASK(3, 0)
#define CS_SETUP_CNT 0x220
-#define CS_SETUP_CNT__VALUE 0x001f
+#define CS_SETUP_CNT__VALUE GENMASK(4, 0)
+#define CS_SETUP_CNT__TWB GENMASK(17, 12)
#define SPARE_AREA_SKIP_BYTES 0x230
-#define SPARE_AREA_SKIP_BYTES__VALUE 0x003f
+#define SPARE_AREA_SKIP_BYTES__VALUE GENMASK(5, 0)
#define SPARE_AREA_MARKER 0x240
-#define SPARE_AREA_MARKER__VALUE 0xffff
+#define SPARE_AREA_MARKER__VALUE GENMASK(15, 0)
#define DEVICES_CONNECTED 0x250
-#define DEVICES_CONNECTED__VALUE 0x0007
+#define DEVICES_CONNECTED__VALUE GENMASK(2, 0)
#define DIE_MASK 0x260
-#define DIE_MASK__VALUE 0x00ff
+#define DIE_MASK__VALUE GENMASK(7, 0)
#define FIRST_BLOCK_OF_NEXT_PLANE 0x270
-#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE 0xffff
+#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE GENMASK(15, 0)
#define WRITE_PROTECT 0x280
-#define WRITE_PROTECT__FLAG 0x0001
+#define WRITE_PROTECT__FLAG BIT(0)
#define RE_2_RE 0x290
-#define RE_2_RE__VALUE 0x003f
+#define RE_2_RE__VALUE GENMASK(5, 0)
#define MANUFACTURER_ID 0x300
-#define MANUFACTURER_ID__VALUE 0x00ff
+#define MANUFACTURER_ID__VALUE GENMASK(7, 0)
#define DEVICE_ID 0x310
-#define DEVICE_ID__VALUE 0x00ff
+#define DEVICE_ID__VALUE GENMASK(7, 0)
#define DEVICE_PARAM_0 0x320
-#define DEVICE_PARAM_0__VALUE 0x00ff
+#define DEVICE_PARAM_0__VALUE GENMASK(7, 0)
#define DEVICE_PARAM_1 0x330
-#define DEVICE_PARAM_1__VALUE 0x00ff
+#define DEVICE_PARAM_1__VALUE GENMASK(7, 0)
#define DEVICE_PARAM_2 0x340
-#define DEVICE_PARAM_2__VALUE 0x00ff
+#define DEVICE_PARAM_2__VALUE GENMASK(7, 0)
#define LOGICAL_PAGE_DATA_SIZE 0x350
-#define LOGICAL_PAGE_DATA_SIZE__VALUE 0xffff
+#define LOGICAL_PAGE_DATA_SIZE__VALUE GENMASK(15, 0)
#define LOGICAL_PAGE_SPARE_SIZE 0x360
-#define LOGICAL_PAGE_SPARE_SIZE__VALUE 0xffff
+#define LOGICAL_PAGE_SPARE_SIZE__VALUE GENMASK(15, 0)
#define REVISION 0x370
-#define REVISION__VALUE 0xffff
+#define REVISION__VALUE GENMASK(15, 0)
#define ONFI_DEVICE_FEATURES 0x380
-#define ONFI_DEVICE_FEATURES__VALUE 0x003f
+#define ONFI_DEVICE_FEATURES__VALUE GENMASK(5, 0)
#define ONFI_OPTIONAL_COMMANDS 0x390
-#define ONFI_OPTIONAL_COMMANDS__VALUE 0x003f
+#define ONFI_OPTIONAL_COMMANDS__VALUE GENMASK(5, 0)
#define ONFI_TIMING_MODE 0x3a0
-#define ONFI_TIMING_MODE__VALUE 0x003f
+#define ONFI_TIMING_MODE__VALUE GENMASK(5, 0)
#define ONFI_PGM_CACHE_TIMING_MODE 0x3b0
-#define ONFI_PGM_CACHE_TIMING_MODE__VALUE 0x003f
+#define ONFI_PGM_CACHE_TIMING_MODE__VALUE GENMASK(5, 0)
#define ONFI_DEVICE_NO_OF_LUNS 0x3c0
-#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS 0x00ff
-#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE 0x0100
+#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS GENMASK(7, 0)
+#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE BIT(8)
#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L 0x3d0
-#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE 0xffff
+#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE GENMASK(15, 0)
#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U 0x3e0
-#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE 0xffff
-
-#define FEATURES 0x3f0
-#define FEATURES__N_BANKS 0x0003
-#define FEATURES__ECC_MAX_ERR 0x003c
-#define FEATURES__DMA 0x0040
-#define FEATURES__CMD_DMA 0x0080
-#define FEATURES__PARTITION 0x0100
-#define FEATURES__XDMA_SIDEBAND 0x0200
-#define FEATURES__GPREG 0x0400
-#define FEATURES__INDEX_ADDR 0x0800
+#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE GENMASK(15, 0)
+
+#define FEATURES 0x3f0
+#define FEATURES__N_BANKS GENMASK(1, 0)
+#define FEATURES__ECC_MAX_ERR GENMASK(5, 2)
+#define FEATURES__DMA BIT(6)
+#define FEATURES__CMD_DMA BIT(7)
+#define FEATURES__PARTITION BIT(8)
+#define FEATURES__XDMA_SIDEBAND BIT(9)
+#define FEATURES__GPREG BIT(10)
+#define FEATURES__INDEX_ADDR BIT(11)
#define TRANSFER_MODE 0x400
-#define TRANSFER_MODE__VALUE 0x0003
+#define TRANSFER_MODE__VALUE GENMASK(1, 0)
-#define INTR_STATUS(__bank) (0x410 + ((__bank) * 0x50))
-#define INTR_EN(__bank) (0x420 + ((__bank) * 0x50))
+#define INTR_STATUS(bank) (0x410 + (bank) * 0x50)
+#define INTR_EN(bank) (0x420 + (bank) * 0x50)
/* bit[1:0] is used differently depending on IP version */
-#define INTR__ECC_UNCOR_ERR 0x0001 /* new IP */
-#define INTR__ECC_TRANSACTION_DONE 0x0001 /* old IP */
-#define INTR__ECC_ERR 0x0002 /* old IP */
-#define INTR__DMA_CMD_COMP 0x0004
-#define INTR__TIME_OUT 0x0008
-#define INTR__PROGRAM_FAIL 0x0010
-#define INTR__ERASE_FAIL 0x0020
-#define INTR__LOAD_COMP 0x0040
-#define INTR__PROGRAM_COMP 0x0080
-#define INTR__ERASE_COMP 0x0100
-#define INTR__PIPE_CPYBCK_CMD_COMP 0x0200
-#define INTR__LOCKED_BLK 0x0400
-#define INTR__UNSUP_CMD 0x0800
-#define INTR__INT_ACT 0x1000
-#define INTR__RST_COMP 0x2000
-#define INTR__PIPE_CMD_ERR 0x4000
-#define INTR__PAGE_XFER_INC 0x8000
-
-#define PAGE_CNT(__bank) (0x430 + ((__bank) * 0x50))
-#define ERR_PAGE_ADDR(__bank) (0x440 + ((__bank) * 0x50))
-#define ERR_BLOCK_ADDR(__bank) (0x450 + ((__bank) * 0x50))
+#define INTR__ECC_UNCOR_ERR BIT(0) /* new IP */
+#define INTR__ECC_TRANSACTION_DONE BIT(0) /* old IP */
+#define INTR__ECC_ERR BIT(1) /* old IP */
+#define INTR__DMA_CMD_COMP BIT(2)
+#define INTR__TIME_OUT BIT(3)
+#define INTR__PROGRAM_FAIL BIT(4)
+#define INTR__ERASE_FAIL BIT(5)
+#define INTR__LOAD_COMP BIT(6)
+#define INTR__PROGRAM_COMP BIT(7)
+#define INTR__ERASE_COMP BIT(8)
+#define INTR__PIPE_CPYBCK_CMD_COMP BIT(9)
+#define INTR__LOCKED_BLK BIT(10)
+#define INTR__UNSUP_CMD BIT(11)
+#define INTR__INT_ACT BIT(12)
+#define INTR__RST_COMP BIT(13)
+#define INTR__PIPE_CMD_ERR BIT(14)
+#define INTR__PAGE_XFER_INC BIT(15)
+#define INTR__ERASED_PAGE BIT(16)
+
+#define PAGE_CNT(bank) (0x430 + (bank) * 0x50)
+#define ERR_PAGE_ADDR(bank) (0x440 + (bank) * 0x50)
+#define ERR_BLOCK_ADDR(bank) (0x450 + (bank) * 0x50)
#define ECC_THRESHOLD 0x600
-#define ECC_THRESHOLD__VALUE 0x03ff
+#define ECC_THRESHOLD__VALUE GENMASK(9, 0)
#define ECC_ERROR_BLOCK_ADDRESS 0x610
-#define ECC_ERROR_BLOCK_ADDRESS__VALUE 0xffff
+#define ECC_ERROR_BLOCK_ADDRESS__VALUE GENMASK(15, 0)
#define ECC_ERROR_PAGE_ADDRESS 0x620
-#define ECC_ERROR_PAGE_ADDRESS__VALUE 0x0fff
-#define ECC_ERROR_PAGE_ADDRESS__BANK 0xf000
+#define ECC_ERROR_PAGE_ADDRESS__VALUE GENMASK(11, 0)
+#define ECC_ERROR_PAGE_ADDRESS__BANK GENMASK(15, 12)
#define ECC_ERROR_ADDRESS 0x630
-#define ECC_ERROR_ADDRESS__OFFSET 0x0fff
-#define ECC_ERROR_ADDRESS__SECTOR_NR 0xf000
+#define ECC_ERROR_ADDRESS__OFFSET GENMASK(11, 0)
+#define ECC_ERROR_ADDRESS__SECTOR_NR GENMASK(15, 12)
#define ERR_CORRECTION_INFO 0x640
-#define ERR_CORRECTION_INFO__BYTEMASK 0x00ff
-#define ERR_CORRECTION_INFO__DEVICE_NR 0x0f00
-#define ERR_CORRECTION_INFO__ERROR_TYPE 0x4000
-#define ERR_CORRECTION_INFO__LAST_ERR_INFO 0x8000
+#define ERR_CORRECTION_INFO__BYTEMASK GENMASK(7, 0)
+#define ERR_CORRECTION_INFO__DEVICE_NR GENMASK(11, 8)
+#define ERR_CORRECTION_INFO__ERROR_TYPE BIT(14)
+#define ERR_CORRECTION_INFO__LAST_ERR_INFO BIT(15)
#define ECC_COR_INFO(bank) (0x650 + (bank) / 2 * 0x10)
#define ECC_COR_INFO__SHIFT(bank) ((bank) % 2 * 8)
-#define ECC_COR_INFO__MAX_ERRORS 0x007f
-#define ECC_COR_INFO__UNCOR_ERR 0x0080
+#define ECC_COR_INFO__MAX_ERRORS GENMASK(6, 0)
+#define ECC_COR_INFO__UNCOR_ERR BIT(7)
+
+#define CFG_DATA_BLOCK_SIZE 0x6b0
+
+#define CFG_LAST_DATA_BLOCK_SIZE 0x6c0
+
+#define CFG_NUM_DATA_BLOCKS 0x6d0
+
+#define CFG_META_DATA_SIZE 0x6e0
#define DMA_ENABLE 0x700
-#define DMA_ENABLE__FLAG 0x0001
+#define DMA_ENABLE__FLAG BIT(0)
#define IGNORE_ECC_DONE 0x710
-#define IGNORE_ECC_DONE__FLAG 0x0001
+#define IGNORE_ECC_DONE__FLAG BIT(0)
#define DMA_INTR 0x720
#define DMA_INTR_EN 0x730
-#define DMA_INTR__TARGET_ERROR 0x0001
-#define DMA_INTR__DESC_COMP_CHANNEL0 0x0002
-#define DMA_INTR__DESC_COMP_CHANNEL1 0x0004
-#define DMA_INTR__DESC_COMP_CHANNEL2 0x0008
-#define DMA_INTR__DESC_COMP_CHANNEL3 0x0010
-#define DMA_INTR__MEMCOPY_DESC_COMP 0x0020
+#define DMA_INTR__TARGET_ERROR BIT(0)
+#define DMA_INTR__DESC_COMP_CHANNEL0 BIT(1)
+#define DMA_INTR__DESC_COMP_CHANNEL1 BIT(2)
+#define DMA_INTR__DESC_COMP_CHANNEL2 BIT(3)
+#define DMA_INTR__DESC_COMP_CHANNEL3 BIT(4)
+#define DMA_INTR__MEMCOPY_DESC_COMP BIT(5)
#define TARGET_ERR_ADDR_LO 0x740
-#define TARGET_ERR_ADDR_LO__VALUE 0xffff
+#define TARGET_ERR_ADDR_LO__VALUE GENMASK(15, 0)
#define TARGET_ERR_ADDR_HI 0x750
-#define TARGET_ERR_ADDR_HI__VALUE 0xffff
+#define TARGET_ERR_ADDR_HI__VALUE GENMASK(15, 0)
#define CHNL_ACTIVE 0x760
-#define CHNL_ACTIVE__CHANNEL0 0x0001
-#define CHNL_ACTIVE__CHANNEL1 0x0002
-#define CHNL_ACTIVE__CHANNEL2 0x0004
-#define CHNL_ACTIVE__CHANNEL3 0x0008
-
-#define FAIL 1 /*failed flag*/
-#define PASS 0 /*success flag*/
-
-#define CLK_X 5
-#define CLK_MULTI 4
-
-#define ONFI_BLOOM_TIME 1
-#define MODE5_WORKAROUND 0
-
-
-#define MODE_00 0x00000000
-#define MODE_01 0x04000000
-#define MODE_10 0x08000000
-#define MODE_11 0x0C000000
-
-#define ECC_SECTOR_SIZE 512
-
-struct nand_buf {
- int head;
- int tail;
- uint8_t *buf;
- dma_addr_t dma_buf;
-};
-
-#define INTEL_CE4100 1
-#define INTEL_MRST 2
-#define DT 3
+#define CHNL_ACTIVE__CHANNEL0 BIT(0)
+#define CHNL_ACTIVE__CHANNEL1 BIT(1)
+#define CHNL_ACTIVE__CHANNEL2 BIT(2)
+#define CHNL_ACTIVE__CHANNEL3 BIT(3)
struct denali_nand_info {
struct nand_chip nand;
- int flash_bank; /* currently selected chip */
- int status;
- int platform;
- struct nand_buf buf;
+ unsigned long clk_x_rate; /* bus interface clock rate */
+ int active_bank; /* currently selected bank */
struct device *dev;
- int total_used_banks;
- int page;
- void __iomem *flash_reg; /* Register Interface */
- void __iomem *flash_mem; /* Host Data/Command Interface */
+ void __iomem *reg; /* Register Interface */
+ void __iomem *host; /* Host Data/Command Interface */
/* elements used by ISR */
struct completion complete;
spinlock_t irq_lock;
+ uint32_t irq_mask;
uint32_t irq_status;
int irq;
- int devnum; /* represent how many nands connected */
- int bbtskipbytes;
+ void *buf;
+ dma_addr_t dma_addr;
+ int dma_avail;
+ int devs_per_cs; /* devices connected in parallel */
+ int oob_skip_bytes;
int max_banks;
unsigned int revision;
unsigned int caps;
+ const struct nand_ecc_caps *ecc_caps;
};
#define DENALI_CAP_HW_ECC_FIXUP BIT(0)
#define DENALI_CAP_DMA_64BIT BIT(1)
+int denali_calc_ecc_bytes(int step_size, int strength);
extern int denali_init(struct denali_nand_info *denali);
extern void denali_remove(struct denali_nand_info *denali);
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index df9ef36cc2ce..47f398edf18f 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -32,10 +32,31 @@ struct denali_dt {
struct denali_dt_data {
unsigned int revision;
unsigned int caps;
+ const struct nand_ecc_caps *ecc_caps;
};
+NAND_ECC_CAPS_SINGLE(denali_socfpga_ecc_caps, denali_calc_ecc_bytes,
+ 512, 8, 15);
static const struct denali_dt_data denali_socfpga_data = {
.caps = DENALI_CAP_HW_ECC_FIXUP,
+ .ecc_caps = &denali_socfpga_ecc_caps,
+};
+
+NAND_ECC_CAPS_SINGLE(denali_uniphier_v5a_ecc_caps, denali_calc_ecc_bytes,
+ 1024, 8, 16, 24);
+static const struct denali_dt_data denali_uniphier_v5a_data = {
+ .caps = DENALI_CAP_HW_ECC_FIXUP |
+ DENALI_CAP_DMA_64BIT,
+ .ecc_caps = &denali_uniphier_v5a_ecc_caps,
+};
+
+NAND_ECC_CAPS_SINGLE(denali_uniphier_v5b_ecc_caps, denali_calc_ecc_bytes,
+ 1024, 8, 16);
+static const struct denali_dt_data denali_uniphier_v5b_data = {
+ .revision = 0x0501,
+ .caps = DENALI_CAP_HW_ECC_FIXUP |
+ DENALI_CAP_DMA_64BIT,
+ .ecc_caps = &denali_uniphier_v5b_ecc_caps,
};
static const struct of_device_id denali_nand_dt_ids[] = {
@@ -43,13 +64,21 @@ static const struct of_device_id denali_nand_dt_ids[] = {
.compatible = "altr,socfpga-denali-nand",
.data = &denali_socfpga_data,
},
+ {
+ .compatible = "socionext,uniphier-denali-nand-v5a",
+ .data = &denali_uniphier_v5a_data,
+ },
+ {
+ .compatible = "socionext,uniphier-denali-nand-v5b",
+ .data = &denali_uniphier_v5b_data,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
static int denali_dt_probe(struct platform_device *pdev)
{
- struct resource *denali_reg, *nand_data;
+ struct resource *res;
struct denali_dt *dt;
const struct denali_dt_data *data;
struct denali_nand_info *denali;
@@ -64,9 +93,9 @@ static int denali_dt_probe(struct platform_device *pdev)
if (data) {
denali->revision = data->revision;
denali->caps = data->caps;
+ denali->ecc_caps = data->ecc_caps;
}
- denali->platform = DT;
denali->dev = &pdev->dev;
denali->irq = platform_get_irq(pdev, 0);
if (denali->irq < 0) {
@@ -74,17 +103,15 @@ static int denali_dt_probe(struct platform_device *pdev)
return denali->irq;
}
- denali_reg = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "denali_reg");
- denali->flash_reg = devm_ioremap_resource(&pdev->dev, denali_reg);
- if (IS_ERR(denali->flash_reg))
- return PTR_ERR(denali->flash_reg);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "denali_reg");
+ denali->reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(denali->reg))
+ return PTR_ERR(denali->reg);
- nand_data = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "nand_data");
- denali->flash_mem = devm_ioremap_resource(&pdev->dev, nand_data);
- if (IS_ERR(denali->flash_mem))
- return PTR_ERR(denali->flash_mem);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
+ denali->host = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(denali->host))
+ return PTR_ERR(denali->host);
dt->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(dt->clk)) {
@@ -93,6 +120,8 @@ static int denali_dt_probe(struct platform_device *pdev)
}
clk_prepare_enable(dt->clk);
+ denali->clk_x_rate = clk_get_rate(dt->clk);
+
ret = denali_init(denali);
if (ret)
goto out_disable_clk;
diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c
index ac843238b77e..81370c79aa48 100644
--- a/drivers/mtd/nand/denali_pci.c
+++ b/drivers/mtd/nand/denali_pci.c
@@ -19,6 +19,9 @@
#define DENALI_NAND_NAME "denali-nand-pci"
+#define INTEL_CE4100 1
+#define INTEL_MRST 2
+
/* List of platforms this NAND controller has be integrated into */
static const struct pci_device_id denali_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 },
@@ -27,6 +30,8 @@ static const struct pci_device_id denali_pci_ids[] = {
};
MODULE_DEVICE_TABLE(pci, denali_pci_ids);
+NAND_ECC_CAPS_SINGLE(denali_pci_ecc_caps, denali_calc_ecc_bytes, 512, 8, 15);
+
static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int ret;
@@ -45,13 +50,11 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
}
if (id->driver_data == INTEL_CE4100) {
- denali->platform = INTEL_CE4100;
mem_base = pci_resource_start(dev, 0);
mem_len = pci_resource_len(dev, 1);
csr_base = pci_resource_start(dev, 1);
csr_len = pci_resource_len(dev, 1);
} else {
- denali->platform = INTEL_MRST;
csr_base = pci_resource_start(dev, 0);
csr_len = pci_resource_len(dev, 0);
mem_base = pci_resource_start(dev, 1);
@@ -65,6 +68,9 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
pci_set_master(dev);
denali->dev = &dev->dev;
denali->irq = dev->irq;
+ denali->ecc_caps = &denali_pci_ecc_caps;
+ denali->nand.ecc.options |= NAND_ECC_MAXIMIZE;
+ denali->clk_x_rate = 200000000; /* 200 MHz */
ret = pci_request_regions(dev, DENALI_NAND_NAME);
if (ret) {
@@ -72,14 +78,14 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
return ret;
}
- denali->flash_reg = ioremap_nocache(csr_base, csr_len);
- if (!denali->flash_reg) {
+ denali->reg = ioremap_nocache(csr_base, csr_len);
+ if (!denali->reg) {
dev_err(&dev->dev, "Spectra: Unable to remap memory region\n");
return -ENOMEM;
}
- denali->flash_mem = ioremap_nocache(mem_base, mem_len);
- if (!denali->flash_mem) {
+ denali->host = ioremap_nocache(mem_base, mem_len);
+ if (!denali->host) {
dev_err(&dev->dev, "Spectra: ioremap_nocache failed!");
ret = -ENOMEM;
goto failed_remap_reg;
@@ -94,9 +100,9 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
return 0;
failed_remap_mem:
- iounmap(denali->flash_mem);
+ iounmap(denali->host);
failed_remap_reg:
- iounmap(denali->flash_reg);
+ iounmap(denali->reg);
return ret;
}
@@ -106,8 +112,8 @@ static void denali_pci_remove(struct pci_dev *dev)
struct denali_nand_info *denali = pci_get_drvdata(dev);
denali_remove(denali);
- iounmap(denali->flash_reg);
- iounmap(denali->flash_mem);
+ iounmap(denali->reg);
+ iounmap(denali->host);
}
static struct pci_driver denali_pci_driver = {
diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c
index 7af2a3cd949e..a27a84fbfb84 100644
--- a/drivers/mtd/nand/docg4.c
+++ b/drivers/mtd/nand/docg4.c
@@ -1260,6 +1260,8 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
nand->read_buf = docg4_read_buf;
nand->write_buf = docg4_write_buf16;
nand->erase = docg4_erase_block;
+ nand->onfi_set_features = nand_onfi_get_set_features_notsupp;
+ nand->onfi_get_features = nand_onfi_get_set_features_notsupp;
nand->ecc.read_page = docg4_read_page;
nand->ecc.write_page = docg4_write_page;
nand->ecc.read_page_raw = docg4_read_page_raw;
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
index 113f76e59937..b9ac16f05057 100644
--- a/drivers/mtd/nand/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/fsl_elbc_nand.c
@@ -775,6 +775,8 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
chip->select_chip = fsl_elbc_select_chip;
chip->cmdfunc = fsl_elbc_cmdfunc;
chip->waitfunc = fsl_elbc_wait;
+ chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
+ chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
chip->bbt_td = &bbt_main_descr;
chip->bbt_md = &bbt_mirror_descr;
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
index d1570f512f0b..59408ec2c69f 100644
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
@@ -171,34 +171,6 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
ifc_nand_ctrl->index += mtd->writesize;
}
-static int is_blank(struct mtd_info *mtd, unsigned int bufnum)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
- u8 __iomem *addr = priv->vbase + bufnum * (mtd->writesize * 2);
- u32 __iomem *mainarea = (u32 __iomem *)addr;
- u8 __iomem *oob = addr + mtd->writesize;
- struct mtd_oob_region oobregion = { };
- int i, section = 0;
-
- for (i = 0; i < mtd->writesize / 4; i++) {
- if (__raw_readl(&mainarea[i]) != 0xffffffff)
- return 0;
- }
-
- mtd_ooblayout_ecc(mtd, section++, &oobregion);
- while (oobregion.length) {
- for (i = 0; i < oobregion.length; i++) {
- if (__raw_readb(&oob[oobregion.offset + i]) != 0xff)
- return 0;
- }
-
- mtd_ooblayout_ecc(mtd, section++, &oobregion);
- }
-
- return 1;
-}
-
/* returns nonzero if entire page is blank */
static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl,
u32 *eccstat, unsigned int bufnum)
@@ -274,16 +246,14 @@ static void fsl_ifc_run_command(struct mtd_info *mtd)
if (errors == 15) {
/*
* Uncorrectable error.
- * OK only if the whole page is blank.
+ * We'll check for blank pages later.
*
* We disable ECCER reporting due to...
* erratum IFC-A002770 -- so report it now if we
* see an uncorrectable error in ECCSTAT.
*/
- if (!is_blank(mtd, bufnum))
- ctrl->nand_stat |=
- IFC_NAND_EVTER_STAT_ECCER;
- break;
+ ctrl->nand_stat |= IFC_NAND_EVTER_STAT_ECCER;
+ continue;
}
mtd->ecc_stats.corrected += errors;
@@ -678,6 +648,39 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip)
return nand_fsr | NAND_STATUS_WP;
}
+/*
+ * The controller does not check for bitflips in erased pages,
+ * therefore software must check instead.
+ */
+static int check_erased_page(struct nand_chip *chip, u8 *buf)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ u8 *ecc = chip->oob_poi;
+ const int ecc_size = chip->ecc.bytes;
+ const int pkt_size = chip->ecc.size;
+ int i, res, bitflips = 0;
+ struct mtd_oob_region oobregion = { };
+
+ mtd_ooblayout_ecc(mtd, 0, &oobregion);
+ ecc += oobregion.offset;
+
+ for (i = 0; i < chip->ecc.steps; ++i) {
+ res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size,
+ NULL, 0,
+ chip->ecc.strength);
+ if (res < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += res;
+
+ bitflips = max(res, bitflips);
+ buf += pkt_size;
+ ecc += ecc_size;
+ }
+
+ return bitflips;
+}
+
static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
@@ -689,8 +692,12 @@ static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
if (oob_required)
fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
- if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_ECCER)
- dev_err(priv->dev, "NAND Flash ECC Uncorrectable Error\n");
+ if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_ECCER) {
+ if (!oob_required)
+ fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return check_erased_page(chip, buf);
+ }
if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC)
mtd->ecc_stats.failed++;
@@ -831,6 +838,8 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
chip->select_chip = fsl_ifc_select_chip;
chip->cmdfunc = fsl_ifc_cmdfunc;
chip->waitfunc = fsl_ifc_wait;
+ chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
+ chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
chip->bbt_td = &bbt_main_descr;
chip->bbt_md = &bbt_mirror_descr;
@@ -904,7 +913,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
chip->ecc.algo = NAND_ECC_HAMMING;
}
- if (ctrl->version == FSL_IFC_VERSION_1_1_0)
+ if (ctrl->version >= FSL_IFC_VERSION_1_1_0)
fsl_ifc_sram_init(priv);
return 0;
diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c
index cea50d2f218c..9d8b051d3187 100644
--- a/drivers/mtd/nand/fsmc_nand.c
+++ b/drivers/mtd/nand/fsmc_nand.c
@@ -302,25 +302,13 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
* This routine initializes timing parameters related to NAND memory access in
* FSMC registers
*/
-static void fsmc_nand_setup(void __iomem *regs, uint32_t bank,
- uint32_t busw, struct fsmc_nand_timings *timings)
+static void fsmc_nand_setup(struct fsmc_nand_data *host,
+ struct fsmc_nand_timings *tims)
{
uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON;
uint32_t tclr, tar, thiz, thold, twait, tset;
- struct fsmc_nand_timings *tims;
- struct fsmc_nand_timings default_timings = {
- .tclr = FSMC_TCLR_1,
- .tar = FSMC_TAR_1,
- .thiz = FSMC_THIZ_1,
- .thold = FSMC_THOLD_4,
- .twait = FSMC_TWAIT_6,
- .tset = FSMC_TSET_0,
- };
-
- if (timings)
- tims = timings;
- else
- tims = &default_timings;
+ unsigned int bank = host->bank;
+ void __iomem *regs = host->regs_va;
tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT;
tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT;
@@ -329,7 +317,7 @@ static void fsmc_nand_setup(void __iomem *regs, uint32_t bank,
twait = (tims->twait & FSMC_TWAIT_MASK) << FSMC_TWAIT_SHIFT;
tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT;
- if (busw)
+ if (host->nand.options & NAND_BUSWIDTH_16)
writel_relaxed(value | FSMC_DEVWID_16,
FSMC_NAND_REG(regs, bank, PC));
else
@@ -344,6 +332,87 @@ static void fsmc_nand_setup(void __iomem *regs, uint32_t bank,
FSMC_NAND_REG(regs, bank, ATTRIB));
}
+static int fsmc_calc_timings(struct fsmc_nand_data *host,
+ const struct nand_sdr_timings *sdrt,
+ struct fsmc_nand_timings *tims)
+{
+ unsigned long hclk = clk_get_rate(host->clk);
+ unsigned long hclkn = NSEC_PER_SEC / hclk;
+ uint32_t thiz, thold, twait, tset;
+
+ if (sdrt->tRC_min < 30000)
+ return -EOPNOTSUPP;
+
+ tims->tar = DIV_ROUND_UP(sdrt->tAR_min / 1000, hclkn) - 1;
+ if (tims->tar > FSMC_TAR_MASK)
+ tims->tar = FSMC_TAR_MASK;
+ tims->tclr = DIV_ROUND_UP(sdrt->tCLR_min / 1000, hclkn) - 1;
+ if (tims->tclr > FSMC_TCLR_MASK)
+ tims->tclr = FSMC_TCLR_MASK;
+
+ thiz = sdrt->tCS_min - sdrt->tWP_min;
+ tims->thiz = DIV_ROUND_UP(thiz / 1000, hclkn);
+
+ thold = sdrt->tDH_min;
+ if (thold < sdrt->tCH_min)
+ thold = sdrt->tCH_min;
+ if (thold < sdrt->tCLH_min)
+ thold = sdrt->tCLH_min;
+ if (thold < sdrt->tWH_min)
+ thold = sdrt->tWH_min;
+ if (thold < sdrt->tALH_min)
+ thold = sdrt->tALH_min;
+ if (thold < sdrt->tREH_min)
+ thold = sdrt->tREH_min;
+ tims->thold = DIV_ROUND_UP(thold / 1000, hclkn);
+ if (tims->thold == 0)
+ tims->thold = 1;
+ else if (tims->thold > FSMC_THOLD_MASK)
+ tims->thold = FSMC_THOLD_MASK;
+
+ twait = max(sdrt->tRP_min, sdrt->tWP_min);
+ tims->twait = DIV_ROUND_UP(twait / 1000, hclkn) - 1;
+ if (tims->twait == 0)
+ tims->twait = 1;
+ else if (tims->twait > FSMC_TWAIT_MASK)
+ tims->twait = FSMC_TWAIT_MASK;
+
+ tset = max(sdrt->tCS_min - sdrt->tWP_min,
+ sdrt->tCEA_max - sdrt->tREA_max);
+ tims->tset = DIV_ROUND_UP(tset / 1000, hclkn) - 1;
+ if (tims->tset == 0)
+ tims->tset = 1;
+ else if (tims->tset > FSMC_TSET_MASK)
+ tims->tset = FSMC_TSET_MASK;
+
+ return 0;
+}
+
+static int fsmc_setup_data_interface(struct mtd_info *mtd, int csline,
+ const struct nand_data_interface *conf)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct fsmc_nand_data *host = nand_get_controller_data(nand);
+ struct fsmc_nand_timings tims;
+ const struct nand_sdr_timings *sdrt;
+ int ret;
+
+ sdrt = nand_get_sdr_timings(conf);
+ if (IS_ERR(sdrt))
+ return PTR_ERR(sdrt);
+
+ ret = fsmc_calc_timings(host, sdrt, &tims);
+ if (ret)
+ return ret;
+
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ fsmc_nand_setup(host, &tims);
+
+ return 0;
+}
+
/*
* fsmc_enable_hwecc - Enables Hardware ECC through FSMC registers
*/
@@ -796,10 +865,8 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
return -ENOMEM;
ret = of_property_read_u8_array(np, "timings", (u8 *)host->dev_timings,
sizeof(*host->dev_timings));
- if (ret) {
- dev_info(&pdev->dev, "No timings in dts specified, using default timings!\n");
+ if (ret)
host->dev_timings = NULL;
- }
/* Set default NAND bank to 0 */
host->bank = 0;
@@ -933,9 +1000,10 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
break;
}
- fsmc_nand_setup(host->regs_va, host->bank,
- nand->options & NAND_BUSWIDTH_16,
- host->dev_timings);
+ if (host->dev_timings)
+ fsmc_nand_setup(host, host->dev_timings);
+ else
+ nand->setup_data_interface = fsmc_setup_data_interface;
if (AMBA_REV_BITS(host->pid) >= 8) {
nand->ecc.read_page = fsmc_read_page_hwecc;
@@ -986,6 +1054,9 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
break;
}
+ case NAND_ECC_ON_DIE:
+ break;
+
default:
dev_err(&pdev->dev, "Unsupported ECC mode!\n");
goto err_probe;
@@ -1073,9 +1144,8 @@ static int fsmc_nand_resume(struct device *dev)
struct fsmc_nand_data *host = dev_get_drvdata(dev);
if (host) {
clk_prepare_enable(host->clk);
- fsmc_nand_setup(host->regs_va, host->bank,
- host->nand.options & NAND_BUSWIDTH_16,
- host->dev_timings);
+ if (host->dev_timings)
+ fsmc_nand_setup(host, host->dev_timings);
}
return 0;
}
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
index 141bd70a49c2..97787246af41 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
@@ -26,7 +26,7 @@
#include "gpmi-regs.h"
#include "bch-regs.h"
-static struct timing_threshod timing_default_threshold = {
+static struct timing_threshold timing_default_threshold = {
.max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >>
BP_GPMI_TIMING0_DATA_SETUP),
.internal_data_setup_in_ns = 0,
@@ -329,7 +329,7 @@ static unsigned int ns_to_cycles(unsigned int time,
static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
struct gpmi_nfc_hardware_timing *hw)
{
- struct timing_threshod *nfc = &timing_default_threshold;
+ struct timing_threshold *nfc = &timing_default_threshold;
struct resources *r = &this->resources;
struct nand_chip *nand = &this->nand;
struct nand_timing target = this->timing;
@@ -932,7 +932,7 @@ static int enable_edo_mode(struct gpmi_nand_data *this, int mode)
nand->select_chip(mtd, 0);
- /* [1] send SET FEATURE commond to NAND */
+ /* [1] send SET FEATURE command to NAND */
feature[0] = mode;
ret = nand->onfi_set_features(mtd, nand,
ONFI_FEATURE_ADDR_TIMING_MODE, feature);
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
index d52139635b67..50f8d4a1b983 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
@@ -82,6 +82,10 @@ static int gpmi_ooblayout_free(struct mtd_info *mtd, int section,
return 0;
}
+static const char * const gpmi_clks_for_mx2x[] = {
+ "gpmi_io",
+};
+
static const struct mtd_ooblayout_ops gpmi_ooblayout_ops = {
.ecc = gpmi_ooblayout_ecc,
.free = gpmi_ooblayout_free,
@@ -91,24 +95,48 @@ static const struct gpmi_devdata gpmi_devdata_imx23 = {
.type = IS_MX23,
.bch_max_ecc_strength = 20,
.max_chain_delay = 16,
+ .clks = gpmi_clks_for_mx2x,
+ .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x),
};
static const struct gpmi_devdata gpmi_devdata_imx28 = {
.type = IS_MX28,
.bch_max_ecc_strength = 20,
.max_chain_delay = 16,
+ .clks = gpmi_clks_for_mx2x,
+ .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x),
+};
+
+static const char * const gpmi_clks_for_mx6[] = {
+ "gpmi_io", "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch",
};
static const struct gpmi_devdata gpmi_devdata_imx6q = {
.type = IS_MX6Q,
.bch_max_ecc_strength = 40,
.max_chain_delay = 12,
+ .clks = gpmi_clks_for_mx6,
+ .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6),
};
static const struct gpmi_devdata gpmi_devdata_imx6sx = {
.type = IS_MX6SX,
.bch_max_ecc_strength = 62,
.max_chain_delay = 12,
+ .clks = gpmi_clks_for_mx6,
+ .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6),
+};
+
+static const char * const gpmi_clks_for_mx7d[] = {
+ "gpmi_io", "gpmi_bch_apb",
+};
+
+static const struct gpmi_devdata gpmi_devdata_imx7d = {
+ .type = IS_MX7D,
+ .bch_max_ecc_strength = 62,
+ .max_chain_delay = 12,
+ .clks = gpmi_clks_for_mx7d,
+ .clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d),
};
static irqreturn_t bch_irq(int irq, void *cookie)
@@ -599,35 +627,14 @@ acquire_err:
return -EINVAL;
}
-static char *extra_clks_for_mx6q[GPMI_CLK_MAX] = {
- "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch",
-};
-
static int gpmi_get_clks(struct gpmi_nand_data *this)
{
struct resources *r = &this->resources;
- char **extra_clks = NULL;
struct clk *clk;
int err, i;
- /* The main clock is stored in the first. */
- r->clock[0] = devm_clk_get(this->dev, "gpmi_io");
- if (IS_ERR(r->clock[0])) {
- err = PTR_ERR(r->clock[0]);
- goto err_clock;
- }
-
- /* Get extra clocks */
- if (GPMI_IS_MX6(this))
- extra_clks = extra_clks_for_mx6q;
- if (!extra_clks)
- return 0;
-
- for (i = 1; i < GPMI_CLK_MAX; i++) {
- if (extra_clks[i - 1] == NULL)
- break;
-
- clk = devm_clk_get(this->dev, extra_clks[i - 1]);
+ for (i = 0; i < this->devdata->clks_count; i++) {
+ clk = devm_clk_get(this->dev, this->devdata->clks[i]);
if (IS_ERR(clk)) {
err = PTR_ERR(clk);
goto err_clock;
@@ -1929,12 +1936,6 @@ static int gpmi_set_geometry(struct gpmi_nand_data *this)
return gpmi_alloc_dma_buffer(this);
}
-static void gpmi_nand_exit(struct gpmi_nand_data *this)
-{
- nand_release(nand_to_mtd(&this->nand));
- gpmi_free_dma_buffer(this);
-}
-
static int gpmi_init_last(struct gpmi_nand_data *this)
{
struct nand_chip *chip = &this->nand;
@@ -2048,18 +2049,20 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
ret = nand_boot_init(this);
if (ret)
- goto err_out;
+ goto err_nand_cleanup;
ret = chip->scan_bbt(mtd);
if (ret)
- goto err_out;
+ goto err_nand_cleanup;
ret = mtd_device_register(mtd, NULL, 0);
if (ret)
- goto err_out;
+ goto err_nand_cleanup;
return 0;
+err_nand_cleanup:
+ nand_cleanup(chip);
err_out:
- gpmi_nand_exit(this);
+ gpmi_free_dma_buffer(this);
return ret;
}
@@ -2076,6 +2079,9 @@ static const struct of_device_id gpmi_nand_id_table[] = {
}, {
.compatible = "fsl,imx6sx-gpmi-nand",
.data = &gpmi_devdata_imx6sx,
+ }, {
+ .compatible = "fsl,imx7d-gpmi-nand",
+ .data = &gpmi_devdata_imx7d,
}, {}
};
MODULE_DEVICE_TABLE(of, gpmi_nand_id_table);
@@ -2129,7 +2135,8 @@ static int gpmi_nand_remove(struct platform_device *pdev)
{
struct gpmi_nand_data *this = platform_get_drvdata(pdev);
- gpmi_nand_exit(this);
+ nand_release(nand_to_mtd(&this->nand));
+ gpmi_free_dma_buffer(this);
release_resources(this);
return 0;
}
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
index 4e49a1f5fa27..9df0ad64e7e0 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
@@ -123,13 +123,16 @@ enum gpmi_type {
IS_MX23,
IS_MX28,
IS_MX6Q,
- IS_MX6SX
+ IS_MX6SX,
+ IS_MX7D,
};
struct gpmi_devdata {
enum gpmi_type type;
int bch_max_ecc_strength;
int max_chain_delay; /* See the async EDO mode */
+ const char * const *clks;
+ const int clks_count;
};
struct gpmi_nand_data {
@@ -231,7 +234,7 @@ struct gpmi_nfc_hardware_timing {
};
/**
- * struct timing_threshod - Timing threshold
+ * struct timing_threshold - Timing threshold
* @max_data_setup_cycles: The maximum number of data setup cycles that
* can be expressed in the hardware.
* @internal_data_setup_in_ns: The time, in ns, that the NFC hardware requires
@@ -253,7 +256,7 @@ struct gpmi_nfc_hardware_timing {
* progress, this is the clock frequency during
* the most recent I/O transaction.
*/
-struct timing_threshod {
+struct timing_threshold {
const unsigned int max_chip_count;
const unsigned int max_data_setup_cycles;
const unsigned int internal_data_setup_in_ns;
@@ -305,6 +308,8 @@ void gpmi_copy_bits(u8 *dst, size_t dst_bit_off,
#define GPMI_IS_MX28(x) ((x)->devdata->type == IS_MX28)
#define GPMI_IS_MX6Q(x) ((x)->devdata->type == IS_MX6Q)
#define GPMI_IS_MX6SX(x) ((x)->devdata->type == IS_MX6SX)
+#define GPMI_IS_MX7D(x) ((x)->devdata->type == IS_MX7D)
-#define GPMI_IS_MX6(x) (GPMI_IS_MX6Q(x) || GPMI_IS_MX6SX(x))
+#define GPMI_IS_MX6(x) (GPMI_IS_MX6Q(x) || GPMI_IS_MX6SX(x) || \
+ GPMI_IS_MX7D(x))
#endif
diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c
index e40364eeb556..530caa80b1b6 100644
--- a/drivers/mtd/nand/hisi504_nand.c
+++ b/drivers/mtd/nand/hisi504_nand.c
@@ -764,6 +764,8 @@ static int hisi_nfc_probe(struct platform_device *pdev)
chip->write_buf = hisi_nfc_write_buf;
chip->read_buf = hisi_nfc_read_buf;
chip->chip_delay = HINFC504_CHIP_DELAY;
+ chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
+ chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
hisi_nfc_host_init(host);
diff --git a/drivers/mtd/nand/jz4780_nand.c b/drivers/mtd/nand/jz4780_nand.c
index a39bb70175ee..8bc835f71b26 100644
--- a/drivers/mtd/nand/jz4780_nand.c
+++ b/drivers/mtd/nand/jz4780_nand.c
@@ -205,7 +205,7 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de
return -EINVAL;
}
- mtd->ooblayout = &nand_ooblayout_lp_ops;
+ mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
return 0;
}
diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c
index 6d6eaed2d20c..0e86fb6277c3 100644
--- a/drivers/mtd/nand/mpc5121_nfc.c
+++ b/drivers/mtd/nand/mpc5121_nfc.c
@@ -708,6 +708,8 @@ static int mpc5121_nfc_probe(struct platform_device *op)
chip->read_buf = mpc5121_nfc_read_buf;
chip->write_buf = mpc5121_nfc_write_buf;
chip->select_chip = mpc5121_nfc_select_chip;
+ chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
+ chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
chip->bbt_options = NAND_BBT_USE_FLASH;
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
diff --git a/drivers/mtd/nand/mtk_ecc.c b/drivers/mtd/nand/mtk_ecc.c
index dbf256217b3e..6c3a4aab0b48 100644
--- a/drivers/mtd/nand/mtk_ecc.c
+++ b/drivers/mtd/nand/mtk_ecc.c
@@ -28,36 +28,16 @@
#define ECC_IDLE_MASK BIT(0)
#define ECC_IRQ_EN BIT(0)
+#define ECC_PG_IRQ_SEL BIT(1)
#define ECC_OP_ENABLE (1)
#define ECC_OP_DISABLE (0)
#define ECC_ENCCON (0x00)
#define ECC_ENCCNFG (0x04)
-#define ECC_CNFG_4BIT (0)
-#define ECC_CNFG_6BIT (1)
-#define ECC_CNFG_8BIT (2)
-#define ECC_CNFG_10BIT (3)
-#define ECC_CNFG_12BIT (4)
-#define ECC_CNFG_14BIT (5)
-#define ECC_CNFG_16BIT (6)
-#define ECC_CNFG_18BIT (7)
-#define ECC_CNFG_20BIT (8)
-#define ECC_CNFG_22BIT (9)
-#define ECC_CNFG_24BIT (0xa)
-#define ECC_CNFG_28BIT (0xb)
-#define ECC_CNFG_32BIT (0xc)
-#define ECC_CNFG_36BIT (0xd)
-#define ECC_CNFG_40BIT (0xe)
-#define ECC_CNFG_44BIT (0xf)
-#define ECC_CNFG_48BIT (0x10)
-#define ECC_CNFG_52BIT (0x11)
-#define ECC_CNFG_56BIT (0x12)
-#define ECC_CNFG_60BIT (0x13)
#define ECC_MODE_SHIFT (5)
#define ECC_MS_SHIFT (16)
#define ECC_ENCDIADDR (0x08)
#define ECC_ENCIDLE (0x0C)
-#define ECC_ENCPAR(x) (0x10 + (x) * sizeof(u32))
#define ECC_ENCIRQ_EN (0x80)
#define ECC_ENCIRQ_STA (0x84)
#define ECC_DECCON (0x100)
@@ -66,7 +46,6 @@
#define DEC_CNFG_CORRECT (0x3 << 12)
#define ECC_DECIDLE (0x10C)
#define ECC_DECENUM0 (0x114)
-#define ERR_MASK (0x3f)
#define ECC_DECDONE (0x124)
#define ECC_DECIRQ_EN (0x200)
#define ECC_DECIRQ_STA (0x204)
@@ -78,8 +57,17 @@
#define ECC_IRQ_REG(op) ((op) == ECC_ENCODE ? \
ECC_ENCIRQ_EN : ECC_DECIRQ_EN)
+struct mtk_ecc_caps {
+ u32 err_mask;
+ const u8 *ecc_strength;
+ u8 num_ecc_strength;
+ u32 encode_parity_reg0;
+ int pg_irq_sel;
+};
+
struct mtk_ecc {
struct device *dev;
+ const struct mtk_ecc_caps *caps;
void __iomem *regs;
struct clk *clk;
@@ -87,7 +75,18 @@ struct mtk_ecc {
struct mutex lock;
u32 sectors;
- u8 eccdata[112];
+ u8 *eccdata;
+};
+
+/* ecc strength that each IP supports */
+static const u8 ecc_strength_mt2701[] = {
+ 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36,
+ 40, 44, 48, 52, 56, 60
+};
+
+static const u8 ecc_strength_mt2712[] = {
+ 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36,
+ 40, 44, 48, 52, 56, 60, 68, 72, 80
};
static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
@@ -136,77 +135,24 @@ static irqreturn_t mtk_ecc_irq(int irq, void *id)
return IRQ_HANDLED;
}
-static void mtk_ecc_config(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
+static int mtk_ecc_config(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
{
- u32 ecc_bit = ECC_CNFG_4BIT, dec_sz, enc_sz;
- u32 reg;
-
- switch (config->strength) {
- case 4:
- ecc_bit = ECC_CNFG_4BIT;
- break;
- case 6:
- ecc_bit = ECC_CNFG_6BIT;
- break;
- case 8:
- ecc_bit = ECC_CNFG_8BIT;
- break;
- case 10:
- ecc_bit = ECC_CNFG_10BIT;
- break;
- case 12:
- ecc_bit = ECC_CNFG_12BIT;
- break;
- case 14:
- ecc_bit = ECC_CNFG_14BIT;
- break;
- case 16:
- ecc_bit = ECC_CNFG_16BIT;
- break;
- case 18:
- ecc_bit = ECC_CNFG_18BIT;
- break;
- case 20:
- ecc_bit = ECC_CNFG_20BIT;
- break;
- case 22:
- ecc_bit = ECC_CNFG_22BIT;
- break;
- case 24:
- ecc_bit = ECC_CNFG_24BIT;
- break;
- case 28:
- ecc_bit = ECC_CNFG_28BIT;
- break;
- case 32:
- ecc_bit = ECC_CNFG_32BIT;
- break;
- case 36:
- ecc_bit = ECC_CNFG_36BIT;
- break;
- case 40:
- ecc_bit = ECC_CNFG_40BIT;
- break;
- case 44:
- ecc_bit = ECC_CNFG_44BIT;
- break;
- case 48:
- ecc_bit = ECC_CNFG_48BIT;
- break;
- case 52:
- ecc_bit = ECC_CNFG_52BIT;
- break;
- case 56:
- ecc_bit = ECC_CNFG_56BIT;
- break;
- case 60:
- ecc_bit = ECC_CNFG_60BIT;
- break;
- default:
- dev_err(ecc->dev, "invalid strength %d, default to 4 bits\n",
+ u32 ecc_bit, dec_sz, enc_sz;
+ u32 reg, i;
+
+ for (i = 0; i < ecc->caps->num_ecc_strength; i++) {
+ if (ecc->caps->ecc_strength[i] == config->strength)
+ break;
+ }
+
+ if (i == ecc->caps->num_ecc_strength) {
+ dev_err(ecc->dev, "invalid ecc strength %d\n",
config->strength);
+ return -EINVAL;
}
+ ecc_bit = i;
+
if (config->op == ECC_ENCODE) {
/* configure ECC encoder (in bits) */
enc_sz = config->len << 3;
@@ -232,6 +178,8 @@ static void mtk_ecc_config(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
if (config->sectors)
ecc->sectors = 1 << (config->sectors - 1);
}
+
+ return 0;
}
void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats,
@@ -247,8 +195,8 @@ void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats,
offset = (i >> 2) << 2;
err = readl(ecc->regs + ECC_DECENUM0 + offset);
err = err >> ((i % 4) * 8);
- err &= ERR_MASK;
- if (err == ERR_MASK) {
+ err &= ecc->caps->err_mask;
+ if (err == ecc->caps->err_mask) {
/* uncorrectable errors */
stats->failed++;
continue;
@@ -313,6 +261,7 @@ EXPORT_SYMBOL(of_mtk_ecc_get);
int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
{
enum mtk_ecc_operation op = config->op;
+ u16 reg_val;
int ret;
ret = mutex_lock_interruptible(&ecc->lock);
@@ -322,11 +271,27 @@ int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
}
mtk_ecc_wait_idle(ecc, op);
- mtk_ecc_config(ecc, config);
- writew(ECC_OP_ENABLE, ecc->regs + ECC_CTL_REG(op));
- init_completion(&ecc->done);
- writew(ECC_IRQ_EN, ecc->regs + ECC_IRQ_REG(op));
+ ret = mtk_ecc_config(ecc, config);
+ if (ret) {
+ mutex_unlock(&ecc->lock);
+ return ret;
+ }
+
+ if (config->mode != ECC_NFI_MODE || op != ECC_ENCODE) {
+ init_completion(&ecc->done);
+ reg_val = ECC_IRQ_EN;
+ /*
+ * For ECC_NFI_MODE, if ecc->caps->pg_irq_sel is 1, then it
+ * means this chip can only generate one ecc irq during page
+ * read / write. If is 0, generate one ecc irq each ecc step.
+ */
+ if (ecc->caps->pg_irq_sel && config->mode == ECC_NFI_MODE)
+ reg_val |= ECC_PG_IRQ_SEL;
+ writew(reg_val, ecc->regs + ECC_IRQ_REG(op));
+ }
+
+ writew(ECC_OP_ENABLE, ecc->regs + ECC_CTL_REG(op));
return 0;
}
@@ -396,7 +361,9 @@ int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config,
len = (config->strength * ECC_PARITY_BITS + 7) >> 3;
/* write the parity bytes generated by the ECC back to temp buffer */
- __ioread32_copy(ecc->eccdata, ecc->regs + ECC_ENCPAR(0), round_up(len, 4));
+ __ioread32_copy(ecc->eccdata,
+ ecc->regs + ecc->caps->encode_parity_reg0,
+ round_up(len, 4));
/* copy into possibly unaligned OOB region with actual length */
memcpy(data + bytes, ecc->eccdata, len);
@@ -409,37 +376,79 @@ timeout:
}
EXPORT_SYMBOL(mtk_ecc_encode);
-void mtk_ecc_adjust_strength(u32 *p)
+void mtk_ecc_adjust_strength(struct mtk_ecc *ecc, u32 *p)
{
- u32 ecc[] = {4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36,
- 40, 44, 48, 52, 56, 60};
+ const u8 *ecc_strength = ecc->caps->ecc_strength;
int i;
- for (i = 0; i < ARRAY_SIZE(ecc); i++) {
- if (*p <= ecc[i]) {
+ for (i = 0; i < ecc->caps->num_ecc_strength; i++) {
+ if (*p <= ecc_strength[i]) {
if (!i)
- *p = ecc[i];
- else if (*p != ecc[i])
- *p = ecc[i - 1];
+ *p = ecc_strength[i];
+ else if (*p != ecc_strength[i])
+ *p = ecc_strength[i - 1];
return;
}
}
- *p = ecc[ARRAY_SIZE(ecc) - 1];
+ *p = ecc_strength[ecc->caps->num_ecc_strength - 1];
}
EXPORT_SYMBOL(mtk_ecc_adjust_strength);
+static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = {
+ .err_mask = 0x3f,
+ .ecc_strength = ecc_strength_mt2701,
+ .num_ecc_strength = 20,
+ .encode_parity_reg0 = 0x10,
+ .pg_irq_sel = 0,
+};
+
+static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = {
+ .err_mask = 0x7f,
+ .ecc_strength = ecc_strength_mt2712,
+ .num_ecc_strength = 23,
+ .encode_parity_reg0 = 0x300,
+ .pg_irq_sel = 1,
+};
+
+static const struct of_device_id mtk_ecc_dt_match[] = {
+ {
+ .compatible = "mediatek,mt2701-ecc",
+ .data = &mtk_ecc_caps_mt2701,
+ }, {
+ .compatible = "mediatek,mt2712-ecc",
+ .data = &mtk_ecc_caps_mt2712,
+ },
+ {},
+};
+
static int mtk_ecc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_ecc *ecc;
struct resource *res;
+ const struct of_device_id *of_ecc_id = NULL;
+ u32 max_eccdata_size;
int irq, ret;
ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL);
if (!ecc)
return -ENOMEM;
+ of_ecc_id = of_match_device(mtk_ecc_dt_match, &pdev->dev);
+ if (!of_ecc_id)
+ return -ENODEV;
+
+ ecc->caps = of_ecc_id->data;
+
+ max_eccdata_size = ecc->caps->num_ecc_strength - 1;
+ max_eccdata_size = ecc->caps->ecc_strength[max_eccdata_size];
+ max_eccdata_size = (max_eccdata_size * ECC_PARITY_BITS + 7) >> 3;
+ max_eccdata_size = round_up(max_eccdata_size, 4);
+ ecc->eccdata = devm_kzalloc(dev, max_eccdata_size, GFP_KERNEL);
+ if (!ecc->eccdata)
+ return -ENOMEM;
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ecc->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(ecc->regs)) {
@@ -500,19 +509,12 @@ static int mtk_ecc_resume(struct device *dev)
return ret;
}
- mtk_ecc_hw_init(ecc);
-
return 0;
}
static SIMPLE_DEV_PM_OPS(mtk_ecc_pm_ops, mtk_ecc_suspend, mtk_ecc_resume);
#endif
-static const struct of_device_id mtk_ecc_dt_match[] = {
- { .compatible = "mediatek,mt2701-ecc" },
- {},
-};
-
MODULE_DEVICE_TABLE(of, mtk_ecc_dt_match);
static struct platform_driver mtk_ecc_driver = {
diff --git a/drivers/mtd/nand/mtk_ecc.h b/drivers/mtd/nand/mtk_ecc.h
index cbeba5cd1c13..d245c14f1b80 100644
--- a/drivers/mtd/nand/mtk_ecc.h
+++ b/drivers/mtd/nand/mtk_ecc.h
@@ -42,7 +42,7 @@ void mtk_ecc_get_stats(struct mtk_ecc *, struct mtk_ecc_stats *, int);
int mtk_ecc_wait_done(struct mtk_ecc *, enum mtk_ecc_operation);
int mtk_ecc_enable(struct mtk_ecc *, struct mtk_ecc_config *);
void mtk_ecc_disable(struct mtk_ecc *);
-void mtk_ecc_adjust_strength(u32 *);
+void mtk_ecc_adjust_strength(struct mtk_ecc *ecc, u32 *p);
struct mtk_ecc *of_mtk_ecc_get(struct device_node *);
void mtk_ecc_release(struct mtk_ecc *);
diff --git a/drivers/mtd/nand/mtk_nand.c b/drivers/mtd/nand/mtk_nand.c
index 6c517c682939..f7ae99464375 100644
--- a/drivers/mtd/nand/mtk_nand.c
+++ b/drivers/mtd/nand/mtk_nand.c
@@ -24,6 +24,7 @@
#include <linux/module.h>
#include <linux/iopoll.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include "mtk_ecc.h"
/* NAND controller register definition */
@@ -38,23 +39,6 @@
#define NFI_PAGEFMT (0x04)
#define PAGEFMT_FDM_ECC_SHIFT (12)
#define PAGEFMT_FDM_SHIFT (8)
-#define PAGEFMT_SPARE_16 (0)
-#define PAGEFMT_SPARE_26 (1)
-#define PAGEFMT_SPARE_27 (2)
-#define PAGEFMT_SPARE_28 (3)
-#define PAGEFMT_SPARE_32 (4)
-#define PAGEFMT_SPARE_36 (5)
-#define PAGEFMT_SPARE_40 (6)
-#define PAGEFMT_SPARE_44 (7)
-#define PAGEFMT_SPARE_48 (8)
-#define PAGEFMT_SPARE_49 (9)
-#define PAGEFMT_SPARE_50 (0xa)
-#define PAGEFMT_SPARE_51 (0xb)
-#define PAGEFMT_SPARE_52 (0xc)
-#define PAGEFMT_SPARE_62 (0xd)
-#define PAGEFMT_SPARE_63 (0xe)
-#define PAGEFMT_SPARE_64 (0xf)
-#define PAGEFMT_SPARE_SHIFT (4)
#define PAGEFMT_SEC_SEL_512 BIT(2)
#define PAGEFMT_512_2K (0)
#define PAGEFMT_2K_4K (1)
@@ -115,6 +99,17 @@
#define MTK_RESET_TIMEOUT (1000000)
#define MTK_MAX_SECTOR (16)
#define MTK_NAND_MAX_NSELS (2)
+#define MTK_NFC_MIN_SPARE (16)
+#define ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt) \
+ ((tpoecs) << 28 | (tprecs) << 22 | (tc2r) << 16 | \
+ (tw2r) << 12 | (twh) << 8 | (twst) << 4 | (trlt))
+
+struct mtk_nfc_caps {
+ const u8 *spare_size;
+ u8 num_spare_size;
+ u8 pageformat_spare_shift;
+ u8 nfi_clk_div;
+};
struct mtk_nfc_bad_mark_ctl {
void (*bm_swap)(struct mtd_info *, u8 *buf, int raw);
@@ -155,6 +150,7 @@ struct mtk_nfc {
struct mtk_ecc *ecc;
struct device *dev;
+ const struct mtk_nfc_caps *caps;
void __iomem *regs;
struct completion done;
@@ -163,6 +159,20 @@ struct mtk_nfc {
u8 *buffer;
};
+/*
+ * supported spare size of each IP.
+ * order should be the same with the spare size bitfiled defination of
+ * register NFI_PAGEFMT.
+ */
+static const u8 spare_size_mt2701[] = {
+ 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 63, 64
+};
+
+static const u8 spare_size_mt2712[] = {
+ 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 61, 63, 64, 67,
+ 74
+};
+
static inline struct mtk_nfc_nand_chip *to_mtk_nand(struct nand_chip *nand)
{
return container_of(nand, struct mtk_nfc_nand_chip, nand);
@@ -308,7 +318,7 @@ static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd)
struct nand_chip *chip = mtd_to_nand(mtd);
struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
struct mtk_nfc *nfc = nand_get_controller_data(chip);
- u32 fmt, spare;
+ u32 fmt, spare, i;
if (!mtd->writesize)
return 0;
@@ -352,63 +362,21 @@ static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd)
if (chip->ecc.size == 1024)
spare >>= 1;
- switch (spare) {
- case 16:
- fmt |= (PAGEFMT_SPARE_16 << PAGEFMT_SPARE_SHIFT);
- break;
- case 26:
- fmt |= (PAGEFMT_SPARE_26 << PAGEFMT_SPARE_SHIFT);
- break;
- case 27:
- fmt |= (PAGEFMT_SPARE_27 << PAGEFMT_SPARE_SHIFT);
- break;
- case 28:
- fmt |= (PAGEFMT_SPARE_28 << PAGEFMT_SPARE_SHIFT);
- break;
- case 32:
- fmt |= (PAGEFMT_SPARE_32 << PAGEFMT_SPARE_SHIFT);
- break;
- case 36:
- fmt |= (PAGEFMT_SPARE_36 << PAGEFMT_SPARE_SHIFT);
- break;
- case 40:
- fmt |= (PAGEFMT_SPARE_40 << PAGEFMT_SPARE_SHIFT);
- break;
- case 44:
- fmt |= (PAGEFMT_SPARE_44 << PAGEFMT_SPARE_SHIFT);
- break;
- case 48:
- fmt |= (PAGEFMT_SPARE_48 << PAGEFMT_SPARE_SHIFT);
- break;
- case 49:
- fmt |= (PAGEFMT_SPARE_49 << PAGEFMT_SPARE_SHIFT);
- break;
- case 50:
- fmt |= (PAGEFMT_SPARE_50 << PAGEFMT_SPARE_SHIFT);
- break;
- case 51:
- fmt |= (PAGEFMT_SPARE_51 << PAGEFMT_SPARE_SHIFT);
- break;
- case 52:
- fmt |= (PAGEFMT_SPARE_52 << PAGEFMT_SPARE_SHIFT);
- break;
- case 62:
- fmt |= (PAGEFMT_SPARE_62 << PAGEFMT_SPARE_SHIFT);
- break;
- case 63:
- fmt |= (PAGEFMT_SPARE_63 << PAGEFMT_SPARE_SHIFT);
- break;
- case 64:
- fmt |= (PAGEFMT_SPARE_64 << PAGEFMT_SPARE_SHIFT);
- break;
- default:
- dev_err(nfc->dev, "invalid spare per sector %d\n", spare);
+ for (i = 0; i < nfc->caps->num_spare_size; i++) {
+ if (nfc->caps->spare_size[i] == spare)
+ break;
+ }
+
+ if (i == nfc->caps->num_spare_size) {
+ dev_err(nfc->dev, "invalid spare size %d\n", spare);
return -EINVAL;
}
+ fmt |= i << nfc->caps->pageformat_spare_shift;
+
fmt |= mtk_nand->fdm.reg_size << PAGEFMT_FDM_SHIFT;
fmt |= mtk_nand->fdm.ecc_size << PAGEFMT_FDM_ECC_SHIFT;
- nfi_writew(nfc, fmt, NFI_PAGEFMT);
+ nfi_writel(nfc, fmt, NFI_PAGEFMT);
nfc->ecc_cfg.strength = chip->ecc.strength;
nfc->ecc_cfg.len = chip->ecc.size + mtk_nand->fdm.ecc_size;
@@ -531,6 +499,74 @@ static void mtk_nfc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
mtk_nfc_write_byte(mtd, buf[i]);
}
+static int mtk_nfc_setup_data_interface(struct mtd_info *mtd, int csline,
+ const struct nand_data_interface *conf)
+{
+ struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+ const struct nand_sdr_timings *timings;
+ u32 rate, tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt;
+
+ timings = nand_get_sdr_timings(conf);
+ if (IS_ERR(timings))
+ return -ENOTSUPP;
+
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ rate = clk_get_rate(nfc->clk.nfi_clk);
+ /* There is a frequency divider in some IPs */
+ rate /= nfc->caps->nfi_clk_div;
+
+ /* turn clock rate into KHZ */
+ rate /= 1000;
+
+ tpoecs = max(timings->tALH_min, timings->tCLH_min) / 1000;
+ tpoecs = DIV_ROUND_UP(tpoecs * rate, 1000000);
+ tpoecs &= 0xf;
+
+ tprecs = max(timings->tCLS_min, timings->tALS_min) / 1000;
+ tprecs = DIV_ROUND_UP(tprecs * rate, 1000000);
+ tprecs &= 0x3f;
+
+ /* sdr interface has no tCR which means CE# low to RE# low */
+ tc2r = 0;
+
+ tw2r = timings->tWHR_min / 1000;
+ tw2r = DIV_ROUND_UP(tw2r * rate, 1000000);
+ tw2r = DIV_ROUND_UP(tw2r - 1, 2);
+ tw2r &= 0xf;
+
+ twh = max(timings->tREH_min, timings->tWH_min) / 1000;
+ twh = DIV_ROUND_UP(twh * rate, 1000000) - 1;
+ twh &= 0xf;
+
+ twst = timings->tWP_min / 1000;
+ twst = DIV_ROUND_UP(twst * rate, 1000000) - 1;
+ twst &= 0xf;
+
+ trlt = max(timings->tREA_max, timings->tRP_min) / 1000;
+ trlt = DIV_ROUND_UP(trlt * rate, 1000000) - 1;
+ trlt &= 0xf;
+
+ /*
+ * ACCON: access timing control register
+ * -------------------------------------
+ * 31:28: tpoecs, minimum required time for CS post pulling down after
+ * accessing the device
+ * 27:22: tprecs, minimum required time for CS pre pulling down before
+ * accessing the device
+ * 21:16: tc2r, minimum required time from NCEB low to NREB low
+ * 15:12: tw2r, minimum required time from NWEB high to NREB low.
+ * 11:08: twh, write enable hold time
+ * 07:04: twst, write wait states
+ * 03:00: trlt, read wait states
+ */
+ trlt = ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt);
+ nfi_writel(nfc, trlt, NFI_ACCCON);
+
+ return 0;
+}
+
static int mtk_nfc_sector_encode(struct nand_chip *chip, u8 *data)
{
struct mtk_nfc *nfc = nand_get_controller_data(chip);
@@ -988,28 +1024,13 @@ static int mtk_nfc_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
static inline void mtk_nfc_hw_init(struct mtk_nfc *nfc)
{
/*
- * ACCON: access timing control register
- * -------------------------------------
- * 31:28: minimum required time for CS post pulling down after accessing
- * the device
- * 27:22: minimum required time for CS pre pulling down before accessing
- * the device
- * 21:16: minimum required time from NCEB low to NREB low
- * 15:12: minimum required time from NWEB high to NREB low.
- * 11:08: write enable hold time
- * 07:04: write wait states
- * 03:00: read wait states
- */
- nfi_writel(nfc, 0x10804211, NFI_ACCCON);
-
- /*
* CNRNB: nand ready/busy register
* -------------------------------
* 7:4: timeout register for polling the NAND busy/ready signal
* 0 : poll the status of the busy/ready signal after [7:4]*16 cycles.
*/
nfi_writew(nfc, 0xf1, NFI_CNRNB);
- nfi_writew(nfc, PAGEFMT_8K_16K, NFI_PAGEFMT);
+ nfi_writel(nfc, PAGEFMT_8K_16K, NFI_PAGEFMT);
mtk_nfc_hw_reset(nfc);
@@ -1131,12 +1152,12 @@ static void mtk_nfc_set_bad_mark_ctl(struct mtk_nfc_bad_mark_ctl *bm_ctl,
}
}
-static void mtk_nfc_set_spare_per_sector(u32 *sps, struct mtd_info *mtd)
+static int mtk_nfc_set_spare_per_sector(u32 *sps, struct mtd_info *mtd)
{
struct nand_chip *nand = mtd_to_nand(mtd);
- u32 spare[] = {16, 26, 27, 28, 32, 36, 40, 44,
- 48, 49, 50, 51, 52, 62, 63, 64};
- u32 eccsteps, i;
+ struct mtk_nfc *nfc = nand_get_controller_data(nand);
+ const u8 *spare = nfc->caps->spare_size;
+ u32 eccsteps, i, closest_spare = 0;
eccsteps = mtd->writesize / nand->ecc.size;
*sps = mtd->oobsize / eccsteps;
@@ -1144,28 +1165,31 @@ static void mtk_nfc_set_spare_per_sector(u32 *sps, struct mtd_info *mtd)
if (nand->ecc.size == 1024)
*sps >>= 1;
- for (i = 0; i < ARRAY_SIZE(spare); i++) {
- if (*sps <= spare[i]) {
- if (!i)
- *sps = spare[i];
- else if (*sps != spare[i])
- *sps = spare[i - 1];
- break;
+ if (*sps < MTK_NFC_MIN_SPARE)
+ return -EINVAL;
+
+ for (i = 0; i < nfc->caps->num_spare_size; i++) {
+ if (*sps >= spare[i] && spare[i] >= spare[closest_spare]) {
+ closest_spare = i;
+ if (*sps == spare[i])
+ break;
}
}
- if (i >= ARRAY_SIZE(spare))
- *sps = spare[ARRAY_SIZE(spare) - 1];
+ *sps = spare[closest_spare];
if (nand->ecc.size == 1024)
*sps <<= 1;
+
+ return 0;
}
static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd)
{
struct nand_chip *nand = mtd_to_nand(mtd);
+ struct mtk_nfc *nfc = nand_get_controller_data(nand);
u32 spare;
- int free;
+ int free, ret;
/* support only ecc hw mode */
if (nand->ecc.mode != NAND_ECC_HW) {
@@ -1194,7 +1218,9 @@ static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd)
nand->ecc.size = 1024;
}
- mtk_nfc_set_spare_per_sector(&spare, mtd);
+ ret = mtk_nfc_set_spare_per_sector(&spare, mtd);
+ if (ret)
+ return ret;
/* calculate oob bytes except ecc parity data */
free = ((nand->ecc.strength * ECC_PARITY_BITS) + 7) >> 3;
@@ -1214,7 +1240,7 @@ static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd)
}
}
- mtk_ecc_adjust_strength(&nand->ecc.strength);
+ mtk_ecc_adjust_strength(nfc->ecc, &nand->ecc.strength);
dev_info(dev, "eccsize %d eccstrength %d\n",
nand->ecc.size, nand->ecc.strength);
@@ -1271,6 +1297,7 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
nand->read_byte = mtk_nfc_read_byte;
nand->read_buf = mtk_nfc_read_buf;
nand->cmd_ctrl = mtk_nfc_cmd_ctrl;
+ nand->setup_data_interface = mtk_nfc_setup_data_interface;
/* set default mode in case dt entry is missing */
nand->ecc.mode = NAND_ECC_HW;
@@ -1312,7 +1339,10 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
return -EINVAL;
}
- mtk_nfc_set_spare_per_sector(&chip->spare_per_sector, mtd);
+ 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);
@@ -1354,12 +1384,39 @@ static int mtk_nfc_nand_chips_init(struct device *dev, struct mtk_nfc *nfc)
return 0;
}
+static const struct mtk_nfc_caps mtk_nfc_caps_mt2701 = {
+ .spare_size = spare_size_mt2701,
+ .num_spare_size = 16,
+ .pageformat_spare_shift = 4,
+ .nfi_clk_div = 1,
+};
+
+static const struct mtk_nfc_caps mtk_nfc_caps_mt2712 = {
+ .spare_size = spare_size_mt2712,
+ .num_spare_size = 19,
+ .pageformat_spare_shift = 16,
+ .nfi_clk_div = 2,
+};
+
+static const struct of_device_id mtk_nfc_id_table[] = {
+ {
+ .compatible = "mediatek,mt2701-nfc",
+ .data = &mtk_nfc_caps_mt2701,
+ }, {
+ .compatible = "mediatek,mt2712-nfc",
+ .data = &mtk_nfc_caps_mt2712,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mtk_nfc_id_table);
+
static int mtk_nfc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct mtk_nfc *nfc;
struct resource *res;
+ const struct of_device_id *of_nfc_id = NULL;
int ret, irq;
nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
@@ -1423,6 +1480,14 @@ static int mtk_nfc_probe(struct platform_device *pdev)
goto clk_disable;
}
+ of_nfc_id = of_match_device(mtk_nfc_id_table, &pdev->dev);
+ if (!of_nfc_id) {
+ ret = -ENODEV;
+ goto clk_disable;
+ }
+
+ nfc->caps = of_nfc_id->data;
+
platform_set_drvdata(pdev, nfc);
ret = mtk_nfc_nand_chips_init(dev, nfc);
@@ -1485,8 +1550,6 @@ static int mtk_nfc_resume(struct device *dev)
if (ret)
return ret;
- mtk_nfc_hw_init(nfc);
-
/* reset NAND chip if VCC was powered off */
list_for_each_entry(chip, &nfc->chips, node) {
nand = &chip->nand;
@@ -1503,12 +1566,6 @@ static int mtk_nfc_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(mtk_nfc_pm_ops, mtk_nfc_suspend, mtk_nfc_resume);
#endif
-static const struct of_device_id mtk_nfc_id_table[] = {
- { .compatible = "mediatek,mt2701-nfc" },
- {}
-};
-MODULE_DEVICE_TABLE(of, mtk_nfc_id_table);
-
static struct platform_driver mtk_nfc_driver = {
.probe = mtk_nfc_probe,
.remove = mtk_nfc_remove,
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index 61ca020c5272..a764d5ca7536 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -152,9 +152,8 @@ struct mxc_nand_devtype_data {
void (*select_chip)(struct mtd_info *mtd, int chip);
int (*correct_data)(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc);
- int (*setup_data_interface)(struct mtd_info *mtd,
- const struct nand_data_interface *conf,
- bool check_only);
+ int (*setup_data_interface)(struct mtd_info *mtd, int csline,
+ const struct nand_data_interface *conf);
/*
* On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked
@@ -1015,9 +1014,8 @@ static void preset_v1(struct mtd_info *mtd)
writew(0x4, NFC_V1_V2_WRPROT);
}
-static int mxc_nand_v2_setup_data_interface(struct mtd_info *mtd,
- const struct nand_data_interface *conf,
- bool check_only)
+static int mxc_nand_v2_setup_data_interface(struct mtd_info *mtd, int csline,
+ const struct nand_data_interface *conf)
{
struct nand_chip *nand_chip = mtd_to_nand(mtd);
struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
@@ -1075,7 +1073,7 @@ static int mxc_nand_v2_setup_data_interface(struct mtd_info *mtd,
return -EINVAL;
}
- if (check_only)
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
return 0;
ret = clk_set_rate(host->clk, rate);
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index bf8486c406d3..5fa5ddc94834 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -755,6 +755,16 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
return;
/* This applies to read commands */
+ case NAND_CMD_READ0:
+ /*
+ * READ0 is sometimes used to exit GET STATUS mode. When this
+ * is the case no address cycles are requested, and we can use
+ * this information to detect that we should not wait for the
+ * device to be ready.
+ */
+ if (column == -1 && page_addr == -1)
+ return;
+
default:
/*
* If we don't have access to the busy pin, we apply the given
@@ -889,6 +899,15 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
return;
case NAND_CMD_READ0:
+ /*
+ * READ0 is sometimes used to exit GET STATUS mode. When this
+ * is the case no address cycles are requested, and we can use
+ * this information to detect that READSTART should not be
+ * issued.
+ */
+ if (column == -1 && page_addr == -1)
+ return;
+
chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
chip->cmd_ctrl(mtd, NAND_CMD_NONE,
@@ -1044,12 +1063,13 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
/**
* nand_reset_data_interface - Reset data interface and timings
* @chip: The NAND chip
+ * @chipnr: Internal die id
*
* Reset the Data interface and timings to ONFI mode 0.
*
* Returns 0 for success or negative error code otherwise.
*/
-static int nand_reset_data_interface(struct nand_chip *chip)
+static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)
{
struct mtd_info *mtd = nand_to_mtd(chip);
const struct nand_data_interface *conf;
@@ -1073,7 +1093,7 @@ static int nand_reset_data_interface(struct nand_chip *chip)
*/
conf = nand_get_default_data_interface();
- ret = chip->setup_data_interface(mtd, conf, false);
+ ret = chip->setup_data_interface(mtd, chipnr, conf);
if (ret)
pr_err("Failed to configure data interface to SDR timing mode 0\n");
@@ -1083,6 +1103,7 @@ static int nand_reset_data_interface(struct nand_chip *chip)
/**
* nand_setup_data_interface - Setup the best data interface and timings
* @chip: The NAND chip
+ * @chipnr: Internal die id
*
* Find and configure the best data interface and NAND timings supported by
* the chip and the driver.
@@ -1092,7 +1113,7 @@ static int nand_reset_data_interface(struct nand_chip *chip)
*
* Returns 0 for success or negative error code otherwise.
*/
-static int nand_setup_data_interface(struct nand_chip *chip)
+static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
{
struct mtd_info *mtd = nand_to_mtd(chip);
int ret;
@@ -1116,7 +1137,7 @@ static int nand_setup_data_interface(struct nand_chip *chip)
goto err;
}
- ret = chip->setup_data_interface(mtd, chip->data_interface, false);
+ ret = chip->setup_data_interface(mtd, chipnr, chip->data_interface);
err:
return ret;
}
@@ -1167,8 +1188,10 @@ static int nand_init_data_interface(struct nand_chip *chip)
if (ret)
continue;
- ret = chip->setup_data_interface(mtd, chip->data_interface,
- true);
+ /* Pass -1 to only */
+ ret = chip->setup_data_interface(mtd,
+ NAND_DATA_IFACE_CHECK_ONLY,
+ chip->data_interface);
if (!ret) {
chip->onfi_timing_mode_default = mode;
break;
@@ -1195,7 +1218,7 @@ int nand_reset(struct nand_chip *chip, int chipnr)
struct mtd_info *mtd = nand_to_mtd(chip);
int ret;
- ret = nand_reset_data_interface(chip);
+ ret = nand_reset_data_interface(chip, chipnr);
if (ret)
return ret;
@@ -1208,7 +1231,7 @@ int nand_reset(struct nand_chip *chip, int chipnr)
chip->select_chip(mtd, -1);
chip->select_chip(mtd, chipnr);
- ret = nand_setup_data_interface(chip);
+ ret = nand_setup_data_interface(chip, chipnr);
chip->select_chip(mtd, -1);
if (ret)
return ret;
@@ -1424,7 +1447,10 @@ static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold)
for (; len >= sizeof(long);
len -= sizeof(long), bitmap += sizeof(long)) {
- weight = hweight_long(*((unsigned long *)bitmap));
+ unsigned long d = *((unsigned long *)bitmap);
+ if (d == ~0UL)
+ continue;
+ weight = hweight_long(d);
bitflips += BITS_PER_LONG - weight;
if (unlikely(bitflips > bitflips_threshold))
return -EBADMSG;
@@ -1527,14 +1553,15 @@ EXPORT_SYMBOL(nand_check_erased_ecc_chunk);
*
* Not for syndrome calculating ECC controllers, which use a special oob layout.
*/
-static 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(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
{
chip->read_buf(mtd, buf, mtd->writesize);
if (oob_required)
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0;
}
+EXPORT_SYMBOL(nand_read_page_raw);
/**
* nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc
@@ -2472,8 +2499,8 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
*
* Not for syndrome calculating ECC controllers, which use a special oob layout.
*/
-static 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(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
{
chip->write_buf(mtd, buf, mtd->writesize);
if (oob_required)
@@ -2481,6 +2508,7 @@ static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
return 0;
}
+EXPORT_SYMBOL(nand_write_page_raw);
/**
* nand_write_page_raw_syndrome - [INTERN] raw page write function
@@ -2718,7 +2746,7 @@ static int nand_write_page_syndrome(struct mtd_info *mtd,
*/
static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offset, int data_len, const uint8_t *buf,
- int oob_required, int page, int cached, int raw)
+ int oob_required, int page, int raw)
{
int status, subpage;
@@ -2744,30 +2772,12 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
if (status < 0)
return status;
- /*
- * Cached progamming disabled for now. Not sure if it's worth the
- * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s).
- */
- cached = 0;
+ if (nand_standard_page_accessors(&chip->ecc)) {
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
- if (!cached || !NAND_HAS_CACHEPROG(chip)) {
-
- if (nand_standard_page_accessors(&chip->ecc))
- chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
status = chip->waitfunc(mtd, chip);
- /*
- * See if operation failed and additional status checks are
- * available.
- */
- if ((status & NAND_STATUS_FAIL) && (chip->errstat))
- status = chip->errstat(mtd, chip, FL_WRITING, status,
- page);
-
if (status & NAND_STATUS_FAIL)
return -EIO;
- } else {
- chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
- status = chip->waitfunc(mtd, chip);
}
return 0;
@@ -2875,7 +2885,6 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
while (1) {
int bytes = mtd->writesize;
- int cached = writelen > bytes && page != blockmask;
uint8_t *wbuf = buf;
int use_bufpoi;
int part_pagewr = (column || writelen < mtd->writesize);
@@ -2893,7 +2902,6 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
if (use_bufpoi) {
pr_debug("%s: using write bounce buffer for buf@%p\n",
__func__, buf);
- cached = 0;
if (part_pagewr)
bytes = min_t(int, bytes - column, writelen);
chip->pagebuf = -1;
@@ -2912,7 +2920,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
}
ret = nand_write_page(mtd, chip, column, bytes, wbuf,
- oob_required, page, cached,
+ oob_required, page,
(ops->mode == MTD_OPS_RAW));
if (ret)
break;
@@ -3228,14 +3236,6 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
status = chip->erase(mtd, page & chip->pagemask);
- /*
- * See if operation failed and additional status checks are
- * available
- */
- if ((status & NAND_STATUS_FAIL) && (chip->errstat))
- status = chip->errstat(mtd, chip, FL_ERASING,
- status, page);
-
/* See if block erase succeeded */
if (status & NAND_STATUS_FAIL) {
pr_debug("%s: failed erase, page 0x%08x\n",
@@ -3422,6 +3422,25 @@ static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
+ * nand_onfi_get_set_features_notsupp - set/get features stub returning
+ * -ENOTSUPP
+ * @mtd: MTD device structure
+ * @chip: nand chip info structure
+ * @addr: feature address.
+ * @subfeature_param: the subfeature parameters, a four bytes array.
+ *
+ * Should be used by NAND controller drivers that do not support the SET/GET
+ * FEATURES operations.
+ */
+int nand_onfi_get_set_features_notsupp(struct mtd_info *mtd,
+ struct nand_chip *chip, int addr,
+ u8 *subfeature_param)
+{
+ return -ENOTSUPP;
+}
+EXPORT_SYMBOL(nand_onfi_get_set_features_notsupp);
+
+/**
* nand_suspend - [MTD Interface] Suspend the NAND flash
* @mtd: MTD device structure
*/
@@ -4180,6 +4199,7 @@ static const char * const nand_ecc_modes[] = {
[NAND_ECC_HW] = "hw",
[NAND_ECC_HW_SYNDROME] = "hw_syndrome",
[NAND_ECC_HW_OOB_FIRST] = "hw_oob_first",
+ [NAND_ECC_ON_DIE] = "on-die",
};
static int of_get_nand_ecc_mode(struct device_node *np)
@@ -4374,7 +4394,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
* For the other dies, nand_reset() will automatically switch to the
* best mode for us.
*/
- ret = nand_setup_data_interface(chip);
+ ret = nand_setup_data_interface(chip, 0);
if (ret)
goto err_nand_init;
@@ -4512,6 +4532,226 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd)
}
}
+/**
+ * nand_check_ecc_caps - check the sanity of preset ECC settings
+ * @chip: nand chip info structure
+ * @caps: ECC caps info structure
+ * @oobavail: OOB size that the ECC engine can use
+ *
+ * When ECC step size and strength are already set, check if they are supported
+ * 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)
+{
+ 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 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];
+
+ if (stepinfo->stepsize != preset_step)
+ continue;
+
+ for (j = 0; j < stepinfo->nstrengths; j++) {
+ if (stepinfo->strengths[j] != preset_strength)
+ continue;
+
+ ecc_bytes = caps->calc_ecc_bytes(preset_step,
+ preset_strength);
+ if (WARN_ON_ONCE(ecc_bytes < 0))
+ return ecc_bytes;
+
+ if (ecc_bytes * nsteps > oobavail) {
+ pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB",
+ preset_step, preset_strength);
+ return -ENOSPC;
+ }
+
+ chip->ecc.bytes = ecc_bytes;
+
+ return 0;
+ }
+ }
+
+ pr_err("ECC (step, strength) = (%d, %d) not supported on this controller",
+ preset_step, preset_strength);
+
+ return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(nand_check_ecc_caps);
+
+/**
+ * nand_match_ecc_req - meet the chip's requirement with least ECC bytes
+ * @chip: nand chip info structure
+ * @caps: ECC engine caps info structure
+ * @oobavail: OOB size that the ECC engine can use
+ *
+ * If a chip's ECC requirement is provided, try to meet it with the least
+ * 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)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ const struct nand_ecc_step_info *stepinfo;
+ int req_step = chip->ecc_step_ds;
+ int req_strength = chip->ecc_strength_ds;
+ int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total;
+ int best_step, best_strength, best_ecc_bytes;
+ 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;
+
+ /* number of correctable bits the chip requires in a page */
+ req_corr = mtd->writesize / req_step * req_strength;
+
+ for (i = 0; i < caps->nstepinfos; i++) {
+ stepinfo = &caps->stepinfos[i];
+ step_size = stepinfo->stepsize;
+
+ for (j = 0; j < stepinfo->nstrengths; j++) {
+ strength = stepinfo->strengths[j];
+
+ /*
+ * If both step size and strength are smaller than the
+ * chip's requirement, it is not easy to compare the
+ * resulted reliability.
+ */
+ if (step_size < req_step && strength < req_strength)
+ continue;
+
+ if (mtd->writesize % step_size)
+ continue;
+
+ nsteps = mtd->writesize / step_size;
+
+ ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
+ if (WARN_ON_ONCE(ecc_bytes < 0))
+ continue;
+ ecc_bytes_total = ecc_bytes * nsteps;
+
+ if (ecc_bytes_total > oobavail ||
+ strength * nsteps < req_corr)
+ continue;
+
+ /*
+ * We assume the best is to meet the chip's requrement
+ * with the least number of ECC bytes.
+ */
+ if (ecc_bytes_total < best_ecc_bytes_total) {
+ best_ecc_bytes_total = ecc_bytes_total;
+ best_step = step_size;
+ best_strength = strength;
+ best_ecc_bytes = ecc_bytes;
+ }
+ }
+ }
+
+ if (best_ecc_bytes_total == INT_MAX)
+ return -ENOTSUPP;
+
+ chip->ecc.size = best_step;
+ chip->ecc.strength = best_strength;
+ chip->ecc.bytes = best_ecc_bytes;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_match_ecc_req);
+
+/**
+ * nand_maximize_ecc - choose the max ECC strength available
+ * @chip: nand chip info structure
+ * @caps: ECC engine caps info structure
+ * @oobavail: OOB size that the ECC engine can use
+ *
+ * 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)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ const struct nand_ecc_step_info *stepinfo;
+ int step_size, strength, nsteps, ecc_bytes, corr;
+ int best_corr = 0;
+ int best_step = 0;
+ 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;
+
+ /* If chip->ecc.size is already set, respect it */
+ if (chip->ecc.size && step_size != chip->ecc.size)
+ continue;
+
+ for (j = 0; j < stepinfo->nstrengths; j++) {
+ strength = stepinfo->strengths[j];
+
+ if (mtd->writesize % step_size)
+ continue;
+
+ nsteps = mtd->writesize / step_size;
+
+ ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
+ if (WARN_ON_ONCE(ecc_bytes < 0))
+ continue;
+
+ if (ecc_bytes * nsteps > oobavail)
+ continue;
+
+ corr = strength * nsteps;
+
+ /*
+ * If the number of correctable bits is the same,
+ * bigger step_size has more reliability.
+ */
+ if (corr > best_corr ||
+ (corr == best_corr && step_size > best_step)) {
+ best_corr = corr;
+ best_step = step_size;
+ best_strength = strength;
+ best_ecc_bytes = ecc_bytes;
+ }
+ }
+ }
+
+ if (!best_corr)
+ return -ENOTSUPP;
+
+ chip->ecc.size = best_step;
+ chip->ecc.strength = best_strength;
+ chip->ecc.bytes = best_ecc_bytes;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_maximize_ecc);
+
/*
* Check if the chip configuration meet the datasheet requirements.
@@ -4733,6 +4973,18 @@ int nand_scan_tail(struct mtd_info *mtd)
}
break;
+ case NAND_ECC_ON_DIE:
+ if (!ecc->read_page || !ecc->write_page) {
+ WARN(1, "No ECC functions supplied; on-die ECC not possible\n");
+ ret = -EINVAL;
+ goto err_free;
+ }
+ if (!ecc->read_oob)
+ ecc->read_oob = nand_read_oob_std;
+ if (!ecc->write_oob)
+ ecc->write_oob = nand_write_oob_std;
+ break;
+
case NAND_ECC_NONE:
pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n");
ecc->read_page = nand_read_page_raw;
@@ -4773,6 +5025,11 @@ int nand_scan_tail(struct mtd_info *mtd)
goto err_free;
}
ecc->total = ecc->steps * ecc->bytes;
+ if (ecc->total > mtd->oobsize) {
+ WARN(1, "Total number of ECC bytes exceeded oobsize\n");
+ ret = -EINVAL;
+ goto err_free;
+ }
/*
* The number of bytes available for a client to place data into
diff --git a/drivers/mtd/nand/nand_micron.c b/drivers/mtd/nand/nand_micron.c
index 877011069251..c30ab60f8e1b 100644
--- a/drivers/mtd/nand/nand_micron.c
+++ b/drivers/mtd/nand/nand_micron.c
@@ -17,6 +17,12 @@
#include <linux/mtd/nand.h>
+/*
+ * Special Micron status bit that indicates when the block has been
+ * corrected by on-die ECC and should be rewritten
+ */
+#define NAND_STATUS_WRITE_RECOMMENDED BIT(3)
+
struct nand_onfi_vendor_micron {
u8 two_plane_read;
u8 read_cache;
@@ -66,9 +72,197 @@ 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)
+{
+ if (section >= 4)
+ return -ERANGE;
+
+ oobregion->offset = (section * 16) + 8;
+ oobregion->length = 8;
+
+ return 0;
+}
+
+static int micron_nand_on_die_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section >= 4)
+ return -ERANGE;
+
+ oobregion->offset = (section * 16) + 2;
+ oobregion->length = 6;
+
+ 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 int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable)
+{
+ u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, };
+
+ if (enable)
+ feature[0] |= ONFI_FEATURE_ON_DIE_ECC_EN;
+
+ return chip->onfi_set_features(nand_to_mtd(chip), chip,
+ ONFI_FEATURE_ON_DIE_ECC, feature);
+}
+
+static int
+micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required,
+ int page)
+{
+ int status;
+ int max_bitflips = 0;
+
+ micron_nand_on_die_ecc_setup(chip, true);
+
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+ status = chip->read_byte(mtd);
+ 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)
+ max_bitflips = chip->ecc.strength;
+
+ chip->cmdfunc(mtd, NAND_CMD_READ0, -1, -1);
+
+ nand_read_page_raw(mtd, chip, buf, oob_required, page);
+
+ micron_nand_on_die_ecc_setup(chip, false);
+
+ return max_bitflips;
+}
+
+static int
+micron_nand_write_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ int status;
+
+ micron_nand_on_die_ecc_setup(chip, true);
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+ nand_write_page_raw(mtd, chip, buf, oob_required, page);
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+
+ micron_nand_on_die_ecc_setup(chip, false);
+
+ return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
+
+static int
+micron_nand_read_page_raw_on_die_ecc(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ uint8_t *buf, int oob_required,
+ int page)
+{
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+ nand_read_page_raw(mtd, chip, buf, oob_required, page);
+
+ return 0;
+}
+
+static int
+micron_nand_write_page_raw_on_die_ecc(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ int status;
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+ nand_write_page_raw(mtd, chip, buf, oob_required, page);
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+
+ return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
+
+enum {
+ /* The NAND flash doesn't support on-die ECC */
+ MICRON_ON_DIE_UNSUPPORTED,
+
+ /*
+ * The NAND flash supports on-die ECC and it can be
+ * enabled/disabled by a set features command.
+ */
+ MICRON_ON_DIE_SUPPORTED,
+
+ /*
+ * The NAND flash supports on-die ECC, and it cannot be
+ * disabled.
+ */
+ MICRON_ON_DIE_MANDATORY,
+};
+
+/*
+ * 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
+ * also check if it can be disabled, because some Micron NANDs do not
+ * allow disabling the on-die ECC and we don't support such NANDs for
+ * now.
+ *
+ * This function also has the side effect of disabling on-die ECC if
+ * it had been left enabled by the firmware/bootloader.
+ */
+static int micron_supports_on_die_ecc(struct nand_chip *chip)
+{
+ u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, };
+ int ret;
+
+ if (chip->onfi_version == 0)
+ return MICRON_ON_DIE_UNSUPPORTED;
+
+ if (chip->bits_per_cell != 1)
+ return MICRON_ON_DIE_UNSUPPORTED;
+
+ ret = micron_nand_on_die_ecc_setup(chip, true);
+ if (ret)
+ return MICRON_ON_DIE_UNSUPPORTED;
+
+ chip->onfi_get_features(nand_to_mtd(chip), chip,
+ ONFI_FEATURE_ON_DIE_ECC, feature);
+ if ((feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) == 0)
+ return MICRON_ON_DIE_UNSUPPORTED;
+
+ ret = micron_nand_on_die_ecc_setup(chip, false);
+ if (ret)
+ return MICRON_ON_DIE_UNSUPPORTED;
+
+ chip->onfi_get_features(nand_to_mtd(chip), chip,
+ ONFI_FEATURE_ON_DIE_ECC, feature);
+ if (feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN)
+ 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.
+ */
+ if (chip->onfi_params.ecc_bits != 4)
+ return MICRON_ON_DIE_UNSUPPORTED;
+
+ return MICRON_ON_DIE_SUPPORTED;
+}
+
static int micron_nand_init(struct nand_chip *chip)
{
struct mtd_info *mtd = nand_to_mtd(chip);
+ int ondie;
int ret;
ret = micron_nand_onfi_init(chip);
@@ -78,6 +272,34 @@ static int micron_nand_init(struct nand_chip *chip)
if (mtd->writesize == 2048)
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
+ ondie = micron_supports_on_die_ecc(chip);
+
+ if (ondie == MICRON_ON_DIE_MANDATORY) {
+ pr_err("On-die ECC forcefully enabled, not supported\n");
+ return -EINVAL;
+ }
+
+ 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;
+ }
+
+ chip->ecc.options = NAND_ECC_CUSTOM_PAGE_ACCESS;
+ chip->ecc.bytes = 8;
+ chip->ecc.size = 512;
+ chip->ecc.strength = 4;
+ 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 =
+ micron_nand_read_page_raw_on_die_ecc;
+ chip->ecc.write_page_raw =
+ micron_nand_write_page_raw_on_die_ecc;
+
+ mtd_set_ooblayout(mtd, &micron_nand_on_die_ooblayout_ops);
+ }
+
return 0;
}
diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c
index f8e463a97b9e..209170ed2b76 100644
--- a/drivers/mtd/nand/orion_nand.c
+++ b/drivers/mtd/nand/orion_nand.c
@@ -166,7 +166,11 @@ static int __init orion_nand_probe(struct platform_device *pdev)
}
}
- clk_prepare_enable(info->clk);
+ ret = clk_prepare_enable(info->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare clock!\n");
+ return ret;
+ }
ret = nand_scan(mtd, 1);
if (ret)
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 649ba8200832..74dae4bbdac8 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -1812,6 +1812,8 @@ static int alloc_nand_resource(struct platform_device *pdev)
chip->write_buf = pxa3xx_nand_write_buf;
chip->options |= NAND_NO_SUBPAGE_WRITE;
chip->cmdfunc = nand_cmdfunc;
+ chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
+ chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
}
nand_hw_control_init(chip->controller);
diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index 57d483ac5765..88af7145a51a 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -2008,6 +2008,8 @@ static int qcom_nand_host_init(struct qcom_nand_controller *nandc,
chip->read_byte = qcom_nandc_read_byte;
chip->read_buf = qcom_nandc_read_buf;
chip->write_buf = qcom_nandc_write_buf;
+ chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
+ chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
/*
* the bad block marker is readable only when we read the last codeword
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index f0b030d44f71..9e0c849607b9 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -812,9 +812,8 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
return -ENODEV;
}
-static int s3c2410_nand_setup_data_interface(struct mtd_info *mtd,
- const struct nand_data_interface *conf,
- bool check_only)
+static int s3c2410_nand_setup_data_interface(struct mtd_info *mtd, int csline,
+ const struct nand_data_interface *conf)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
struct s3c2410_platform_nand *pdata = info->platform;
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
index 442ce619b3b6..891ac7b99305 100644
--- a/drivers/mtd/nand/sh_flctl.c
+++ b/drivers/mtd/nand/sh_flctl.c
@@ -1183,6 +1183,8 @@ static int flctl_probe(struct platform_device *pdev)
nand->read_buf = flctl_read_buf;
nand->select_chip = flctl_select_chip;
nand->cmdfunc = flctl_cmdfunc;
+ nand->onfi_set_features = nand_onfi_get_set_features_notsupp;
+ nand->onfi_get_features = nand_onfi_get_set_features_notsupp;
if (pdata->flcmncr_val & SEL_16BIT)
nand->options |= NAND_BUSWIDTH_16;
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index 118a26fff368..d0b6f8f9f297 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -1301,7 +1301,6 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd,
sunxi_nfc_hw_ecc_enable(mtd);
- chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
for (i = data_offs / ecc->size;
i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) {
int data_off = i * ecc->size;
@@ -1592,9 +1591,8 @@ static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
#define sunxi_nand_lookup_timing(l, p, c) \
_sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
-static int sunxi_nfc_setup_data_interface(struct mtd_info *mtd,
- const struct nand_data_interface *conf,
- bool check_only)
+static int sunxi_nfc_setup_data_interface(struct mtd_info *mtd, int csline,
+ const struct nand_data_interface *conf)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nand_chip *chip = to_sunxi_nand(nand);
@@ -1707,7 +1705,7 @@ static int sunxi_nfc_setup_data_interface(struct mtd_info *mtd,
return tRHW;
}
- if (check_only)
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
return 0;
/*
@@ -1922,7 +1920,6 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage;
ecc->read_oob_raw = nand_read_oob_std;
ecc->write_oob_raw = nand_write_oob_std;
- ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage;
return 0;
}
diff --git a/drivers/mtd/nand/tango_nand.c b/drivers/mtd/nand/tango_nand.c
index 49b286c6c10f..9d40b793b1c4 100644
--- a/drivers/mtd/nand/tango_nand.c
+++ b/drivers/mtd/nand/tango_nand.c
@@ -303,7 +303,7 @@ static int tango_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const u8 *buf, int oob_required, int page)
{
struct tango_nfc *nfc = to_tango_nfc(chip->controller);
- int err, len = mtd->writesize;
+ int err, status, len = mtd->writesize;
/* Calling tango_write_oob() would send PAGEPROG twice */
if (oob_required)
@@ -314,6 +314,10 @@ static int tango_write_page(struct mtd_info *mtd, struct nand_chip *chip,
if (err)
return err;
+ status = chip->waitfunc(mtd, chip);
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+
return 0;
}
@@ -340,7 +344,7 @@ static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos)
if (!*buf) {
/* skip over "len" bytes */
- chip->cmdfunc(mtd, NAND_CMD_SEQIN, *pos, -1);
+ chip->cmdfunc(mtd, NAND_CMD_RNDIN, *pos, -1);
} else {
tango_write_buf(mtd, *buf, len);
*buf += len;
@@ -431,9 +435,16 @@ static int tango_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
static int tango_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
const u8 *buf, int oob_required, int page)
{
+ int status;
+
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page);
raw_write(chip, buf, chip->oob_poi);
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+ status = chip->waitfunc(mtd, chip);
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+
return 0;
}
@@ -484,9 +495,8 @@ static u32 to_ticks(int kHz, int ps)
return DIV_ROUND_UP_ULL((u64)kHz * ps, NSEC_PER_SEC);
}
-static int tango_set_timings(struct mtd_info *mtd,
- const struct nand_data_interface *conf,
- bool check_only)
+static int tango_set_timings(struct mtd_info *mtd, int csline,
+ const struct nand_data_interface *conf)
{
const struct nand_sdr_timings *sdr = nand_get_sdr_timings(conf);
struct nand_chip *chip = mtd_to_nand(mtd);
@@ -498,7 +508,7 @@ static int tango_set_timings(struct mtd_info *mtd,
if (IS_ERR(sdr))
return PTR_ERR(sdr);
- if (check_only)
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
return 0;
Trdy = to_ticks(kHz, sdr->tCEA_max - sdr->tREA_max);
diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c
index 3ea4bb19e12d..744ab10e8962 100644
--- a/drivers/mtd/nand/vf610_nfc.c
+++ b/drivers/mtd/nand/vf610_nfc.c
@@ -703,6 +703,8 @@ static int vf610_nfc_probe(struct platform_device *pdev)
chip->read_buf = vf610_nfc_read_buf;
chip->write_buf = vf610_nfc_write_buf;
chip->select_chip = vf610_nfc_select_chip;
+ chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
+ chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
chip->options |= NAND_NO_SUBPAGE_WRITE;
diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig
new file mode 100644
index 000000000000..d206b3c533bc
--- /dev/null
+++ b/drivers/mtd/parsers/Kconfig
@@ -0,0 +1,8 @@
+config MTD_PARSER_TRX
+ tristate "Parser for TRX format partitions"
+ depends on MTD && (BCM47XX || ARCH_BCM_5301X || COMPILE_TEST)
+ help
+ TRX is a firmware format used by Broadcom on their devices. It
+ may contain up to 3/4 partitions (depending on the version).
+ This driver will parse TRX header and report at least two partitions:
+ kernel and rootfs.
diff --git a/drivers/mtd/parsers/Makefile b/drivers/mtd/parsers/Makefile
new file mode 100644
index 000000000000..4d9024e0be3b
--- /dev/null
+++ b/drivers/mtd/parsers/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
diff --git a/drivers/mtd/parsers/parser_trx.c b/drivers/mtd/parsers/parser_trx.c
new file mode 100644
index 000000000000..df360a75e1eb
--- /dev/null
+++ b/drivers/mtd/parsers/parser_trx.c
@@ -0,0 +1,126 @@
+/*
+ * Parser for TRX format partitions
+ *
+ * Copyright (C) 2012 - 2017 Rafał Miłecki <rafal@milecki.pl>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#define TRX_PARSER_MAX_PARTS 4
+
+/* Magics */
+#define TRX_MAGIC 0x30524448
+#define UBI_EC_MAGIC 0x23494255 /* UBI# */
+
+struct trx_header {
+ uint32_t magic;
+ uint32_t length;
+ uint32_t crc32;
+ uint16_t flags;
+ uint16_t version;
+ uint32_t offset[3];
+} __packed;
+
+static const char *parser_trx_data_part_name(struct mtd_info *master,
+ size_t offset)
+{
+ uint32_t buf;
+ size_t bytes_read;
+ int err;
+
+ err = mtd_read(master, offset, sizeof(buf), &bytes_read,
+ (uint8_t *)&buf);
+ if (err && !mtd_is_bitflip(err)) {
+ pr_err("mtd_read error while parsing (offset: 0x%zX): %d\n",
+ offset, err);
+ goto out_default;
+ }
+
+ if (buf == UBI_EC_MAGIC)
+ return "ubi";
+
+out_default:
+ return "rootfs";
+}
+
+static int parser_trx_parse(struct mtd_info *mtd,
+ const struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+{
+ struct mtd_partition *parts;
+ struct mtd_partition *part;
+ struct trx_header trx;
+ size_t bytes_read;
+ uint8_t curr_part = 0, i = 0;
+ int err;
+
+ parts = kzalloc(sizeof(struct mtd_partition) * TRX_PARSER_MAX_PARTS,
+ GFP_KERNEL);
+ if (!parts)
+ return -ENOMEM;
+
+ err = mtd_read(mtd, 0, sizeof(trx), &bytes_read, (uint8_t *)&trx);
+ if (err) {
+ pr_err("MTD reading error: %d\n", err);
+ kfree(parts);
+ return err;
+ }
+
+ if (trx.magic != TRX_MAGIC) {
+ kfree(parts);
+ return -ENOENT;
+ }
+
+ /* We have LZMA loader if there is address in offset[2] */
+ if (trx.offset[2]) {
+ part = &parts[curr_part++];
+ part->name = "loader";
+ part->offset = trx.offset[i];
+ i++;
+ }
+
+ if (trx.offset[i]) {
+ part = &parts[curr_part++];
+ part->name = "linux";
+ part->offset = trx.offset[i];
+ i++;
+ }
+
+ if (trx.offset[i]) {
+ part = &parts[curr_part++];
+ part->name = parser_trx_data_part_name(mtd, trx.offset[i]);
+ part->offset = trx.offset[i];
+ i++;
+ }
+
+ /*
+ * Assume that every partition ends at the beginning of the one it is
+ * followed by.
+ */
+ for (i = 0; i < curr_part; i++) {
+ u64 next_part_offset = (i < curr_part - 1) ?
+ parts[i + 1].offset : mtd->size;
+
+ parts[i].size = next_part_offset - parts[i].offset;
+ }
+
+ *pparts = parts;
+ return i;
+};
+
+static struct mtd_part_parser mtd_parser_trx = {
+ .parse_fn = parser_trx_parse,
+ .name = "trx",
+};
+module_mtd_part_parser(mtd_parser_trx);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Parser for TRX format partitions");
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
index bfdfb1e72b38..293c8a4d1e49 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -108,7 +108,7 @@ config SPI_INTEL_SPI_PLATFORM
config SPI_STM32_QUADSPI
tristate "STM32 Quad SPI controller"
- depends on ARCH_STM32
+ depends on ARCH_STM32 || COMPILE_TEST
help
This enables support for the STM32 Quad SPI controller.
We only connect the NOR to this controller.
diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
index 56051d30f000..0106357421bd 100644
--- a/drivers/mtd/spi-nor/aspeed-smc.c
+++ b/drivers/mtd/spi-nor/aspeed-smc.c
@@ -19,6 +19,7 @@
#include <linux/mtd/spi-nor.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#include <linux/sizes.h>
#include <linux/sysfs.h>
#define DEVICE_NAME "aspeed-smc"
@@ -97,6 +98,7 @@ struct aspeed_smc_chip {
struct aspeed_smc_controller *controller;
void __iomem *ctl; /* control register */
void __iomem *ahb_base; /* base of chip window */
+ u32 ahb_window_size; /* chip mapping window size */
u32 ctl_val[smc_max]; /* control settings */
enum aspeed_smc_flash_type type; /* what type of flash */
struct spi_nor nor;
@@ -109,6 +111,7 @@ struct aspeed_smc_controller {
const struct aspeed_smc_info *info; /* type info of controller */
void __iomem *regs; /* controller registers */
void __iomem *ahb_base; /* per-chip windows resource */
+ u32 ahb_window_size; /* full mapping window size */
struct aspeed_smc_chip *chips[0]; /* pointers to attached chips */
};
@@ -180,8 +183,7 @@ struct aspeed_smc_controller {
#define CONTROL_KEEP_MASK \
(CONTROL_AAF_MODE | CONTROL_CE_INACTIVE_MASK | CONTROL_CLK_DIV4 | \
- CONTROL_IO_DUMMY_MASK | CONTROL_CLOCK_FREQ_SEL_MASK | \
- CONTROL_LSB_FIRST | CONTROL_CLOCK_MODE_3)
+ CONTROL_CLOCK_FREQ_SEL_MASK | CONTROL_LSB_FIRST | CONTROL_CLOCK_MODE_3)
/*
* The Segment Register uses a 8MB unit to encode the start address
@@ -194,6 +196,10 @@ struct aspeed_smc_controller {
#define SEGMENT_ADDR_REG0 0x30
#define SEGMENT_ADDR_START(_r) ((((_r) >> 16) & 0xFF) << 23)
#define SEGMENT_ADDR_END(_r) ((((_r) >> 24) & 0xFF) << 23)
+#define SEGMENT_ADDR_VALUE(start, end) \
+ (((((start) >> 23) & 0xFF) << 16) | ((((end) >> 23) & 0xFF) << 24))
+#define SEGMENT_ADDR_REG(controller, cs) \
+ ((controller)->regs + SEGMENT_ADDR_REG0 + (cs) * 4)
/*
* In user mode all data bytes read or written to the chip decode address
@@ -439,8 +445,7 @@ static void __iomem *aspeed_smc_chip_base(struct aspeed_smc_chip *chip,
u32 reg;
if (controller->info->nce > 1) {
- reg = readl(controller->regs + SEGMENT_ADDR_REG0 +
- chip->cs * 4);
+ reg = readl(SEGMENT_ADDR_REG(controller, chip->cs));
if (SEGMENT_ADDR_START(reg) >= SEGMENT_ADDR_END(reg))
return NULL;
@@ -451,6 +456,146 @@ static void __iomem *aspeed_smc_chip_base(struct aspeed_smc_chip *chip,
return controller->ahb_base + offset;
}
+static u32 aspeed_smc_ahb_base_phy(struct aspeed_smc_controller *controller)
+{
+ u32 seg0_val = readl(SEGMENT_ADDR_REG(controller, 0));
+
+ return SEGMENT_ADDR_START(seg0_val);
+}
+
+static u32 chip_set_segment(struct aspeed_smc_chip *chip, u32 cs, u32 start,
+ u32 size)
+{
+ struct aspeed_smc_controller *controller = chip->controller;
+ void __iomem *seg_reg;
+ u32 seg_oldval, seg_newval, ahb_base_phy, end;
+
+ ahb_base_phy = aspeed_smc_ahb_base_phy(controller);
+
+ seg_reg = SEGMENT_ADDR_REG(controller, cs);
+ seg_oldval = readl(seg_reg);
+
+ /*
+ * If the chip size is not specified, use the default segment
+ * size, but take into account the possible overlap with the
+ * previous segment
+ */
+ if (!size)
+ size = SEGMENT_ADDR_END(seg_oldval) - start;
+
+ /*
+ * The segment cannot exceed the maximum window size of the
+ * controller.
+ */
+ if (start + size > ahb_base_phy + controller->ahb_window_size) {
+ size = ahb_base_phy + controller->ahb_window_size - start;
+ dev_warn(chip->nor.dev, "CE%d window resized to %dMB",
+ cs, size >> 20);
+ }
+
+ end = start + size;
+ seg_newval = SEGMENT_ADDR_VALUE(start, end);
+ writel(seg_newval, seg_reg);
+
+ /*
+ * Restore default value if something goes wrong. The chip
+ * might have set some bogus value and we would loose access
+ * to the chip.
+ */
+ if (seg_newval != readl(seg_reg)) {
+ dev_err(chip->nor.dev, "CE%d window invalid", cs);
+ writel(seg_oldval, seg_reg);
+ start = SEGMENT_ADDR_START(seg_oldval);
+ end = SEGMENT_ADDR_END(seg_oldval);
+ size = end - start;
+ }
+
+ dev_info(chip->nor.dev, "CE%d window [ 0x%.8x - 0x%.8x ] %dMB",
+ cs, start, end, size >> 20);
+
+ return size;
+}
+
+/*
+ * The segment register defines the mapping window on the AHB bus and
+ * it needs to be configured depending on the chip size. The segment
+ * register of the following CE also needs to be tuned in order to
+ * provide a contiguous window across multiple chips.
+ *
+ * This is expected to be called in increasing CE order
+ */
+static u32 aspeed_smc_chip_set_segment(struct aspeed_smc_chip *chip)
+{
+ struct aspeed_smc_controller *controller = chip->controller;
+ u32 ahb_base_phy, start;
+ u32 size = chip->nor.mtd.size;
+
+ /*
+ * Each controller has a chip size limit for direct memory
+ * access
+ */
+ if (size > controller->info->maxsize)
+ size = controller->info->maxsize;
+
+ /*
+ * The AST2400 SPI controller only handles one chip and does
+ * not have segment registers. Let's use the chip size for the
+ * AHB window.
+ */
+ if (controller->info == &spi_2400_info)
+ goto out;
+
+ /*
+ * The AST2500 SPI controller has a HW bug when the CE0 chip
+ * size reaches 128MB. Enforce a size limit of 120MB to
+ * prevent the controller from using bogus settings in the
+ * segment register.
+ */
+ if (chip->cs == 0 && controller->info == &spi_2500_info &&
+ size == SZ_128M) {
+ size = 120 << 20;
+ dev_info(chip->nor.dev,
+ "CE%d window resized to %dMB (AST2500 HW quirk)",
+ chip->cs, size >> 20);
+ }
+
+ ahb_base_phy = aspeed_smc_ahb_base_phy(controller);
+
+ /*
+ * As a start address for the current segment, use the default
+ * start address if we are handling CE0 or use the previous
+ * segment ending address
+ */
+ if (chip->cs) {
+ u32 prev = readl(SEGMENT_ADDR_REG(controller, chip->cs - 1));
+
+ start = SEGMENT_ADDR_END(prev);
+ } else {
+ start = ahb_base_phy;
+ }
+
+ size = chip_set_segment(chip, chip->cs, start, size);
+
+ /* Update chip base address on the AHB bus */
+ chip->ahb_base = controller->ahb_base + (start - ahb_base_phy);
+
+ /*
+ * Now, make sure the next segment does not overlap with the
+ * current one we just configured, even if there is no
+ * available chip. That could break access in Command Mode.
+ */
+ if (chip->cs < controller->info->nce - 1)
+ chip_set_segment(chip, chip->cs + 1, start + size, 0);
+
+out:
+ if (size < chip->nor.mtd.size)
+ dev_warn(chip->nor.dev,
+ "CE%d window too small for chip %dMB",
+ chip->cs, (u32)chip->nor.mtd.size >> 20);
+
+ return size;
+}
+
static void aspeed_smc_chip_enable_write(struct aspeed_smc_chip *chip)
{
struct aspeed_smc_controller *controller = chip->controller;
@@ -524,7 +669,7 @@ static int aspeed_smc_chip_setup_init(struct aspeed_smc_chip *chip,
*/
chip->ahb_base = aspeed_smc_chip_base(chip, res);
if (!chip->ahb_base) {
- dev_warn(chip->nor.dev, "CE segment window closed.\n");
+ dev_warn(chip->nor.dev, "CE%d window closed", chip->cs);
return -EINVAL;
}
@@ -571,6 +716,9 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
if (chip->nor.addr_width == 4 && info->set_4b)
info->set_4b(chip);
+ /* This is for direct AHB access when using Command Mode. */
+ chip->ahb_window_size = aspeed_smc_chip_set_segment(chip);
+
/*
* base mode has not been optimized yet. use it for writes.
*/
@@ -585,14 +733,12 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
* TODO: Adjust clocks if fast read is supported and interpret
* SPI-NOR flags to adjust controller settings.
*/
- switch (chip->nor.flash_read) {
- case SPI_NOR_NORMAL:
- cmd = CONTROL_COMMAND_MODE_NORMAL;
- break;
- case SPI_NOR_FAST:
- cmd = CONTROL_COMMAND_MODE_FREAD;
- break;
- default:
+ if (chip->nor.read_proto == SNOR_PROTO_1_1_1) {
+ if (chip->nor.read_dummy == 0)
+ cmd = CONTROL_COMMAND_MODE_NORMAL;
+ else
+ cmd = CONTROL_COMMAND_MODE_FREAD;
+ } else {
dev_err(chip->nor.dev, "unsupported SPI read mode\n");
return -EINVAL;
}
@@ -608,6 +754,11 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
struct device_node *np, struct resource *r)
{
+ const struct spi_nor_hwcaps hwcaps = {
+ .mask = SNOR_HWCAPS_READ |
+ SNOR_HWCAPS_READ_FAST |
+ SNOR_HWCAPS_PP,
+ };
const struct aspeed_smc_info *info = controller->info;
struct device *dev = controller->dev;
struct device_node *child;
@@ -671,11 +822,11 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
break;
/*
- * TODO: Add support for SPI_NOR_QUAD and SPI_NOR_DUAL
+ * TODO: Add support for Dual and Quad SPI protocols
* attach when board support is present as determined
* by of property.
*/
- ret = spi_nor_scan(nor, NULL, SPI_NOR_NORMAL);
+ ret = spi_nor_scan(nor, NULL, &hwcaps);
if (ret)
break;
@@ -731,6 +882,8 @@ static int aspeed_smc_probe(struct platform_device *pdev)
if (IS_ERR(controller->ahb_base))
return PTR_ERR(controller->ahb_base);
+ controller->ahb_window_size = resource_size(res);
+
ret = aspeed_smc_setup_flash(controller, np, res);
if (ret)
dev_err(dev, "Aspeed SMC probe failed %d\n", ret);
diff --git a/drivers/mtd/spi-nor/atmel-quadspi.c b/drivers/mtd/spi-nor/atmel-quadspi.c
index 47937d9beec6..ba76fa8f2031 100644
--- a/drivers/mtd/spi-nor/atmel-quadspi.c
+++ b/drivers/mtd/spi-nor/atmel-quadspi.c
@@ -275,14 +275,48 @@ static void atmel_qspi_debug_command(struct atmel_qspi *aq,
static int atmel_qspi_run_command(struct atmel_qspi *aq,
const struct atmel_qspi_command *cmd,
- u32 ifr_tfrtyp, u32 ifr_width)
+ u32 ifr_tfrtyp, enum spi_nor_protocol proto)
{
u32 iar, icr, ifr, sr;
int err = 0;
iar = 0;
icr = 0;
- ifr = ifr_tfrtyp | ifr_width;
+ ifr = ifr_tfrtyp;
+
+ /* Set the SPI protocol */
+ switch (proto) {
+ case SNOR_PROTO_1_1_1:
+ ifr |= QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
+ break;
+
+ case SNOR_PROTO_1_1_2:
+ ifr |= QSPI_IFR_WIDTH_DUAL_OUTPUT;
+ break;
+
+ case SNOR_PROTO_1_1_4:
+ ifr |= QSPI_IFR_WIDTH_QUAD_OUTPUT;
+ break;
+
+ case SNOR_PROTO_1_2_2:
+ ifr |= QSPI_IFR_WIDTH_DUAL_IO;
+ break;
+
+ case SNOR_PROTO_1_4_4:
+ ifr |= QSPI_IFR_WIDTH_QUAD_IO;
+ break;
+
+ case SNOR_PROTO_2_2_2:
+ ifr |= QSPI_IFR_WIDTH_DUAL_CMD;
+ break;
+
+ case SNOR_PROTO_4_4_4:
+ ifr |= QSPI_IFR_WIDTH_QUAD_CMD;
+ break;
+
+ default:
+ return -EINVAL;
+ }
/* Compute instruction parameters */
if (cmd->enable.bits.instruction) {
@@ -434,7 +468,7 @@ static int atmel_qspi_read_reg(struct spi_nor *nor, u8 opcode,
cmd.rx_buf = buf;
cmd.buf_len = len;
return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ,
- QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
+ nor->reg_proto);
}
static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
@@ -450,7 +484,7 @@ static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
cmd.tx_buf = buf;
cmd.buf_len = len;
return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
- QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
+ nor->reg_proto);
}
static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
@@ -469,7 +503,7 @@ static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
cmd.tx_buf = write_buf;
cmd.buf_len = len;
ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM,
- QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
+ nor->write_proto);
return (ret < 0) ? ret : len;
}
@@ -484,7 +518,7 @@ static int atmel_qspi_erase(struct spi_nor *nor, loff_t offs)
cmd.instruction = nor->erase_opcode;
cmd.address = (u32)offs;
return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
- QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
+ nor->reg_proto);
}
static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
@@ -493,27 +527,8 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
struct atmel_qspi *aq = nor->priv;
struct atmel_qspi_command cmd;
u8 num_mode_cycles, num_dummy_cycles;
- u32 ifr_width;
ssize_t ret;
- switch (nor->flash_read) {
- case SPI_NOR_NORMAL:
- case SPI_NOR_FAST:
- ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
- break;
-
- case SPI_NOR_DUAL:
- ifr_width = QSPI_IFR_WIDTH_DUAL_OUTPUT;
- break;
-
- case SPI_NOR_QUAD:
- ifr_width = QSPI_IFR_WIDTH_QUAD_OUTPUT;
- break;
-
- default:
- return -EINVAL;
- }
-
if (nor->read_dummy >= 2) {
num_mode_cycles = 2;
num_dummy_cycles = nor->read_dummy - 2;
@@ -536,7 +551,7 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
cmd.rx_buf = read_buf;
cmd.buf_len = len;
ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ_MEM,
- ifr_width);
+ nor->read_proto);
return (ret < 0) ? ret : len;
}
@@ -590,6 +605,20 @@ static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
static int atmel_qspi_probe(struct platform_device *pdev)
{
+ const struct spi_nor_hwcaps hwcaps = {
+ .mask = SNOR_HWCAPS_READ |
+ SNOR_HWCAPS_READ_FAST |
+ SNOR_HWCAPS_READ_1_1_2 |
+ SNOR_HWCAPS_READ_1_2_2 |
+ SNOR_HWCAPS_READ_2_2_2 |
+ SNOR_HWCAPS_READ_1_1_4 |
+ SNOR_HWCAPS_READ_1_4_4 |
+ SNOR_HWCAPS_READ_4_4_4 |
+ SNOR_HWCAPS_PP |
+ SNOR_HWCAPS_PP_1_1_4 |
+ SNOR_HWCAPS_PP_1_4_4 |
+ SNOR_HWCAPS_PP_4_4_4,
+ };
struct device_node *child, *np = pdev->dev.of_node;
struct atmel_qspi *aq;
struct resource *res;
@@ -679,7 +708,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
if (err)
goto disable_clk;
- err = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
+ err = spi_nor_scan(nor, NULL, &hwcaps);
if (err)
goto disable_clk;
diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index 9f8102de1b16..53c7d8e0327a 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -855,15 +855,14 @@ static int cqspi_set_protocol(struct spi_nor *nor, const int read)
f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
if (read) {
- switch (nor->flash_read) {
- case SPI_NOR_NORMAL:
- case SPI_NOR_FAST:
+ switch (nor->read_proto) {
+ case SNOR_PROTO_1_1_1:
f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
break;
- case SPI_NOR_DUAL:
+ case SNOR_PROTO_1_1_2:
f_pdata->data_width = CQSPI_INST_TYPE_DUAL;
break;
- case SPI_NOR_QUAD:
+ case SNOR_PROTO_1_1_4:
f_pdata->data_width = CQSPI_INST_TYPE_QUAD;
break;
default:
@@ -1069,6 +1068,13 @@ static void cqspi_controller_init(struct cqspi_st *cqspi)
static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
{
+ const struct spi_nor_hwcaps hwcaps = {
+ .mask = SNOR_HWCAPS_READ |
+ SNOR_HWCAPS_READ_FAST |
+ SNOR_HWCAPS_READ_1_1_2 |
+ SNOR_HWCAPS_READ_1_1_4 |
+ SNOR_HWCAPS_PP,
+ };
struct platform_device *pdev = cqspi->pdev;
struct device *dev = &pdev->dev;
struct cqspi_flash_pdata *f_pdata;
@@ -1123,7 +1129,7 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
goto err;
}
- ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
+ ret = spi_nor_scan(nor, NULL, &hwcaps);
if (ret)
goto err;
@@ -1277,7 +1283,7 @@ static const struct dev_pm_ops cqspi__dev_pm_ops = {
#define CQSPI_DEV_PM_OPS NULL
#endif
-static struct of_device_id const cqspi_dt_ids[] = {
+static const struct of_device_id cqspi_dt_ids[] = {
{.compatible = "cdns,qspi-nor",},
{ /* end of table */ }
};
diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index 1476135e0d50..f17d22435bfc 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -957,6 +957,10 @@ static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
static int fsl_qspi_probe(struct platform_device *pdev)
{
+ const struct spi_nor_hwcaps hwcaps = {
+ .mask = SNOR_HWCAPS_READ_1_1_4 |
+ SNOR_HWCAPS_PP,
+ };
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct fsl_qspi *q;
@@ -1065,7 +1069,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
/* set the chip address for READID */
fsl_qspi_set_base_addr(q, nor);
- ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
+ ret = spi_nor_scan(nor, NULL, &hwcaps);
if (ret)
goto mutex_failed;
diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c
index a286350627a6..d1106832b9d5 100644
--- a/drivers/mtd/spi-nor/hisi-sfc.c
+++ b/drivers/mtd/spi-nor/hisi-sfc.c
@@ -120,19 +120,24 @@ static inline int wait_op_finish(struct hifmc_host *host)
(reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT);
}
-static int get_if_type(enum read_mode flash_read)
+static int get_if_type(enum spi_nor_protocol proto)
{
enum hifmc_iftype if_type;
- switch (flash_read) {
- case SPI_NOR_DUAL:
+ switch (proto) {
+ case SNOR_PROTO_1_1_2:
if_type = IF_TYPE_DUAL;
break;
- case SPI_NOR_QUAD:
+ case SNOR_PROTO_1_2_2:
+ if_type = IF_TYPE_DIO;
+ break;
+ case SNOR_PROTO_1_1_4:
if_type = IF_TYPE_QUAD;
break;
- case SPI_NOR_NORMAL:
- case SPI_NOR_FAST:
+ case SNOR_PROTO_1_4_4:
+ if_type = IF_TYPE_QIO;
+ break;
+ case SNOR_PROTO_1_1_1:
default:
if_type = IF_TYPE_STD;
break;
@@ -253,7 +258,10 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
writel(FMC_DMA_LEN_SET(len), host->regbase + FMC_DMA_LEN);
reg = OP_CFG_FM_CS(priv->chipselect);
- if_type = get_if_type(nor->flash_read);
+ if (op_type == FMC_OP_READ)
+ if_type = get_if_type(nor->read_proto);
+ else
+ if_type = get_if_type(nor->write_proto);
reg |= OP_CFG_MEM_IF_TYPE(if_type);
if (op_type == FMC_OP_READ)
reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3);
@@ -321,6 +329,13 @@ static ssize_t hisi_spi_nor_write(struct spi_nor *nor, loff_t to,
static int hisi_spi_nor_register(struct device_node *np,
struct hifmc_host *host)
{
+ const struct spi_nor_hwcaps hwcaps = {
+ .mask = SNOR_HWCAPS_READ |
+ SNOR_HWCAPS_READ_FAST |
+ SNOR_HWCAPS_READ_1_1_2 |
+ SNOR_HWCAPS_READ_1_1_4 |
+ SNOR_HWCAPS_PP,
+ };
struct device *dev = host->dev;
struct spi_nor *nor;
struct hifmc_priv *priv;
@@ -362,7 +377,7 @@ static int hisi_spi_nor_register(struct device_node *np,
nor->read = hisi_spi_nor_read;
nor->write = hisi_spi_nor_write;
nor->erase = NULL;
- ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
+ ret = spi_nor_scan(nor, NULL, &hwcaps);
if (ret)
return ret;
diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/intel-spi.c
index 986a3d020a3a..8a596bfeddff 100644
--- a/drivers/mtd/spi-nor/intel-spi.c
+++ b/drivers/mtd/spi-nor/intel-spi.c
@@ -715,6 +715,11 @@ static void intel_spi_fill_partition(struct intel_spi *ispi,
struct intel_spi *intel_spi_probe(struct device *dev,
struct resource *mem, const struct intel_spi_boardinfo *info)
{
+ const struct spi_nor_hwcaps hwcaps = {
+ .mask = SNOR_HWCAPS_READ |
+ SNOR_HWCAPS_READ_FAST |
+ SNOR_HWCAPS_PP,
+ };
struct mtd_partition part;
struct intel_spi *ispi;
int ret;
@@ -746,7 +751,7 @@ struct intel_spi *intel_spi_probe(struct device *dev,
ispi->nor.write = intel_spi_write;
ispi->nor.erase = intel_spi_erase;
- ret = spi_nor_scan(&ispi->nor, NULL, SPI_NOR_NORMAL);
+ ret = spi_nor_scan(&ispi->nor, NULL, &hwcaps);
if (ret) {
dev_info(dev, "failed to locate the chip\n");
return ERR_PTR(ret);
diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c
index b6377707ce32..8a20ec4991c8 100644
--- a/drivers/mtd/spi-nor/mtk-quadspi.c
+++ b/drivers/mtd/spi-nor/mtk-quadspi.c
@@ -123,20 +123,20 @@ static void mt8173_nor_set_read_mode(struct mt8173_nor *mt8173_nor)
{
struct spi_nor *nor = &mt8173_nor->nor;
- switch (nor->flash_read) {
- case SPI_NOR_FAST:
+ switch (nor->read_proto) {
+ case SNOR_PROTO_1_1_1:
writeb(nor->read_opcode, mt8173_nor->base +
MTK_NOR_PRGDATA3_REG);
writeb(MTK_NOR_FAST_READ, mt8173_nor->base +
MTK_NOR_CFG1_REG);
break;
- case SPI_NOR_DUAL:
+ case SNOR_PROTO_1_1_2:
writeb(nor->read_opcode, mt8173_nor->base +
MTK_NOR_PRGDATA3_REG);
writeb(MTK_NOR_DUAL_READ_EN, mt8173_nor->base +
MTK_NOR_DUAL_REG);
break;
- case SPI_NOR_QUAD:
+ case SNOR_PROTO_1_1_4:
writeb(nor->read_opcode, mt8173_nor->base +
MTK_NOR_PRGDATA4_REG);
writeb(MTK_NOR_QUAD_READ_EN, mt8173_nor->base +
@@ -408,6 +408,11 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
struct device_node *flash_node)
{
+ const struct spi_nor_hwcaps hwcaps = {
+ .mask = SNOR_HWCAPS_READ_FAST |
+ SNOR_HWCAPS_READ_1_1_2 |
+ SNOR_HWCAPS_PP,
+ };
int ret;
struct spi_nor *nor;
@@ -426,7 +431,7 @@ static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
nor->write_reg = mt8173_nor_write_reg;
nor->mtd.name = "mtk_nor";
/* initialized with NULL */
- ret = spi_nor_scan(nor, NULL, SPI_NOR_DUAL);
+ ret = spi_nor_scan(nor, NULL, &hwcaps);
if (ret)
return ret;
diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c
index 73a14f40928b..15374216d4d9 100644
--- a/drivers/mtd/spi-nor/nxp-spifi.c
+++ b/drivers/mtd/spi-nor/nxp-spifi.c
@@ -240,13 +240,12 @@ static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs)
static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi)
{
- switch (spifi->nor.flash_read) {
- case SPI_NOR_NORMAL:
- case SPI_NOR_FAST:
+ switch (spifi->nor.read_proto) {
+ case SNOR_PROTO_1_1_1:
spifi->mcmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL;
break;
- case SPI_NOR_DUAL:
- case SPI_NOR_QUAD:
+ case SNOR_PROTO_1_1_2:
+ case SNOR_PROTO_1_1_4:
spifi->mcmd = SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA;
break;
default:
@@ -274,7 +273,11 @@ static void nxp_spifi_dummy_id_read(struct spi_nor *nor)
static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
struct device_node *np)
{
- enum read_mode flash_read;
+ struct spi_nor_hwcaps hwcaps = {
+ .mask = SNOR_HWCAPS_READ |
+ SNOR_HWCAPS_READ_FAST |
+ SNOR_HWCAPS_PP,
+ };
u32 ctrl, property;
u16 mode = 0;
int ret;
@@ -308,13 +311,12 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
if (mode & SPI_RX_DUAL) {
ctrl |= SPIFI_CTRL_DUAL;
- flash_read = SPI_NOR_DUAL;
+ hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
} else if (mode & SPI_RX_QUAD) {
ctrl &= ~SPIFI_CTRL_DUAL;
- flash_read = SPI_NOR_QUAD;
+ hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
} else {
ctrl |= SPIFI_CTRL_DUAL;
- flash_read = SPI_NOR_NORMAL;
}
switch (mode & (SPI_CPHA | SPI_CPOL)) {
@@ -351,7 +353,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
*/
nxp_spifi_dummy_id_read(&spifi->nor);
- ret = spi_nor_scan(&spifi->nor, NULL, flash_read);
+ ret = spi_nor_scan(&spifi->nor, NULL, &hwcaps);
if (ret) {
dev_err(spifi->dev, "device scan failed\n");
return ret;
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index dea8c9cbadf0..1413828ff1fb 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -150,24 +150,6 @@ static int read_cr(struct spi_nor *nor)
}
/*
- * Dummy Cycle calculation for different type of read.
- * It can be used to support more commands with
- * different dummy cycle requirements.
- */
-static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
-{
- switch (nor->flash_read) {
- case SPI_NOR_FAST:
- case SPI_NOR_DUAL:
- case SPI_NOR_QUAD:
- return 8;
- case SPI_NOR_NORMAL:
- return 0;
- }
- return 0;
-}
-
-/*
* Write status register 1 byte
* Returns negative if error occurred.
*/
@@ -221,6 +203,10 @@ static inline u8 spi_nor_convert_3to4_read(u8 opcode)
{ SPINOR_OP_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B },
{ SPINOR_OP_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B },
{ SPINOR_OP_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B },
+
+ { SPINOR_OP_READ_1_1_1_DTR, SPINOR_OP_READ_1_1_1_DTR_4B },
+ { SPINOR_OP_READ_1_2_2_DTR, SPINOR_OP_READ_1_2_2_DTR_4B },
+ { SPINOR_OP_READ_1_4_4_DTR, SPINOR_OP_READ_1_4_4_DTR_4B },
};
return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
@@ -1022,10 +1008,12 @@ static const struct flash_info spi_nor_ids[] = {
{ "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) },
{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
- { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
+ { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) },
{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
- { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },
+ { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "mx66u51235f", INFO(0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+ { "mx66l1g45g", INFO(0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
/* Micron */
@@ -1036,7 +1024,7 @@ static const struct flash_info spi_nor_ids[] = {
{ "n25q064a", INFO(0x20bb17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
- { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) },
+ { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "n25q256ax1", INFO(0x20bb19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) },
{ "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
{ "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
@@ -1076,6 +1064,7 @@ static const struct flash_info spi_nor_ids[] = {
{ "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) },
{ "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_DUAL_READ) },
{ "s25fl208k", INFO(0x014014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ) },
+ { "s25fl064l", INFO(0x016017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
@@ -1159,7 +1148,9 @@ static const struct flash_info spi_nor_ids[] = {
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
- { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) },
+ { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024,
+ SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ) },
/* Catalyst / On Semiconductor -- non-JEDEC */
{ "cat25c11", CAT25_INFO( 16, 8, 16, 1, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
@@ -1403,8 +1394,9 @@ static int macronix_quad_enable(struct spi_nor *nor)
write_sr(nor, val | SR_QUAD_EN_MX);
- if (spi_nor_wait_till_ready(nor))
- return 1;
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ return ret;
ret = read_sr(nor);
if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
@@ -1460,30 +1452,6 @@ static int spansion_quad_enable(struct spi_nor *nor)
return 0;
}
-static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
-{
- int status;
-
- switch (JEDEC_MFR(info)) {
- case SNOR_MFR_MACRONIX:
- status = macronix_quad_enable(nor);
- if (status) {
- dev_err(nor->dev, "Macronix quad-read not enabled\n");
- return -EINVAL;
- }
- return status;
- case SNOR_MFR_MICRON:
- return 0;
- default:
- status = spansion_quad_enable(nor);
- if (status) {
- dev_err(nor->dev, "Spansion quad-read not enabled\n");
- return -EINVAL;
- }
- return status;
- }
-}
-
static int spi_nor_check(struct spi_nor *nor)
{
if (!nor->dev || !nor->read || !nor->write ||
@@ -1536,8 +1504,349 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor)
return 0;
}
-int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
+struct spi_nor_read_command {
+ u8 num_mode_clocks;
+ u8 num_wait_states;
+ u8 opcode;
+ enum spi_nor_protocol proto;
+};
+
+struct spi_nor_pp_command {
+ u8 opcode;
+ enum spi_nor_protocol proto;
+};
+
+enum spi_nor_read_command_index {
+ SNOR_CMD_READ,
+ SNOR_CMD_READ_FAST,
+ SNOR_CMD_READ_1_1_1_DTR,
+
+ /* Dual SPI */
+ SNOR_CMD_READ_1_1_2,
+ SNOR_CMD_READ_1_2_2,
+ SNOR_CMD_READ_2_2_2,
+ SNOR_CMD_READ_1_2_2_DTR,
+
+ /* Quad SPI */
+ SNOR_CMD_READ_1_1_4,
+ SNOR_CMD_READ_1_4_4,
+ SNOR_CMD_READ_4_4_4,
+ SNOR_CMD_READ_1_4_4_DTR,
+
+ /* Octo SPI */
+ SNOR_CMD_READ_1_1_8,
+ SNOR_CMD_READ_1_8_8,
+ SNOR_CMD_READ_8_8_8,
+ SNOR_CMD_READ_1_8_8_DTR,
+
+ SNOR_CMD_READ_MAX
+};
+
+enum spi_nor_pp_command_index {
+ SNOR_CMD_PP,
+
+ /* Quad SPI */
+ SNOR_CMD_PP_1_1_4,
+ SNOR_CMD_PP_1_4_4,
+ SNOR_CMD_PP_4_4_4,
+
+ /* Octo SPI */
+ SNOR_CMD_PP_1_1_8,
+ SNOR_CMD_PP_1_8_8,
+ SNOR_CMD_PP_8_8_8,
+
+ SNOR_CMD_PP_MAX
+};
+
+struct spi_nor_flash_parameter {
+ u64 size;
+ u32 page_size;
+
+ struct spi_nor_hwcaps hwcaps;
+ struct spi_nor_read_command reads[SNOR_CMD_READ_MAX];
+ struct spi_nor_pp_command page_programs[SNOR_CMD_PP_MAX];
+
+ int (*quad_enable)(struct spi_nor *nor);
+};
+
+static void
+spi_nor_set_read_settings(struct spi_nor_read_command *read,
+ u8 num_mode_clocks,
+ u8 num_wait_states,
+ u8 opcode,
+ enum spi_nor_protocol proto)
{
+ read->num_mode_clocks = num_mode_clocks;
+ read->num_wait_states = num_wait_states;
+ read->opcode = opcode;
+ read->proto = proto;
+}
+
+static void
+spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
+ u8 opcode,
+ enum spi_nor_protocol proto)
+{
+ pp->opcode = opcode;
+ pp->proto = proto;
+}
+
+static int spi_nor_init_params(struct spi_nor *nor,
+ const struct flash_info *info,
+ struct spi_nor_flash_parameter *params)
+{
+ /* Set legacy flash parameters as default. */
+ memset(params, 0, sizeof(*params));
+
+ /* Set SPI NOR sizes. */
+ params->size = info->sector_size * info->n_sectors;
+ params->page_size = info->page_size;
+
+ /* (Fast) Read settings. */
+ params->hwcaps.mask |= SNOR_HWCAPS_READ;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
+ 0, 0, SPINOR_OP_READ,
+ SNOR_PROTO_1_1_1);
+
+ if (!(info->flags & SPI_NOR_NO_FR)) {
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],
+ 0, 8, SPINOR_OP_READ_FAST,
+ SNOR_PROTO_1_1_1);
+ }
+
+ if (info->flags & SPI_NOR_DUAL_READ) {
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],
+ 0, 8, SPINOR_OP_READ_1_1_2,
+ SNOR_PROTO_1_1_2);
+ }
+
+ if (info->flags & SPI_NOR_QUAD_READ) {
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
+ 0, 8, SPINOR_OP_READ_1_1_4,
+ SNOR_PROTO_1_1_4);
+ }
+
+ /* Page Program settings. */
+ params->hwcaps.mask |= SNOR_HWCAPS_PP;
+ spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
+ SPINOR_OP_PP, SNOR_PROTO_1_1_1);
+
+ /* Select the procedure to set the Quad Enable bit. */
+ if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
+ SNOR_HWCAPS_PP_QUAD)) {
+ switch (JEDEC_MFR(info)) {
+ case SNOR_MFR_MACRONIX:
+ params->quad_enable = macronix_quad_enable;
+ break;
+
+ case SNOR_MFR_MICRON:
+ break;
+
+ default:
+ params->quad_enable = spansion_quad_enable;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int spi_nor_hwcaps2cmd(u32 hwcaps, const int table[][2], size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ if (table[i][0] == (int)hwcaps)
+ return table[i][1];
+
+ return -EINVAL;
+}
+
+static int spi_nor_hwcaps_read2cmd(u32 hwcaps)
+{
+ static const int hwcaps_read2cmd[][2] = {
+ { SNOR_HWCAPS_READ, SNOR_CMD_READ },
+ { SNOR_HWCAPS_READ_FAST, SNOR_CMD_READ_FAST },
+ { SNOR_HWCAPS_READ_1_1_1_DTR, SNOR_CMD_READ_1_1_1_DTR },
+ { SNOR_HWCAPS_READ_1_1_2, SNOR_CMD_READ_1_1_2 },
+ { SNOR_HWCAPS_READ_1_2_2, SNOR_CMD_READ_1_2_2 },
+ { SNOR_HWCAPS_READ_2_2_2, SNOR_CMD_READ_2_2_2 },
+ { SNOR_HWCAPS_READ_1_2_2_DTR, SNOR_CMD_READ_1_2_2_DTR },
+ { SNOR_HWCAPS_READ_1_1_4, SNOR_CMD_READ_1_1_4 },
+ { SNOR_HWCAPS_READ_1_4_4, SNOR_CMD_READ_1_4_4 },
+ { SNOR_HWCAPS_READ_4_4_4, SNOR_CMD_READ_4_4_4 },
+ { SNOR_HWCAPS_READ_1_4_4_DTR, SNOR_CMD_READ_1_4_4_DTR },
+ { SNOR_HWCAPS_READ_1_1_8, SNOR_CMD_READ_1_1_8 },
+ { SNOR_HWCAPS_READ_1_8_8, SNOR_CMD_READ_1_8_8 },
+ { SNOR_HWCAPS_READ_8_8_8, SNOR_CMD_READ_8_8_8 },
+ { SNOR_HWCAPS_READ_1_8_8_DTR, SNOR_CMD_READ_1_8_8_DTR },
+ };
+
+ return spi_nor_hwcaps2cmd(hwcaps, hwcaps_read2cmd,
+ ARRAY_SIZE(hwcaps_read2cmd));
+}
+
+static int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
+{
+ static const int hwcaps_pp2cmd[][2] = {
+ { SNOR_HWCAPS_PP, SNOR_CMD_PP },
+ { SNOR_HWCAPS_PP_1_1_4, SNOR_CMD_PP_1_1_4 },
+ { SNOR_HWCAPS_PP_1_4_4, SNOR_CMD_PP_1_4_4 },
+ { SNOR_HWCAPS_PP_4_4_4, SNOR_CMD_PP_4_4_4 },
+ { SNOR_HWCAPS_PP_1_1_8, SNOR_CMD_PP_1_1_8 },
+ { SNOR_HWCAPS_PP_1_8_8, SNOR_CMD_PP_1_8_8 },
+ { SNOR_HWCAPS_PP_8_8_8, SNOR_CMD_PP_8_8_8 },
+ };
+
+ return spi_nor_hwcaps2cmd(hwcaps, hwcaps_pp2cmd,
+ ARRAY_SIZE(hwcaps_pp2cmd));
+}
+
+static int spi_nor_select_read(struct spi_nor *nor,
+ const struct spi_nor_flash_parameter *params,
+ u32 shared_hwcaps)
+{
+ int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
+ const struct spi_nor_read_command *read;
+
+ if (best_match < 0)
+ return -EINVAL;
+
+ cmd = spi_nor_hwcaps_read2cmd(BIT(best_match));
+ if (cmd < 0)
+ return -EINVAL;
+
+ read = &params->reads[cmd];
+ nor->read_opcode = read->opcode;
+ nor->read_proto = read->proto;
+
+ /*
+ * In the spi-nor framework, we don't need to make the difference
+ * between mode clock cycles and wait state clock cycles.
+ * Indeed, the value of the mode clock cycles is used by a QSPI
+ * flash memory to know whether it should enter or leave its 0-4-4
+ * (Continuous Read / XIP) mode.
+ * eXecution In Place is out of the scope of the mtd sub-system.
+ * Hence we choose to merge both mode and wait state clock cycles
+ * into the so called dummy clock cycles.
+ */
+ nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
+ return 0;
+}
+
+static int spi_nor_select_pp(struct spi_nor *nor,
+ const struct spi_nor_flash_parameter *params,
+ u32 shared_hwcaps)
+{
+ int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
+ const struct spi_nor_pp_command *pp;
+
+ if (best_match < 0)
+ return -EINVAL;
+
+ cmd = spi_nor_hwcaps_pp2cmd(BIT(best_match));
+ if (cmd < 0)
+ return -EINVAL;
+
+ pp = &params->page_programs[cmd];
+ nor->program_opcode = pp->opcode;
+ nor->write_proto = pp->proto;
+ return 0;
+}
+
+static int spi_nor_select_erase(struct spi_nor *nor,
+ const struct flash_info *info)
+{
+ struct mtd_info *mtd = &nor->mtd;
+
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+ /* prefer "small sector" erase if possible */
+ if (info->flags & SECT_4K) {
+ nor->erase_opcode = SPINOR_OP_BE_4K;
+ mtd->erasesize = 4096;
+ } else if (info->flags & SECT_4K_PMC) {
+ nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
+ mtd->erasesize = 4096;
+ } else
+#endif
+ {
+ nor->erase_opcode = SPINOR_OP_SE;
+ mtd->erasesize = info->sector_size;
+ }
+ return 0;
+}
+
+static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
+ const struct spi_nor_flash_parameter *params,
+ const struct spi_nor_hwcaps *hwcaps)
+{
+ u32 ignored_mask, shared_mask;
+ bool enable_quad_io;
+ int err;
+
+ /*
+ * Keep only the hardware capabilities supported by both the SPI
+ * controller and the SPI flash memory.
+ */
+ shared_mask = hwcaps->mask & params->hwcaps.mask;
+
+ /* SPI n-n-n protocols are not supported yet. */
+ ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |
+ SNOR_HWCAPS_READ_4_4_4 |
+ SNOR_HWCAPS_READ_8_8_8 |
+ SNOR_HWCAPS_PP_4_4_4 |
+ SNOR_HWCAPS_PP_8_8_8);
+ if (shared_mask & ignored_mask) {
+ dev_dbg(nor->dev,
+ "SPI n-n-n protocols are not supported yet.\n");
+ shared_mask &= ~ignored_mask;
+ }
+
+ /* Select the (Fast) Read command. */
+ err = spi_nor_select_read(nor, params, shared_mask);
+ if (err) {
+ dev_err(nor->dev,
+ "can't select read settings supported by both the SPI controller and memory.\n");
+ return err;
+ }
+
+ /* Select the Page Program command. */
+ err = spi_nor_select_pp(nor, params, shared_mask);
+ if (err) {
+ dev_err(nor->dev,
+ "can't select write settings supported by both the SPI controller and memory.\n");
+ return err;
+ }
+
+ /* Select the Sector Erase command. */
+ err = spi_nor_select_erase(nor, info);
+ if (err) {
+ dev_err(nor->dev,
+ "can't select erase settings supported by both the SPI controller and memory.\n");
+ return err;
+ }
+
+ /* Enable Quad I/O if needed. */
+ enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||
+ spi_nor_get_protocol_width(nor->write_proto) == 4);
+ if (enable_quad_io && params->quad_enable) {
+ err = params->quad_enable(nor);
+ if (err) {
+ dev_err(nor->dev, "quad mode not supported\n");
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int spi_nor_scan(struct spi_nor *nor, const char *name,
+ const struct spi_nor_hwcaps *hwcaps)
+{
+ struct spi_nor_flash_parameter params;
const struct flash_info *info = NULL;
struct device *dev = nor->dev;
struct mtd_info *mtd = &nor->mtd;
@@ -1549,6 +1858,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (ret)
return ret;
+ /* Reset SPI protocol for all commands. */
+ nor->reg_proto = SNOR_PROTO_1_1_1;
+ nor->read_proto = SNOR_PROTO_1_1_1;
+ nor->write_proto = SNOR_PROTO_1_1_1;
+
if (name)
info = spi_nor_match_id(name);
/* Try to auto-detect if chip name wasn't specified or not found */
@@ -1591,6 +1905,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (info->flags & SPI_S3AN)
nor->flags |= SNOR_F_READY_XSR_RDY;
+ /* Parse the Serial Flash Discoverable Parameters table. */
+ ret = spi_nor_init_params(nor, info, &params);
+ if (ret)
+ return ret;
+
/*
* Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
* with the software protection bits set
@@ -1611,7 +1930,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
mtd->type = MTD_NORFLASH;
mtd->writesize = 1;
mtd->flags = MTD_CAP_NORFLASH;
- mtd->size = info->sector_size * info->n_sectors;
+ mtd->size = params.size;
mtd->_erase = spi_nor_erase;
mtd->_read = spi_nor_read;
@@ -1642,75 +1961,38 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (info->flags & NO_CHIP_ERASE)
nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
-#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
- /* prefer "small sector" erase if possible */
- if (info->flags & SECT_4K) {
- nor->erase_opcode = SPINOR_OP_BE_4K;
- mtd->erasesize = 4096;
- } else if (info->flags & SECT_4K_PMC) {
- nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
- mtd->erasesize = 4096;
- } else
-#endif
- {
- nor->erase_opcode = SPINOR_OP_SE;
- mtd->erasesize = info->sector_size;
- }
-
if (info->flags & SPI_NOR_NO_ERASE)
mtd->flags |= MTD_NO_ERASE;
mtd->dev.parent = dev;
- nor->page_size = info->page_size;
+ nor->page_size = params.page_size;
mtd->writebufsize = nor->page_size;
if (np) {
/* If we were instantiated by DT, use it */
if (of_property_read_bool(np, "m25p,fast-read"))
- nor->flash_read = SPI_NOR_FAST;
+ params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
else
- nor->flash_read = SPI_NOR_NORMAL;
+ params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
} else {
/* If we weren't instantiated by DT, default to fast-read */
- nor->flash_read = SPI_NOR_FAST;
+ params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
}
/* Some devices cannot do fast-read, no matter what DT tells us */
if (info->flags & SPI_NOR_NO_FR)
- nor->flash_read = SPI_NOR_NORMAL;
-
- /* Quad/Dual-read mode takes precedence over fast/normal */
- if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
- ret = set_quad_mode(nor, info);
- if (ret) {
- dev_err(dev, "quad mode not supported\n");
- return ret;
- }
- nor->flash_read = SPI_NOR_QUAD;
- } else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
- nor->flash_read = SPI_NOR_DUAL;
- }
-
- /* Default commands */
- switch (nor->flash_read) {
- case SPI_NOR_QUAD:
- nor->read_opcode = SPINOR_OP_READ_1_1_4;
- break;
- case SPI_NOR_DUAL:
- nor->read_opcode = SPINOR_OP_READ_1_1_2;
- break;
- case SPI_NOR_FAST:
- nor->read_opcode = SPINOR_OP_READ_FAST;
- break;
- case SPI_NOR_NORMAL:
- nor->read_opcode = SPINOR_OP_READ;
- break;
- default:
- dev_err(dev, "No Read opcode defined\n");
- return -EINVAL;
- }
+ params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
- nor->program_opcode = SPINOR_OP_PP;
+ /*
+ * Configure the SPI memory:
+ * - select op codes for (Fast) Read, Page Program and Sector Erase.
+ * - set the number of dummy cycles (mode cycles + wait states).
+ * - set the SPI protocols for register and memory accesses.
+ * - set the Quad Enable bit if needed (required by SPI x-y-4 protos).
+ */
+ ret = spi_nor_setup(nor, info, &params, hwcaps);
+ if (ret)
+ return ret;
if (info->addr_width)
nor->addr_width = info->addr_width;
@@ -1732,8 +2014,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
return -EINVAL;
}
- nor->read_dummy = spi_nor_read_dummy_cycles(nor);
-
if (info->flags & SPI_S3AN) {
ret = s3an_nor_scan(info, nor);
if (ret)
diff --git a/drivers/mtd/spi-nor/stm32-quadspi.c b/drivers/mtd/spi-nor/stm32-quadspi.c
index ae45f81b8cd3..86c0931543c5 100644
--- a/drivers/mtd/spi-nor/stm32-quadspi.c
+++ b/drivers/mtd/spi-nor/stm32-quadspi.c
@@ -19,6 +19,7 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
+#include <linux/sizes.h>
#define QUADSPI_CR 0x00
#define CR_EN BIT(0)
@@ -192,15 +193,15 @@ static void stm32_qspi_set_framemode(struct spi_nor *nor,
cmd->framemode = CCR_IMODE_1;
if (read) {
- switch (nor->flash_read) {
- case SPI_NOR_NORMAL:
- case SPI_NOR_FAST:
+ switch (nor->read_proto) {
+ default:
+ case SNOR_PROTO_1_1_1:
dmode = CCR_DMODE_1;
break;
- case SPI_NOR_DUAL:
+ case SNOR_PROTO_1_1_2:
dmode = CCR_DMODE_2;
break;
- case SPI_NOR_QUAD:
+ case SNOR_PROTO_1_1_4:
dmode = CCR_DMODE_4;
break;
}
@@ -375,7 +376,7 @@ static ssize_t stm32_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
struct stm32_qspi_cmd cmd;
int err;
- dev_dbg(qspi->dev, "read(%#.2x): buf:%p from:%#.8x len:%#x\n",
+ dev_dbg(qspi->dev, "read(%#.2x): buf:%p from:%#.8x len:%#zx\n",
nor->read_opcode, buf, (u32)from, len);
memset(&cmd, 0, sizeof(cmd));
@@ -402,7 +403,7 @@ static ssize_t stm32_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
struct stm32_qspi_cmd cmd;
int err;
- dev_dbg(dev, "write(%#.2x): buf:%p to:%#.8x len:%#x\n",
+ dev_dbg(dev, "write(%#.2x): buf:%p to:%#.8x len:%#zx\n",
nor->program_opcode, buf, (u32)to, len);
memset(&cmd, 0, sizeof(cmd));
@@ -480,7 +481,12 @@ static void stm32_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
static int stm32_qspi_flash_setup(struct stm32_qspi *qspi,
struct device_node *np)
{
- u32 width, flash_read, presc, cs_num, max_rate = 0;
+ struct spi_nor_hwcaps hwcaps = {
+ .mask = SNOR_HWCAPS_READ |
+ SNOR_HWCAPS_READ_FAST |
+ SNOR_HWCAPS_PP,
+ };
+ u32 width, presc, cs_num, max_rate = 0;
struct stm32_qspi_flash *flash;
struct mtd_info *mtd;
int ret;
@@ -499,12 +505,10 @@ static int stm32_qspi_flash_setup(struct stm32_qspi *qspi,
width = 1;
if (width == 4)
- flash_read = SPI_NOR_QUAD;
+ hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
else if (width == 2)
- flash_read = SPI_NOR_DUAL;
- else if (width == 1)
- flash_read = SPI_NOR_NORMAL;
- else
+ hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
+ else if (width != 1)
return -EINVAL;
flash = &qspi->flash[cs_num];
@@ -539,7 +543,7 @@ static int stm32_qspi_flash_setup(struct stm32_qspi *qspi,
*/
flash->fsize = FSIZE_VAL(SZ_1K);
- ret = spi_nor_scan(&flash->nor, NULL, flash_read);
+ ret = spi_nor_scan(&flash->nor, NULL, &hwcaps);
if (ret) {
dev_err(qspi->dev, "device scan failed\n");
return ret;
diff --git a/drivers/mtd/tests/subpagetest.c b/drivers/mtd/tests/subpagetest.c
index aecc6ce5a9e1..fa2519ad2435 100644
--- a/drivers/mtd/tests/subpagetest.c
+++ b/drivers/mtd/tests/subpagetest.c
@@ -102,7 +102,7 @@ static int write_eraseblock2(int ebnum)
if (unlikely(err || written != subpgsize * k)) {
pr_err("error: write failed at %#llx\n",
(long long)addr);
- if (written != subpgsize) {
+ if (written != subpgsize * k) {
pr_err(" write size: %#x\n",
subpgsize * k);
pr_err(" written: %#08zx\n",
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index a19f68f5862d..e7c8539cbddf 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -3458,13 +3458,18 @@ static int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp)
req.ver_upd = DRV_VER_UPD;
if (BNXT_PF(bp)) {
- DECLARE_BITMAP(vf_req_snif_bmap, 256);
- u32 *data = (u32 *)vf_req_snif_bmap;
+ u32 data[8];
int i;
- memset(vf_req_snif_bmap, 0, sizeof(vf_req_snif_bmap));
- for (i = 0; i < ARRAY_SIZE(bnxt_vf_req_snif); i++)
- __set_bit(bnxt_vf_req_snif[i], vf_req_snif_bmap);
+ memset(data, 0, sizeof(data));
+ for (i = 0; i < ARRAY_SIZE(bnxt_vf_req_snif); i++) {
+ u16 cmd = bnxt_vf_req_snif[i];
+ unsigned int bit, idx;
+
+ idx = cmd / 32;
+ bit = cmd % 32;
+ data[idx] |= 1 << bit;
+ }
for (i = 0; i < 8; i++)
req.vf_req_fwd[i] = cpu_to_le32(data[i]);
@@ -6279,6 +6284,12 @@ static int bnxt_open(struct net_device *dev)
return __bnxt_open_nic(bp, true, true);
}
+static bool bnxt_drv_busy(struct bnxt *bp)
+{
+ return (test_bit(BNXT_STATE_IN_SP_TASK, &bp->state) ||
+ test_bit(BNXT_STATE_READ_STATS, &bp->state));
+}
+
int bnxt_close_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init)
{
int rc = 0;
@@ -6297,7 +6308,7 @@ int bnxt_close_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init)
clear_bit(BNXT_STATE_OPEN, &bp->state);
smp_mb__after_atomic();
- while (test_bit(BNXT_STATE_IN_SP_TASK, &bp->state))
+ while (bnxt_drv_busy(bp))
msleep(20);
/* Flush rings and and disable interrupts */
@@ -6358,8 +6369,15 @@ bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
u32 i;
struct bnxt *bp = netdev_priv(dev);
- if (!bp->bnapi)
+ set_bit(BNXT_STATE_READ_STATS, &bp->state);
+ /* Make sure bnxt_close_nic() sees that we are reading stats before
+ * we check the BNXT_STATE_OPEN flag.
+ */
+ smp_mb__after_atomic();
+ if (!test_bit(BNXT_STATE_OPEN, &bp->state)) {
+ clear_bit(BNXT_STATE_READ_STATS, &bp->state);
return;
+ }
/* TODO check if we need to synchronize with bnxt_close path */
for (i = 0; i < bp->cp_nr_rings; i++) {
@@ -6406,6 +6424,7 @@ bnxt_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
stats->tx_fifo_errors = le64_to_cpu(tx->tx_fifo_underruns);
stats->tx_errors = le64_to_cpu(tx->tx_err);
}
+ clear_bit(BNXT_STATE_READ_STATS, &bp->state);
}
static bool bnxt_mc_list_updated(struct bnxt *bp, u32 *rx_mask)
@@ -6904,16 +6923,13 @@ static void bnxt_sp_task(struct work_struct *work)
}
/* Under rtnl_lock */
-int bnxt_reserve_rings(struct bnxt *bp, int tx, int rx, int tcs, int tx_xdp)
+int bnxt_reserve_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs,
+ int tx_xdp)
{
int max_rx, max_tx, tx_sets = 1;
int tx_rings_needed;
- bool sh = true;
int rc;
- if (!(bp->flags & BNXT_FLAG_SHARED_RINGS))
- sh = false;
-
if (tcs)
tx_sets = tcs;
@@ -7121,7 +7137,7 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc)
sh = true;
rc = bnxt_reserve_rings(bp, bp->tx_nr_rings_per_tc, bp->rx_nr_rings,
- tc, bp->tx_nr_rings_xdp);
+ sh, tc, bp->tx_nr_rings_xdp);
if (rc)
return rc;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index f872a7db2ca8..f34691f85602 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -1117,6 +1117,7 @@ struct bnxt {
unsigned long state;
#define BNXT_STATE_OPEN 0
#define BNXT_STATE_IN_SP_TASK 1
+#define BNXT_STATE_READ_STATS 2
struct bnxt_irq *irq_tbl;
int total_irqs;
@@ -1300,7 +1301,8 @@ int bnxt_open_nic(struct bnxt *, bool, bool);
int bnxt_half_open_nic(struct bnxt *bp);
void bnxt_half_close_nic(struct bnxt *bp);
int bnxt_close_nic(struct bnxt *, bool, bool);
-int bnxt_reserve_rings(struct bnxt *bp, int tx, int rx, int tcs, int tx_xdp);
+int bnxt_reserve_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs,
+ int tx_xdp);
int bnxt_setup_mq_tc(struct net_device *dev, u8 tc);
int bnxt_get_max_rings(struct bnxt *, int *, int *, bool);
void bnxt_restore_pf_fw_resources(struct bnxt *bp);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
index fd1181510b65..be6acadcb202 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
@@ -432,7 +432,8 @@ static int bnxt_set_channels(struct net_device *dev,
}
tx_xdp = req_rx_rings;
}
- rc = bnxt_reserve_rings(bp, req_tx_rings, req_rx_rings, tcs, tx_xdp);
+ rc = bnxt_reserve_rings(bp, req_tx_rings, req_rx_rings, sh, tcs,
+ tx_xdp);
if (rc) {
netdev_warn(dev, "Unable to allocate the requested rings\n");
return rc;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
index 7d67552e70d7..3961a6807454 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
@@ -170,7 +170,7 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog)
if (!tc)
tc = 1;
rc = bnxt_reserve_rings(bp, bp->tx_nr_rings_per_tc, bp->rx_nr_rings,
- tc, tx_xdp);
+ true, tc, tx_xdp);
if (rc) {
netdev_warn(dev, "Unable to reserve enough TX rings to support XDP.\n");
return rc;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 86f92e31e8aa..e403fa18f1b1 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -2083,12 +2083,12 @@ static void detach_ulds(struct adapter *adap)
mutex_lock(&uld_mutex);
list_del(&adap->list_node);
+
for (i = 0; i < CXGB4_ULD_MAX; i++)
- if (adap->uld && adap->uld[i].handle) {
+ if (adap->uld && adap->uld[i].handle)
adap->uld[i].state_change(adap->uld[i].handle,
CXGB4_STATE_DETACH);
- adap->uld[i].handle = NULL;
- }
+
if (netevent_registered && list_empty(&adapter_list)) {
unregister_netevent_notifier(&cxgb4_netevent_nb);
netevent_registered = false;
@@ -5303,8 +5303,10 @@ static void remove_one(struct pci_dev *pdev)
*/
destroy_workqueue(adapter->workq);
- if (is_uld(adapter))
+ if (is_uld(adapter)) {
detach_ulds(adapter);
+ t4_uld_clean_up(adapter);
+ }
disable_interrupts(adapter);
@@ -5385,7 +5387,11 @@ static void shutdown_one(struct pci_dev *pdev)
if (adapter->port[i]->reg_state == NETREG_REGISTERED)
cxgb_close(adapter->port[i]);
- t4_uld_clean_up(adapter);
+ if (is_uld(adapter)) {
+ detach_ulds(adapter);
+ t4_uld_clean_up(adapter);
+ }
+
disable_interrupts(adapter);
disable_msi(adapter);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c
index ec53fe9dec68..71a315bc1409 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.c
@@ -589,22 +589,37 @@ void t4_uld_mem_free(struct adapter *adap)
kfree(adap->uld);
}
+/* This function should be called with uld_mutex taken. */
+static void cxgb4_shutdown_uld_adapter(struct adapter *adap, enum cxgb4_uld type)
+{
+ if (adap->uld[type].handle) {
+ adap->uld[type].handle = NULL;
+ adap->uld[type].add = NULL;
+ release_sge_txq_uld(adap, type);
+
+ if (adap->flags & FULL_INIT_DONE)
+ quiesce_rx_uld(adap, type);
+
+ if (adap->flags & USING_MSIX)
+ free_msix_queue_irqs_uld(adap, type);
+
+ free_sge_queues_uld(adap, type);
+ free_queues_uld(adap, type);
+ }
+}
+
void t4_uld_clean_up(struct adapter *adap)
{
unsigned int i;
- if (!adap->uld)
- return;
+ mutex_lock(&uld_mutex);
for (i = 0; i < CXGB4_ULD_MAX; i++) {
if (!adap->uld[i].handle)
continue;
- if (adap->flags & FULL_INIT_DONE)
- quiesce_rx_uld(adap, i);
- if (adap->flags & USING_MSIX)
- free_msix_queue_irqs_uld(adap, i);
- free_sge_queues_uld(adap, i);
- free_queues_uld(adap, i);
+
+ cxgb4_shutdown_uld_adapter(adap, i);
}
+ mutex_unlock(&uld_mutex);
}
static void uld_init(struct adapter *adap, struct cxgb4_lld_info *lld)
@@ -783,15 +798,8 @@ int cxgb4_unregister_uld(enum cxgb4_uld type)
continue;
if (type == CXGB4_ULD_ISCSIT && is_t4(adap->params.chip))
continue;
- adap->uld[type].handle = NULL;
- adap->uld[type].add = NULL;
- release_sge_txq_uld(adap, type);
- if (adap->flags & FULL_INIT_DONE)
- quiesce_rx_uld(adap, type);
- if (adap->flags & USING_MSIX)
- free_msix_queue_irqs_uld(adap, type);
- free_sge_queues_uld(adap, type);
- free_queues_uld(adap, type);
+
+ cxgb4_shutdown_uld_adapter(adap, type);
}
mutex_unlock(&uld_mutex);
diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.c b/drivers/net/ethernet/cisco/enic/vnic_dev.c
index 1841ad45d215..39bad67422dd 100644
--- a/drivers/net/ethernet/cisco/enic/vnic_dev.c
+++ b/drivers/net/ethernet/cisco/enic/vnic_dev.c
@@ -402,8 +402,8 @@ static int vnic_dev_init_devcmd2(struct vnic_dev *vdev)
fetch_index = ioread32(&vdev->devcmd2->wq.ctrl->fetch_index);
if (fetch_index == 0xFFFFFFFF) { /* check for hardware gone */
vdev_err(vdev, "Fatal error in devcmd2 init - hardware surprise removal\n");
-
- return -ENODEV;
+ err = -ENODEV;
+ goto err_free_wq;
}
enic_wq_init_start(&vdev->devcmd2->wq, 0, fetch_index, fetch_index, 0,
@@ -414,7 +414,7 @@ static int vnic_dev_init_devcmd2(struct vnic_dev *vdev)
err = vnic_dev_alloc_desc_ring(vdev, &vdev->devcmd2->results_ring,
DEVCMD2_RING_SIZE, DEVCMD2_DESC_SIZE);
if (err)
- goto err_free_wq;
+ goto err_disable_wq;
vdev->devcmd2->result = vdev->devcmd2->results_ring.descs;
vdev->devcmd2->cmd_ring = vdev->devcmd2->wq.ring.descs;
@@ -433,8 +433,9 @@ static int vnic_dev_init_devcmd2(struct vnic_dev *vdev)
err_free_desc_ring:
vnic_dev_free_desc_ring(vdev, &vdev->devcmd2->results_ring);
-err_free_wq:
+err_disable_wq:
vnic_wq_disable(&vdev->devcmd2->wq);
+err_free_wq:
vnic_wq_free(&vdev->devcmd2->wq);
err_free_devcmd2:
kfree(vdev->devcmd2);
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
index fe166e0f6781..3987699f8fe6 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
@@ -1378,13 +1378,20 @@ void hns_nic_net_reset(struct net_device *ndev)
void hns_nic_net_reinit(struct net_device *netdev)
{
struct hns_nic_priv *priv = netdev_priv(netdev);
+ enum hnae_port_type type = priv->ae_handle->port_type;
netif_trans_update(priv->netdev);
while (test_and_set_bit(NIC_STATE_REINITING, &priv->state))
usleep_range(1000, 2000);
hns_nic_net_down(netdev);
- hns_nic_net_reset(netdev);
+
+ /* Only do hns_nic_net_reset in debug mode
+ * because of hardware limitation.
+ */
+ if (type == HNAE_PORT_DEBUG)
+ hns_nic_net_reset(netdev);
+
(void)hns_nic_net_up(netdev);
clear_bit(NIC_STATE_REINITING, &priv->state);
}
@@ -1997,13 +2004,8 @@ static void hns_nic_reset_subtask(struct hns_nic_priv *priv)
rtnl_lock();
/* put off any impending NetWatchDogTimeout */
netif_trans_update(priv->netdev);
+ hns_nic_net_reinit(priv->netdev);
- if (type == HNAE_PORT_DEBUG) {
- hns_nic_net_reinit(priv->netdev);
- } else {
- netif_carrier_off(priv->netdev);
- netif_tx_disable(priv->netdev);
- }
rtnl_unlock();
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
index ca367445f864..9d17e4e76d3a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
@@ -4,14 +4,14 @@ subdir-ccflags-y += -I$(src)
mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \
health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.o \
mad.o transobj.o vport.o sriov.o fs_cmd.o fs_core.o \
- fs_counters.o rl.o lag.o dev.o lib/gid.o
+ fs_counters.o rl.o lag.o dev.o wq.o lib/gid.o
mlx5_core-$(CONFIG_MLX5_ACCEL) += accel/ipsec.o
mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o \
fpga/ipsec.o
-mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o eswitch.o eswitch_offloads.o \
+mlx5_core-$(CONFIG_MLX5_CORE_EN) += eswitch.o eswitch_offloads.o \
en_main.o en_common.o en_fs.o en_ethtool.o en_tx.o \
en_rx.o en_rx_am.o en_txrx.o en_clock.o vxlan.o \
en_tc.o en_arfs.o en_rep.o en_fs_ethtool.o en_selftest.o
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/accel/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/accel/Makefile
new file mode 100644
index 000000000000..d8e17110f25d
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/accel/Makefile
@@ -0,0 +1 @@
+subdir-ccflags-y += -I$(src)/..
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/Makefile
new file mode 100644
index 000000000000..d8e17110f25d
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/Makefile
@@ -0,0 +1 @@
+subdir-ccflags-y += -I$(src)/..
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c
index 4a78aefdf157..4614ddfa91eb 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c
@@ -372,7 +372,7 @@ void mlx5e_ipsec_build_inverse_table(void)
*/
mlx5e_ipsec_inverse_table[1] = htons(0xFFFF);
for (mss = 2; mss < MAX_LSO_MSS; mss++) {
- mss_inv = ((1ULL << 32) / mss) >> 16;
+ mss_inv = div_u64(1ULL << 32, mss) >> 16;
mlx5e_ipsec_inverse_table[mss] = htons(mss_inv);
}
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c
index 8fa23f6a1f67..2eb54d36e16e 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c
@@ -464,6 +464,8 @@ static void mlx5e_dcbnl_getpermhwaddr(struct net_device *netdev,
if (!perm_addr)
return;
+ memset(perm_addr, 0xff, MAX_ADDR_LEN);
+
mlx5_query_nic_vport_mac_address(priv->mdev, 0, perm_addr);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/fpga/Makefile
new file mode 100644
index 000000000000..d8e17110f25d
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/Makefile
@@ -0,0 +1 @@
+subdir-ccflags-y += -I$(src)/..
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c b/drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c
index 31e5a2627eb8..9034e9960a76 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c
@@ -102,7 +102,7 @@ static int mlx5_fpga_device_load_check(struct mlx5_fpga_device *fdev)
return 0;
}
-int mlx5_fpga_device_brb(struct mlx5_fpga_device *fdev)
+static int mlx5_fpga_device_brb(struct mlx5_fpga_device *fdev)
{
int err;
struct mlx5_core_dev *mdev = fdev->mdev;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c
index 42970e2a05ff..35d0e33381ca 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c
@@ -275,7 +275,7 @@ int mlx5_fpga_ipsec_counters_read(struct mlx5_core_dev *mdev, u64 *counters,
{
struct mlx5_fpga_device *fdev = mdev->fpga;
unsigned int i;
- u32 *data;
+ __be32 *data;
u32 count;
u64 addr;
int ret;
@@ -290,7 +290,7 @@ int mlx5_fpga_ipsec_counters_read(struct mlx5_core_dev *mdev, u64 *counters,
count = mlx5_fpga_ipsec_counters_count(mdev);
- data = kzalloc(sizeof(u32) * count * 2, GFP_KERNEL);
+ data = kzalloc(sizeof(*data) * count * 2, GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto out;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/Makefile
new file mode 100644
index 000000000000..d8e17110f25d
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/Makefile
@@ -0,0 +1 @@
+subdir-ccflags-y += -I$(src)/..
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/lib/Makefile
new file mode 100644
index 000000000000..d8e17110f25d
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/Makefile
@@ -0,0 +1 @@
+subdir-ccflags-y += -I$(src)/..
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/gid.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/gid.c
index de2aed44ab85..573f59f46d41 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/gid.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/gid.c
@@ -34,6 +34,7 @@
#include <linux/etherdevice.h>
#include <linux/idr.h>
#include "mlx5_core.h"
+#include "lib/mlx5.h"
void mlx5_init_reserved_gids(struct mlx5_core_dev *dev)
{
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index 192cb93e7669..383fef5a8e24 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -1790,6 +1790,7 @@ static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp,
return 0;
err_nexthop_neigh_init:
+ mlxsw_sp_nexthop_rif_fini(nh);
mlxsw_sp_nexthop_remove(mlxsw_sp, nh);
return err;
}
@@ -1866,6 +1867,7 @@ mlxsw_sp_nexthop_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi)
nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK;
nh_grp->count = fi->fib_nhs;
nh_grp->key.fi = fi;
+ fib_info_hold(fi);
for (i = 0; i < nh_grp->count; i++) {
nh = &nh_grp->nexthops[i];
fib_nh = &fi->fib_nh[i];
@@ -1885,6 +1887,7 @@ err_nexthop_init:
nh = &nh_grp->nexthops[i];
mlxsw_sp_nexthop_fini(mlxsw_sp, nh);
}
+ fib_info_put(nh_grp->key.fi);
kfree(nh_grp);
return ERR_PTR(err);
}
@@ -1903,6 +1906,7 @@ mlxsw_sp_nexthop_group_destroy(struct mlxsw_sp *mlxsw_sp,
}
mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp);
WARN_ON_ONCE(nh_grp->adj_index_valid);
+ fib_info_put(nh_grp->key.fi);
kfree(nh_grp);
}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index cd89a3e6cd81..656b2d3f1bee 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -979,7 +979,6 @@ mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port,
{
u16 pvid = mlxsw_sp_port_pvid_determine(mlxsw_sp_port, vid, is_pvid);
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
- struct mlxsw_sp_bridge_vlan *bridge_vlan;
u16 old_pvid = mlxsw_sp_port->pvid;
int err;
@@ -1000,8 +999,6 @@ mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port,
if (err)
goto err_port_vlan_bridge_join;
- bridge_vlan = mlxsw_sp_bridge_vlan_find(bridge_port, vid);
-
return 0;
err_port_vlan_bridge_join:
@@ -1919,6 +1916,8 @@ static int mlxsw_sp_switchdev_event(struct notifier_block *unused,
memcpy(&switchdev_work->fdb_info, ptr,
sizeof(switchdev_work->fdb_info));
switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
+ if (!switchdev_work->fdb_info.addr)
+ goto err_addr_alloc;
ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
fdb_info->addr);
/* Take a reference on the device. This can be either
@@ -1935,6 +1934,10 @@ static int mlxsw_sp_switchdev_event(struct notifier_block *unused,
mlxsw_core_schedule_work(&switchdev_work->work);
return NOTIFY_DONE;
+
+err_addr_alloc:
+ kfree(switchdev_work);
+ return NOTIFY_BAD;
}
static struct notifier_block mlxsw_sp_switchdev_notifier = {
diff --git a/drivers/net/ethernet/netronome/nfp/flower/metadata.c b/drivers/net/ethernet/netronome/nfp/flower/metadata.c
index fec0ff2ca94f..3226ddc55f99 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/metadata.c
+++ b/drivers/net/ethernet/netronome/nfp/flower/metadata.c
@@ -419,7 +419,7 @@ int nfp_flower_metadata_init(struct nfp_app *app)
return 0;
err_free_last_used:
- kfree(priv->stats_ids.free_list.buf);
+ kfree(priv->mask_ids.last_used);
err_free_mask_id:
kfree(priv->mask_ids.mask_id_free_list.buf);
return -ENOMEM;
diff --git a/drivers/net/ethernet/qlogic/qed/qed_iwarp.c b/drivers/net/ethernet/qlogic/qed/qed_iwarp.c
index b251ebaec4db..9d989c96278c 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_iwarp.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_iwarp.c
@@ -575,7 +575,7 @@ qed_iwarp_print_tcp_ramrod(struct qed_hwfn *p_hwfn,
if (p_tcp_ramrod->tcp.ip_version == TCP_IPV4) {
DP_VERBOSE(p_hwfn, QED_MSG_RDMA,
- "local_ip=%pI4h:%x, remote_ip=%pI4h%x, vlan=%x\n",
+ "local_ip=%pI4h:%x, remote_ip=%pI4h:%x, vlan=%x\n",
p_tcp_ramrod->tcp.local_ip,
p_tcp_ramrod->tcp.local_port,
p_tcp_ramrod->tcp.remote_ip,
@@ -583,7 +583,7 @@ qed_iwarp_print_tcp_ramrod(struct qed_hwfn *p_hwfn,
p_tcp_ramrod->tcp.vlan_id);
} else {
DP_VERBOSE(p_hwfn, QED_MSG_RDMA,
- "local_ip=%pI6h:%x, remote_ip=%pI6h:%x, vlan=%x\n",
+ "local_ip=%pI6:%x, remote_ip=%pI6:%x, vlan=%x\n",
p_tcp_ramrod->tcp.local_ip,
p_tcp_ramrod->tcp.local_port,
p_tcp_ramrod->tcp.remote_ip,
@@ -1519,7 +1519,7 @@ qed_iwarp_print_cm_info(struct qed_hwfn *p_hwfn,
cm_info->vlan);
else
DP_VERBOSE(p_hwfn, QED_MSG_RDMA,
- "remote_ip %pI6h:%x, local_ip %pI6h:%x vlan=%x\n",
+ "remote_ip %pI6:%x, local_ip %pI6:%x vlan=%x\n",
cm_info->remote_ip, cm_info->remote_port,
cm_info->local_ip, cm_info->local_port,
cm_info->vlan);
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
index 761c518b2f92..13f72f5b18d2 100644
--- a/drivers/net/ethernet/sfc/ef10.c
+++ b/drivers/net/ethernet/sfc/ef10.c
@@ -5034,12 +5034,9 @@ static void efx_ef10_filter_uc_addr_list(struct efx_nic *efx)
struct efx_ef10_filter_table *table = efx->filter_state;
struct net_device *net_dev = efx->net_dev;
struct netdev_hw_addr *uc;
- int addr_count;
unsigned int i;
- addr_count = netdev_uc_count(net_dev);
table->uc_promisc = !!(net_dev->flags & IFF_PROMISC);
- table->dev_uc_count = 1 + addr_count;
ether_addr_copy(table->dev_uc_list[0].addr, net_dev->dev_addr);
i = 1;
netdev_for_each_uc_addr(uc, net_dev) {
@@ -5050,6 +5047,8 @@ static void efx_ef10_filter_uc_addr_list(struct efx_nic *efx)
ether_addr_copy(table->dev_uc_list[i].addr, uc->addr);
i++;
}
+
+ table->dev_uc_count = i;
}
static void efx_ef10_filter_mc_addr_list(struct efx_nic *efx)
@@ -5057,12 +5056,11 @@ static void efx_ef10_filter_mc_addr_list(struct efx_nic *efx)
struct efx_ef10_filter_table *table = efx->filter_state;
struct net_device *net_dev = efx->net_dev;
struct netdev_hw_addr *mc;
- unsigned int i, addr_count;
+ unsigned int i;
table->mc_overflow = false;
table->mc_promisc = !!(net_dev->flags & (IFF_PROMISC | IFF_ALLMULTI));
- addr_count = netdev_mc_count(net_dev);
i = 0;
netdev_for_each_mc_addr(mc, net_dev) {
if (i >= EFX_EF10_FILTER_DEV_MC_MAX) {
diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c
index ea1bbc355b4d..0b6a39b003a4 100644
--- a/drivers/net/ethernet/smsc/smsc911x.c
+++ b/drivers/net/ethernet/smsc/smsc911x.c
@@ -2467,6 +2467,10 @@ static int smsc911x_drv_probe(struct platform_device *pdev)
pdata = netdev_priv(dev);
dev->irq = irq;
pdata->ioaddr = ioremap_nocache(res->start, res_size);
+ if (!pdata->ioaddr) {
+ retval = -ENOMEM;
+ goto out_ioremap_fail;
+ }
pdata->dev = dev;
pdata->msg_enable = ((1 << debug) - 1);
@@ -2572,6 +2576,7 @@ out_enable_resources_fail:
smsc911x_free_resources(pdev);
out_request_resources_fail:
iounmap(pdata->ioaddr);
+out_ioremap_fail:
free_netdev(dev);
out_release_io_1:
release_mem_region(res->start, resource_size(res));
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index 6c2d1da05588..fffd6d5fc907 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -638,7 +638,7 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
{
struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
struct device_node *node = priv->device->of_node;
- int ret, phy_interface;
+ int ret;
u32 reg, val;
regmap_read(gmac->regmap, SYSCON_EMAC_REG, &val);
@@ -718,11 +718,7 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
if (gmac->variant->support_rmii)
reg &= ~SYSCON_RMII_EN;
- phy_interface = priv->plat->interface;
- /* if PHY is internal, select the mode (xMII) used by the SoC */
- if (gmac->use_internal_phy)
- phy_interface = gmac->variant->internal_phy;
- switch (phy_interface) {
+ switch (priv->plat->interface) {
case PHY_INTERFACE_MODE_MII:
/* default */
break;
@@ -936,7 +932,7 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
}
plat_dat->interface = of_get_phy_mode(dev->of_node);
- if (plat_dat->interface == PHY_INTERFACE_MODE_INTERNAL) {
+ if (plat_dat->interface == gmac->variant->internal_phy) {
dev_info(&pdev->dev, "Will use internal PHY\n");
gmac->use_internal_phy = true;
gmac->ephy_clk = of_clk_get(plat_dat->phy_node, 0);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 19bba6281dab..1853f7ff6657 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1449,7 +1449,7 @@ static void free_dma_rx_desc_resources(struct stmmac_priv *priv)
static void free_dma_tx_desc_resources(struct stmmac_priv *priv)
{
u32 tx_count = priv->plat->tx_queues_to_use;
- u32 queue = 0;
+ u32 queue;
/* Free TX queue resources */
for (queue = 0; queue < tx_count; queue++) {
@@ -1498,7 +1498,7 @@ static int alloc_dma_rx_desc_resources(struct stmmac_priv *priv)
sizeof(dma_addr_t),
GFP_KERNEL);
if (!rx_q->rx_skbuff_dma)
- return -ENOMEM;
+ goto err_dma;
rx_q->rx_skbuff = kmalloc_array(DMA_RX_SIZE,
sizeof(struct sk_buff *),
@@ -1561,13 +1561,13 @@ static int alloc_dma_tx_desc_resources(struct stmmac_priv *priv)
sizeof(*tx_q->tx_skbuff_dma),
GFP_KERNEL);
if (!tx_q->tx_skbuff_dma)
- return -ENOMEM;
+ goto err_dma;
tx_q->tx_skbuff = kmalloc_array(DMA_TX_SIZE,
sizeof(struct sk_buff *),
GFP_KERNEL);
if (!tx_q->tx_skbuff)
- goto err_dma_buffers;
+ goto err_dma;
if (priv->extend_desc) {
tx_q->dma_etx = dma_zalloc_coherent(priv->device,
@@ -1577,7 +1577,7 @@ static int alloc_dma_tx_desc_resources(struct stmmac_priv *priv)
&tx_q->dma_tx_phy,
GFP_KERNEL);
if (!tx_q->dma_etx)
- goto err_dma_buffers;
+ goto err_dma;
} else {
tx_q->dma_tx = dma_zalloc_coherent(priv->device,
DMA_TX_SIZE *
@@ -1586,13 +1586,13 @@ static int alloc_dma_tx_desc_resources(struct stmmac_priv *priv)
&tx_q->dma_tx_phy,
GFP_KERNEL);
if (!tx_q->dma_tx)
- goto err_dma_buffers;
+ goto err_dma;
}
}
return 0;
-err_dma_buffers:
+err_dma:
free_dma_tx_desc_resources(priv);
return ret;
diff --git a/drivers/net/tap.c b/drivers/net/tap.c
index 9af3239d6ad5..3570c7576993 100644
--- a/drivers/net/tap.c
+++ b/drivers/net/tap.c
@@ -106,7 +106,7 @@ struct major_info {
struct rcu_head rcu;
dev_t major;
struct idr minor_idr;
- struct mutex minor_lock;
+ spinlock_t minor_lock;
const char *device_name;
struct list_head next;
};
@@ -416,15 +416,15 @@ int tap_get_minor(dev_t major, struct tap_dev *tap)
goto unlock;
}
- mutex_lock(&tap_major->minor_lock);
- retval = idr_alloc(&tap_major->minor_idr, tap, 1, TAP_NUM_DEVS, GFP_KERNEL);
+ spin_lock(&tap_major->minor_lock);
+ retval = idr_alloc(&tap_major->minor_idr, tap, 1, TAP_NUM_DEVS, GFP_ATOMIC);
if (retval >= 0) {
tap->minor = retval;
} else if (retval == -ENOSPC) {
netdev_err(tap->dev, "Too many tap devices\n");
retval = -EINVAL;
}
- mutex_unlock(&tap_major->minor_lock);
+ spin_unlock(&tap_major->minor_lock);
unlock:
rcu_read_unlock();
@@ -442,12 +442,12 @@ void tap_free_minor(dev_t major, struct tap_dev *tap)
goto unlock;
}
- mutex_lock(&tap_major->minor_lock);
+ spin_lock(&tap_major->minor_lock);
if (tap->minor) {
idr_remove(&tap_major->minor_idr, tap->minor);
tap->minor = 0;
}
- mutex_unlock(&tap_major->minor_lock);
+ spin_unlock(&tap_major->minor_lock);
unlock:
rcu_read_unlock();
@@ -467,13 +467,13 @@ static struct tap_dev *dev_get_by_tap_file(int major, int minor)
goto unlock;
}
- mutex_lock(&tap_major->minor_lock);
+ spin_lock(&tap_major->minor_lock);
tap = idr_find(&tap_major->minor_idr, minor);
if (tap) {
dev = tap->dev;
dev_hold(dev);
}
- mutex_unlock(&tap_major->minor_lock);
+ spin_unlock(&tap_major->minor_lock);
unlock:
rcu_read_unlock();
@@ -1244,7 +1244,7 @@ static int tap_list_add(dev_t major, const char *device_name)
tap_major->major = MAJOR(major);
idr_init(&tap_major->minor_idr);
- mutex_init(&tap_major->minor_lock);
+ spin_lock_init(&tap_major->minor_lock);
tap_major->device_name = device_name;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index dcde596c9eb9..7e689c86d565 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -4934,6 +4934,11 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
GFP_KERNEL);
} else if (ieee80211_is_action(mgmt->frame_control)) {
+ if (len > BRCMF_FIL_ACTION_FRAME_SIZE + DOT11_MGMT_HDR_LEN) {
+ brcmf_err("invalid action frame length\n");
+ err = -EINVAL;
+ goto exit;
+ }
af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
if (af_params == NULL) {
brcmf_err("unable to allocate frame\n");
diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c
index 9866fec78c1c..0831b428c217 100644
--- a/drivers/platform/x86/alienware-wmi.c
+++ b/drivers/platform/x86/alienware-wmi.c
@@ -604,7 +604,7 @@ static struct attribute *hdmi_attrs[] = {
NULL,
};
-static struct attribute_group hdmi_attribute_group = {
+static const struct attribute_group hdmi_attribute_group = {
.name = "hdmi",
.attrs = hdmi_attrs,
};
@@ -660,7 +660,7 @@ static struct attribute *amplifier_attrs[] = {
NULL,
};
-static struct attribute_group amplifier_attribute_group = {
+static const struct attribute_group amplifier_attribute_group = {
.name = "amplifier",
.attrs = amplifier_attrs,
};
@@ -741,7 +741,7 @@ static struct attribute *deepsleep_attrs[] = {
NULL,
};
-static struct attribute_group deepsleep_attribute_group = {
+static const struct attribute_group deepsleep_attribute_group = {
.name = "deepsleep",
.attrs = deepsleep_attrs,
};
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 6c7d86074b38..709e3a67391a 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -1433,7 +1433,7 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
return ok ? attr->mode : 0;
}
-static struct attribute_group hwmon_attribute_group = {
+static const struct attribute_group hwmon_attribute_group = {
.is_visible = asus_hwmon_sysfs_is_visible,
.attrs = hwmon_attributes
};
@@ -1821,7 +1821,7 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
return ok ? attr->mode : 0;
}
-static struct attribute_group platform_attribute_group = {
+static const struct attribute_group platform_attribute_group = {
.is_visible = asus_sysfs_is_visible,
.attrs = platform_attributes
};
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c
index e1c2b6d4b24a..a8e4a539e704 100644
--- a/drivers/platform/x86/compal-laptop.c
+++ b/drivers/platform/x86/compal-laptop.c
@@ -718,7 +718,7 @@ static struct attribute *compal_platform_attrs[] = {
&dev_attr_wake_up_mouse.attr,
NULL
};
-static struct attribute_group compal_platform_attr_group = {
+static const struct attribute_group compal_platform_attr_group = {
.attrs = compal_platform_attrs
};
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
index c1a852847d02..85de30f93a9c 100644
--- a/drivers/platform/x86/fujitsu-laptop.c
+++ b/drivers/platform/x86/fujitsu-laptop.c
@@ -317,7 +317,7 @@ static struct attribute *fujitsu_pf_attributes[] = {
NULL
};
-static struct attribute_group fujitsu_pf_attribute_group = {
+static const struct attribute_group fujitsu_pf_attribute_group = {
.attrs = fujitsu_pf_attributes
};
@@ -695,6 +695,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
if (call_fext_func(device,
FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
led->name = "fujitsu::logolamp";
led->brightness_set_blocking = logolamp_set;
led->brightness_get = logolamp_get;
@@ -707,6 +710,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
(call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
led->name = "fujitsu::kblamps";
led->brightness_set_blocking = kblamps_set;
led->brightness_get = kblamps_get;
@@ -723,6 +729,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
*/
if (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) {
led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
led->name = "fujitsu::radio_led";
led->brightness_set_blocking = radio_led_set;
led->brightness_get = radio_led_get;
@@ -741,6 +750,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
(call_fext_func(device,
FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) {
led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
led->name = "fujitsu::eco_led";
led->brightness_set_blocking = eco_led_set;
led->brightness_get = eco_led_get;
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index 527e5d9ab9bf..603fc6050971 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -909,17 +909,94 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
},
},
{
+ .ident = "Lenovo V310-14IKB",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-14IKB"),
+ },
+ },
+ {
+ .ident = "Lenovo V310-14ISK",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-14ISK"),
+ },
+ },
+ {
+ .ident = "Lenovo V310-15IKB",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-15IKB"),
+ },
+ },
+ {
.ident = "Lenovo V310-15ISK",
.matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-15ISK"),
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-15ISK"),
+ },
+ },
+ {
+ .ident = "Lenovo V510-15IKB",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V510-15IKB"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad 300-15IBR",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 300-15IBR"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad 300-15IKB",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 300-15IKB"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad 300S-11IBR",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 300S-11BR"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad 310-15ABR",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15ABR"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad 310-15IAP",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15IAP"),
},
},
{
.ident = "Lenovo ideapad 310-15IKB",
.matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15IKB"),
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15IKB"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad 310-15ISK",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15ISK"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad Y700-14ISK",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-14ISK"),
},
},
{
diff --git a/drivers/platform/x86/intel_telemetry_debugfs.c b/drivers/platform/x86/intel_telemetry_debugfs.c
index 4cc2f4ea0a25..cd21df982abd 100644
--- a/drivers/platform/x86/intel_telemetry_debugfs.c
+++ b/drivers/platform/x86/intel_telemetry_debugfs.c
@@ -710,6 +710,24 @@ static const struct file_operations telem_socstate_ops = {
.release = single_release,
};
+static int telem_s0ix_res_get(void *data, u64 *val)
+{
+ u64 s0ix_total_res;
+ int ret;
+
+ ret = intel_pmc_s0ix_counter_read(&s0ix_total_res);
+ if (ret) {
+ pr_err("Failed to read S0ix residency");
+ return ret;
+ }
+
+ *val = s0ix_total_res;
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(telem_s0ix_fops, telem_s0ix_res_get, NULL, "%llu\n");
+
static int telem_pss_trc_verb_show(struct seq_file *s, void *unused)
{
u32 verbosity;
@@ -938,7 +956,7 @@ static struct notifier_block pm_notifier = {
static int __init telemetry_debugfs_init(void)
{
const struct x86_cpu_id *id;
- int err = -ENOMEM;
+ int err;
struct dentry *f;
/* Only APL supported for now */
@@ -958,11 +976,10 @@ static int __init telemetry_debugfs_init(void)
register_pm_notifier(&pm_notifier);
+ err = -ENOMEM;
debugfs_conf->telemetry_dbg_dir = debugfs_create_dir("telemetry", NULL);
- if (!debugfs_conf->telemetry_dbg_dir) {
- err = -ENOMEM;
+ if (!debugfs_conf->telemetry_dbg_dir)
goto out_pm;
- }
f = debugfs_create_file("pss_info", S_IFREG | S_IRUGO,
debugfs_conf->telemetry_dbg_dir, NULL,
@@ -988,6 +1005,14 @@ static int __init telemetry_debugfs_init(void)
goto out;
}
+ f = debugfs_create_file("s0ix_residency_usec", S_IFREG | S_IRUGO,
+ debugfs_conf->telemetry_dbg_dir,
+ NULL, &telem_s0ix_fops);
+ if (!f) {
+ pr_err("s0ix_residency_usec debugfs register failed\n");
+ goto out;
+ }
+
f = debugfs_create_file("pss_trace_verbosity", S_IFREG | S_IRUGO,
debugfs_conf->telemetry_dbg_dir, NULL,
&telem_pss_trc_verb_ops);
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
index 76b0a58e205b..5c39b3211709 100644
--- a/drivers/platform/x86/panasonic-laptop.c
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -437,7 +437,7 @@ static struct attribute *pcc_sysfs_entries[] = {
NULL,
};
-static struct attribute_group pcc_attr_group = {
+static const struct attribute_group pcc_attr_group = {
.name = NULL, /* put in device directory */
.attrs = pcc_sysfs_entries,
};
diff --git a/drivers/platform/x86/peaq-wmi.c b/drivers/platform/x86/peaq-wmi.c
index ca75b4dc437e..77d1f90b0794 100644
--- a/drivers/platform/x86/peaq-wmi.c
+++ b/drivers/platform/x86/peaq-wmi.c
@@ -51,7 +51,7 @@ static void peaq_wmi_poll(struct input_polled_dev *dev)
return;
}
- if (peaq_ignore_events_counter && --peaq_ignore_events_counter > 0)
+ if (peaq_ignore_events_counter && --peaq_ignore_events_counter >= 0)
return;
if (obj.integer.value) {
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c
index 5c4dfe48f03d..0c703feaeb88 100644
--- a/drivers/platform/x86/samsung-laptop.c
+++ b/drivers/platform/x86/samsung-laptop.c
@@ -1232,7 +1232,7 @@ static umode_t samsung_sysfs_is_visible(struct kobject *kobj,
return ok ? attr->mode : 0;
}
-static struct attribute_group platform_attribute_group = {
+static const struct attribute_group platform_attribute_group = {
.is_visible = samsung_sysfs_is_visible,
.attrs = platform_attributes
};
diff --git a/drivers/platform/x86/silead_dmi.c b/drivers/platform/x86/silead_dmi.c
index 3cd3bdfe51df..1157a7b646d6 100644
--- a/drivers/platform/x86/silead_dmi.c
+++ b/drivers/platform/x86/silead_dmi.c
@@ -122,6 +122,20 @@ static const struct silead_ts_dmi_data pov_mobii_wintab_p800w_data = {
.properties = pov_mobii_wintab_p800w_props,
};
+static const struct property_entry itworks_tw891_props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1600),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 890),
+ PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
+ PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+ PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-itworks-tw891.fw"),
+ { }
+};
+
+static const struct silead_ts_dmi_data itworks_tw891_data = {
+ .acpi_name = "MSSL1680:00",
+ .properties = itworks_tw891_props,
+};
+
static const struct dmi_system_id silead_ts_dmi_table[] = {
{
/* CUBE iwork8 Air */
@@ -160,6 +174,16 @@ static const struct dmi_system_id silead_ts_dmi_table[] = {
},
},
{
+ /* Ployer Momo7w (same hardware as the Trekstor ST70416-6) */
+ .driver_data = (void *)&surftab_wintron70_st70416_6_data,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Shenzhen PLOYER"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MOMO7W"),
+ /* Exact match, different versions need different fw */
+ DMI_MATCH(DMI_BIOS_VERSION, "MOMO.G.WI71C.MABMRBA02"),
+ },
+ },
+ {
/* GP-electronic T701 */
.driver_data = (void *)&gp_electronic_t701_data,
.matches = {
@@ -187,6 +211,14 @@ static const struct dmi_system_id silead_ts_dmi_table[] = {
DMI_MATCH(DMI_BIOS_DATE, "08/22/2014"),
},
},
+ {
+ /* I.T.Works TW891 */
+ .driver_data = (void *)&itworks_tw891_data,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TW891"),
+ },
+ },
{ },
};
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 88f9f79a7cf6..bb1dcd7fbdeb 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -2419,7 +2419,7 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
return exists ? attr->mode : 0;
}
-static struct attribute_group toshiba_attr_group = {
+static const struct attribute_group toshiba_attr_group = {
.is_visible = toshiba_sysfs_is_visible,
.attrs = toshiba_attributes,
};
diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c
index 3de802f169a1..9dff1b4b85fc 100644
--- a/drivers/power/supply/twl4030_charger.c
+++ b/drivers/power/supply/twl4030_charger.c
@@ -980,10 +980,37 @@ static int twl4030_bci_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, bci);
+ INIT_WORK(&bci->work, twl4030_bci_usb_work);
+ INIT_DELAYED_WORK(&bci->current_worker, twl4030_current_worker);
+
bci->channel_vac = devm_iio_channel_get(&pdev->dev, "vac");
if (IS_ERR(bci->channel_vac)) {
+ ret = PTR_ERR(bci->channel_vac);
+ if (ret == -EPROBE_DEFER)
+ return ret; /* iio not ready */
+ dev_warn(&pdev->dev, "could not request vac iio channel (%d)",
+ ret);
bci->channel_vac = NULL;
- dev_warn(&pdev->dev, "could not request vac iio channel");
+ }
+
+ if (bci->dev->of_node) {
+ struct device_node *phynode;
+
+ phynode = of_find_compatible_node(bci->dev->of_node->parent,
+ NULL, "ti,twl4030-usb");
+ if (phynode) {
+ bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
+ bci->transceiver = devm_usb_get_phy_by_node(
+ bci->dev, phynode, &bci->usb_nb);
+ if (IS_ERR(bci->transceiver)) {
+ ret = PTR_ERR(bci->transceiver);
+ if (ret == -EPROBE_DEFER)
+ return ret; /* phy not ready */
+ dev_warn(&pdev->dev, "could not request transceiver (%d)",
+ ret);
+ bci->transceiver = NULL;
+ }
+ }
}
bci->ac = devm_power_supply_register(&pdev->dev, &twl4030_bci_ac_desc,
@@ -1019,20 +1046,6 @@ static int twl4030_bci_probe(struct platform_device *pdev)
return ret;
}
- INIT_WORK(&bci->work, twl4030_bci_usb_work);
- INIT_DELAYED_WORK(&bci->current_worker, twl4030_current_worker);
-
- bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
- if (bci->dev->of_node) {
- struct device_node *phynode;
-
- phynode = of_find_compatible_node(bci->dev->of_node->parent,
- NULL, "ti,twl4030-usb");
- if (phynode)
- bci->transceiver = devm_usb_get_phy_by_node(
- bci->dev, phynode, &bci->usb_nb);
- }
-
/* Enable interrupts now. */
reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 |
TWL4030_TBATOR1 | TWL4030_BATSTS);
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index a0860b30bd93..1581f6ab1b1f 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -678,7 +678,9 @@ struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id)
pc = of_node_to_pwmchip(args.np);
if (IS_ERR(pc)) {
- pr_err("%s(): PWM chip not found\n", __func__);
+ if (PTR_ERR(pc) != -EPROBE_DEFER)
+ pr_err("%s(): PWM chip not found\n", __func__);
+
pwm = ERR_CAST(pc);
goto put;
}
diff --git a/drivers/pwm/pwm-bfin.c b/drivers/pwm/pwm-bfin.c
index d2ed0a2a18e8..a9a88137f2cb 100644
--- a/drivers/pwm/pwm-bfin.c
+++ b/drivers/pwm/pwm-bfin.c
@@ -118,10 +118,8 @@ static int bfin_pwm_probe(struct platform_device *pdev)
int ret;
pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
- if (!pwm) {
- dev_err(&pdev->dev, "failed to allocate memory\n");
+ if (!pwm)
return -ENOMEM;
- }
platform_set_drvdata(pdev, pwm);
diff --git a/drivers/pwm/pwm-cros-ec.c b/drivers/pwm/pwm-cros-ec.c
index f6ca4e8c6253..9c13694eaa24 100644
--- a/drivers/pwm/pwm-cros-ec.c
+++ b/drivers/pwm/pwm-cros-ec.c
@@ -75,8 +75,8 @@ static int __cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index,
msg->version = 0;
msg->command = EC_CMD_PWM_GET_DUTY;
- msg->insize = sizeof(*params);
- msg->outsize = sizeof(*resp);
+ msg->insize = sizeof(*resp);
+ msg->outsize = sizeof(*params);
params->pwm_type = EC_PWM_TYPE_GENERIC;
params->index = index;
diff --git a/drivers/pwm/pwm-hibvt.c b/drivers/pwm/pwm-hibvt.c
index d0e8f8542626..8dadc58d6cdf 100644
--- a/drivers/pwm/pwm-hibvt.c
+++ b/drivers/pwm/pwm-hibvt.c
@@ -165,7 +165,7 @@ static int hibvt_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
return 0;
}
-static struct pwm_ops hibvt_pwm_ops = {
+static const struct pwm_ops hibvt_pwm_ops = {
.get_state = hibvt_pwm_get_state,
.apply = hibvt_pwm_apply,
diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c
index 045ef9fa6fe3..cb845edfe2b4 100644
--- a/drivers/pwm/pwm-meson.c
+++ b/drivers/pwm/pwm-meson.c
@@ -103,6 +103,7 @@ struct meson_pwm_channel {
struct meson_pwm_data {
const char * const *parent_names;
+ unsigned int num_parents;
};
struct meson_pwm {
@@ -162,7 +163,8 @@ static int meson_pwm_calc(struct meson_pwm *meson,
unsigned int duty, unsigned int period)
{
unsigned int pre_div, cnt, duty_cnt;
- unsigned long fin_freq = -1, fin_ns;
+ unsigned long fin_freq = -1;
+ u64 fin_ps;
if (~(meson->inverter_mask >> id) & 0x1)
duty = period - duty;
@@ -178,13 +180,15 @@ static int meson_pwm_calc(struct meson_pwm *meson,
}
dev_dbg(meson->chip.dev, "fin_freq: %lu Hz\n", fin_freq);
- fin_ns = NSEC_PER_SEC / fin_freq;
+ fin_ps = (u64)NSEC_PER_SEC * 1000;
+ do_div(fin_ps, fin_freq);
/* Calc pre_div with the period */
for (pre_div = 0; pre_div < MISC_CLK_DIV_MASK; pre_div++) {
- cnt = DIV_ROUND_CLOSEST(period, fin_ns * (pre_div + 1));
- dev_dbg(meson->chip.dev, "fin_ns=%lu pre_div=%u cnt=%u\n",
- fin_ns, pre_div, cnt);
+ cnt = DIV_ROUND_CLOSEST_ULL((u64)period * 1000,
+ fin_ps * (pre_div + 1));
+ dev_dbg(meson->chip.dev, "fin_ps=%llu pre_div=%u cnt=%u\n",
+ fin_ps, pre_div, cnt);
if (cnt <= 0xffff)
break;
}
@@ -207,7 +211,8 @@ static int meson_pwm_calc(struct meson_pwm *meson,
channel->lo = cnt;
} else {
/* Then check is we can have the duty with the same pre_div */
- duty_cnt = DIV_ROUND_CLOSEST(duty, fin_ns * (pre_div + 1));
+ duty_cnt = DIV_ROUND_CLOSEST_ULL((u64)duty * 1000,
+ fin_ps * (pre_div + 1));
if (duty_cnt > 0xffff) {
dev_err(meson->chip.dev, "unable to get duty cycle\n");
return -EINVAL;
@@ -381,6 +386,7 @@ static const char * const pwm_meson8b_parent_names[] = {
static const struct meson_pwm_data pwm_meson8b_data = {
.parent_names = pwm_meson8b_parent_names,
+ .num_parents = ARRAY_SIZE(pwm_meson8b_parent_names),
};
static const char * const pwm_gxbb_parent_names[] = {
@@ -389,11 +395,35 @@ static const char * const pwm_gxbb_parent_names[] = {
static const struct meson_pwm_data pwm_gxbb_data = {
.parent_names = pwm_gxbb_parent_names,
+ .num_parents = ARRAY_SIZE(pwm_gxbb_parent_names),
+};
+
+/*
+ * Only the 2 first inputs of the GXBB AO PWMs are valid
+ * The last 2 are grounded
+ */
+static const char * const pwm_gxbb_ao_parent_names[] = {
+ "xtal", "clk81"
+};
+
+static const struct meson_pwm_data pwm_gxbb_ao_data = {
+ .parent_names = pwm_gxbb_ao_parent_names,
+ .num_parents = ARRAY_SIZE(pwm_gxbb_ao_parent_names),
};
static const struct of_device_id meson_pwm_matches[] = {
- { .compatible = "amlogic,meson8b-pwm", .data = &pwm_meson8b_data },
- { .compatible = "amlogic,meson-gxbb-pwm", .data = &pwm_gxbb_data },
+ {
+ .compatible = "amlogic,meson8b-pwm",
+ .data = &pwm_meson8b_data
+ },
+ {
+ .compatible = "amlogic,meson-gxbb-pwm",
+ .data = &pwm_gxbb_data
+ },
+ {
+ .compatible = "amlogic,meson-gxbb-ao-pwm",
+ .data = &pwm_gxbb_ao_data
+ },
{},
};
MODULE_DEVICE_TABLE(of, meson_pwm_matches);
@@ -417,7 +447,7 @@ static int meson_pwm_init_channels(struct meson_pwm *meson,
init.ops = &clk_mux_ops;
init.flags = CLK_IS_BASIC;
init.parent_names = meson->data->parent_names;
- init.num_parents = 1 << MISC_CLK_SEL_WIDTH;
+ init.num_parents = meson->data->num_parents;
channel->mux.reg = meson->base + REG_MISC_AB;
channel->mux.shift = mux_reg_shifts[i];
diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c
index 1284ffa05921..6d23f1d1c9b7 100644
--- a/drivers/pwm/pwm-sun4i.c
+++ b/drivers/pwm/pwm-sun4i.c
@@ -8,8 +8,10 @@
#include <linux/bitops.h>
#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
+#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -44,6 +46,10 @@
#define PWM_DTY_MASK GENMASK(15, 0)
+#define PWM_REG_PRD(reg) ((((reg) >> 16) & PWM_PRD_MASK) + 1)
+#define PWM_REG_DTY(reg) ((reg) & PWM_DTY_MASK)
+#define PWM_REG_PRESCAL(reg, chan) (((reg) >> ((chan) * PWMCH_OFFSET)) & PWM_PRESCAL_MASK)
+
#define BIT_CH(bit, chan) ((bit) << ((chan) * PWMCH_OFFSET))
static const u32 prescaler_table[] = {
@@ -77,6 +83,8 @@ struct sun4i_pwm_chip {
void __iomem *base;
spinlock_t ctrl_lock;
const struct sun4i_pwm_data *data;
+ unsigned long next_period[2];
+ bool needs_delay[2];
};
static inline struct sun4i_pwm_chip *to_sun4i_pwm_chip(struct pwm_chip *chip)
@@ -96,26 +104,65 @@ static inline void sun4i_pwm_writel(struct sun4i_pwm_chip *chip,
writel(val, chip->base + offset);
}
-static int sun4i_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
- int duty_ns, int period_ns)
+static void sun4i_pwm_get_state(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ struct pwm_state *state)
{
struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip);
- u32 prd, dty, val, clk_gate;
+ u64 clk_rate, tmp;
+ u32 val;
+ unsigned int prescaler;
+
+ clk_rate = clk_get_rate(sun4i_pwm->clk);
+
+ val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
+
+ if ((val == PWM_PRESCAL_MASK) && sun4i_pwm->data->has_prescaler_bypass)
+ prescaler = 1;
+ else
+ prescaler = prescaler_table[PWM_REG_PRESCAL(val, pwm->hwpwm)];
+
+ if (prescaler == 0)
+ return;
+
+ if (val & BIT_CH(PWM_ACT_STATE, pwm->hwpwm))
+ state->polarity = PWM_POLARITY_NORMAL;
+ else
+ state->polarity = PWM_POLARITY_INVERSED;
+
+ if (val & BIT_CH(PWM_CLK_GATING | PWM_EN, pwm->hwpwm))
+ state->enabled = true;
+ else
+ state->enabled = false;
+
+ val = sun4i_pwm_readl(sun4i_pwm, PWM_CH_PRD(pwm->hwpwm));
+
+ tmp = prescaler * NSEC_PER_SEC * PWM_REG_DTY(val);
+ state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);
+
+ tmp = prescaler * NSEC_PER_SEC * PWM_REG_PRD(val);
+ state->period = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);
+}
+
+static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4i_pwm,
+ struct pwm_state *state,
+ u32 *dty, u32 *prd, unsigned int *prsclr)
+{
u64 clk_rate, div = 0;
- unsigned int prescaler = 0;
- int err;
+ unsigned int pval, prescaler = 0;
clk_rate = clk_get_rate(sun4i_pwm->clk);
if (sun4i_pwm->data->has_prescaler_bypass) {
/* First, test without any prescaler when available */
prescaler = PWM_PRESCAL_MASK;
+ pval = 1;
/*
* When not using any prescaler, the clock period in nanoseconds
* is not an integer so round it half up instead of
* truncating to get less surprising values.
*/
- div = clk_rate * period_ns + NSEC_PER_SEC / 2;
+ div = clk_rate * state->period + NSEC_PER_SEC / 2;
do_div(div, NSEC_PER_SEC);
if (div - 1 > PWM_PRD_MASK)
prescaler = 0;
@@ -126,137 +173,141 @@ static int sun4i_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
for (prescaler = 0; prescaler < PWM_PRESCAL_MASK; prescaler++) {
if (!prescaler_table[prescaler])
continue;
+ pval = prescaler_table[prescaler];
div = clk_rate;
- do_div(div, prescaler_table[prescaler]);
- div = div * period_ns;
+ do_div(div, pval);
+ div = div * state->period;
do_div(div, NSEC_PER_SEC);
if (div - 1 <= PWM_PRD_MASK)
break;
}
- if (div - 1 > PWM_PRD_MASK) {
- dev_err(chip->dev, "period exceeds the maximum value\n");
+ if (div - 1 > PWM_PRD_MASK)
return -EINVAL;
- }
- }
-
- prd = div;
- div *= duty_ns;
- do_div(div, period_ns);
- dty = div;
-
- err = clk_prepare_enable(sun4i_pwm->clk);
- if (err) {
- dev_err(chip->dev, "failed to enable PWM clock\n");
- return err;
- }
-
- spin_lock(&sun4i_pwm->ctrl_lock);
- val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
-
- if (sun4i_pwm->data->has_rdy && (val & PWM_RDY(pwm->hwpwm))) {
- spin_unlock(&sun4i_pwm->ctrl_lock);
- clk_disable_unprepare(sun4i_pwm->clk);
- return -EBUSY;
- }
-
- clk_gate = val & BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
- if (clk_gate) {
- val &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
- sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG);
}
- val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
- val &= ~BIT_CH(PWM_PRESCAL_MASK, pwm->hwpwm);
- val |= BIT_CH(prescaler, pwm->hwpwm);
- sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG);
-
- val = (dty & PWM_DTY_MASK) | PWM_PRD(prd);
- sun4i_pwm_writel(sun4i_pwm, val, PWM_CH_PRD(pwm->hwpwm));
+ *prd = div;
+ div *= state->duty_cycle;
+ do_div(div, state->period);
+ *dty = div;
+ *prsclr = prescaler;
- if (clk_gate) {
- val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
- val |= clk_gate;
- sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG);
- }
+ div = (u64)pval * NSEC_PER_SEC * *prd;
+ state->period = DIV_ROUND_CLOSEST_ULL(div, clk_rate);
- spin_unlock(&sun4i_pwm->ctrl_lock);
- clk_disable_unprepare(sun4i_pwm->clk);
+ div = (u64)pval * NSEC_PER_SEC * *dty;
+ state->duty_cycle = DIV_ROUND_CLOSEST_ULL(div, clk_rate);
return 0;
}
-static int sun4i_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
- enum pwm_polarity polarity)
+static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
{
struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip);
- u32 val;
+ struct pwm_state cstate;
+ u32 ctrl;
int ret;
+ unsigned int delay_us;
+ unsigned long now;
- ret = clk_prepare_enable(sun4i_pwm->clk);
- if (ret) {
- dev_err(chip->dev, "failed to enable PWM clock\n");
- return ret;
+ pwm_get_state(pwm, &cstate);
+
+ if (!cstate.enabled) {
+ ret = clk_prepare_enable(sun4i_pwm->clk);
+ if (ret) {
+ dev_err(chip->dev, "failed to enable PWM clock\n");
+ return ret;
+ }
}
spin_lock(&sun4i_pwm->ctrl_lock);
- val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
+ ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
- if (polarity != PWM_POLARITY_NORMAL)
- val &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
- else
- val |= BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
+ if ((cstate.period != state->period) ||
+ (cstate.duty_cycle != state->duty_cycle)) {
+ u32 period, duty, val;
+ unsigned int prescaler;
- sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG);
+ ret = sun4i_pwm_calculate(sun4i_pwm, state,
+ &duty, &period, &prescaler);
+ if (ret) {
+ dev_err(chip->dev, "period exceeds the maximum value\n");
+ spin_unlock(&sun4i_pwm->ctrl_lock);
+ if (!cstate.enabled)
+ clk_disable_unprepare(sun4i_pwm->clk);
+ return ret;
+ }
- spin_unlock(&sun4i_pwm->ctrl_lock);
- clk_disable_unprepare(sun4i_pwm->clk);
+ if (PWM_REG_PRESCAL(ctrl, pwm->hwpwm) != prescaler) {
+ /* Prescaler changed, the clock has to be gated */
+ ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
+ sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
- return 0;
-}
+ ctrl &= ~BIT_CH(PWM_PRESCAL_MASK, pwm->hwpwm);
+ ctrl |= BIT_CH(prescaler, pwm->hwpwm);
+ }
-static int sun4i_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
-{
- struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip);
- u32 val;
- int ret;
+ val = (duty & PWM_DTY_MASK) | PWM_PRD(period);
+ sun4i_pwm_writel(sun4i_pwm, val, PWM_CH_PRD(pwm->hwpwm));
+ sun4i_pwm->next_period[pwm->hwpwm] = jiffies +
+ usecs_to_jiffies(cstate.period / 1000 + 1);
+ sun4i_pwm->needs_delay[pwm->hwpwm] = true;
+ }
- ret = clk_prepare_enable(sun4i_pwm->clk);
- if (ret) {
- dev_err(chip->dev, "failed to enable PWM clock\n");
- return ret;
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ ctrl &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
+ else
+ ctrl |= BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
+
+ ctrl |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
+ if (state->enabled) {
+ ctrl |= BIT_CH(PWM_EN, pwm->hwpwm);
+ } else if (!sun4i_pwm->needs_delay[pwm->hwpwm]) {
+ ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm);
+ ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
}
- spin_lock(&sun4i_pwm->ctrl_lock);
- val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
- val |= BIT_CH(PWM_EN, pwm->hwpwm);
- val |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
- sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG);
+ sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
+
spin_unlock(&sun4i_pwm->ctrl_lock);
- return 0;
-}
+ if (state->enabled)
+ return 0;
-static void sun4i_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
-{
- struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip);
- u32 val;
+ if (!sun4i_pwm->needs_delay[pwm->hwpwm]) {
+ clk_disable_unprepare(sun4i_pwm->clk);
+ return 0;
+ }
+
+ /* We need a full period to elapse before disabling the channel. */
+ now = jiffies;
+ if (sun4i_pwm->needs_delay[pwm->hwpwm] &&
+ time_before(now, sun4i_pwm->next_period[pwm->hwpwm])) {
+ delay_us = jiffies_to_usecs(sun4i_pwm->next_period[pwm->hwpwm] -
+ now);
+ if ((delay_us / 500) > MAX_UDELAY_MS)
+ msleep(delay_us / 1000 + 1);
+ else
+ usleep_range(delay_us, delay_us * 2);
+ }
+ sun4i_pwm->needs_delay[pwm->hwpwm] = false;
spin_lock(&sun4i_pwm->ctrl_lock);
- val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
- val &= ~BIT_CH(PWM_EN, pwm->hwpwm);
- val &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
- sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG);
+ ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
+ ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
+ ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm);
+ sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
spin_unlock(&sun4i_pwm->ctrl_lock);
clk_disable_unprepare(sun4i_pwm->clk);
+
+ return 0;
}
static const struct pwm_ops sun4i_pwm_ops = {
- .config = sun4i_pwm_config,
- .set_polarity = sun4i_pwm_set_polarity,
- .enable = sun4i_pwm_enable,
- .disable = sun4i_pwm_disable,
+ .apply = sun4i_pwm_apply,
+ .get_state = sun4i_pwm_get_state,
.owner = THIS_MODULE,
};
@@ -316,8 +367,7 @@ static int sun4i_pwm_probe(struct platform_device *pdev)
{
struct sun4i_pwm_chip *pwm;
struct resource *res;
- u32 val;
- int i, ret;
+ int ret;
const struct of_device_id *match;
match = of_match_device(sun4i_pwm_dt_ids, &pdev->dev);
@@ -353,24 +403,7 @@ static int sun4i_pwm_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pwm);
- ret = clk_prepare_enable(pwm->clk);
- if (ret) {
- dev_err(&pdev->dev, "failed to enable PWM clock\n");
- goto clk_error;
- }
-
- val = sun4i_pwm_readl(pwm, PWM_CTRL_REG);
- for (i = 0; i < pwm->chip.npwm; i++)
- if (!(val & BIT_CH(PWM_ACT_STATE, i)))
- pwm_set_polarity(&pwm->chip.pwms[i],
- PWM_POLARITY_INVERSED);
- clk_disable_unprepare(pwm->clk);
-
return 0;
-
-clk_error:
- pwmchip_remove(&pwm->chip);
- return ret;
}
static int sun4i_pwm_remove(struct platform_device *pdev)
diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
index 8c6ed556db28..e9b33f09ff09 100644
--- a/drivers/pwm/pwm-tegra.c
+++ b/drivers/pwm/pwm-tegra.c
@@ -41,6 +41,9 @@
struct tegra_pwm_soc {
unsigned int num_channels;
+
+ /* Maximum IP frequency for given SoCs */
+ unsigned long max_frequency;
};
struct tegra_pwm_chip {
@@ -201,7 +204,18 @@ static int tegra_pwm_probe(struct platform_device *pdev)
if (IS_ERR(pwm->clk))
return PTR_ERR(pwm->clk);
- /* Read PWM clock rate from source */
+ /* Set maximum frequency of the IP */
+ ret = clk_set_rate(pwm->clk, pwm->soc->max_frequency);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to set max frequency: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * The requested and configured frequency may differ due to
+ * clock register resolutions. Get the configured frequency
+ * so that PWM period can be calculated more accurately.
+ */
pwm->clk_rate = clk_get_rate(pwm->clk);
pwm->rst = devm_reset_control_get(&pdev->dev, "pwm");
@@ -273,10 +287,12 @@ static int tegra_pwm_resume(struct device *dev)
static const struct tegra_pwm_soc tegra20_pwm_soc = {
.num_channels = 4,
+ .max_frequency = 48000000UL,
};
static const struct tegra_pwm_soc tegra186_pwm_soc = {
.num_channels = 1,
+ .max_frequency = 102000000UL,
};
static const struct of_device_id tegra_pwm_of_match[] = {
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 8d3b95728326..72419ac2c52a 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -77,6 +77,14 @@ config RTC_DEBUG
Say yes here to enable debugging support in the RTC framework
and individual RTC drivers.
+config RTC_NVMEM
+ bool "RTC non volatile storage support"
+ select NVMEM
+ default RTC_CLASS
+ help
+ Say yes here to add support for the non volatile (often battery
+ backed) storage present on RTCs.
+
comment "RTC interfaces"
config RTC_INTF_SYSFS
@@ -197,6 +205,17 @@ config RTC_DRV_AC100
This driver can also be built as a module. If so, the module
will be called rtc-ac100.
+config RTC_DRV_BRCMSTB
+ tristate "Broadcom STB wake-timer"
+ depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
+ default ARCH_BRCMSTB || BMIPS_GENERIC
+ help
+ If you say yes here you get support for the wake-timer found on
+ Broadcom STB SoCs (BCM7xxx).
+
+ This driver can also be built as a module. If so, the module will
+ be called rtc-brcmstb-waketimer.
+
config RTC_DRV_AS3722
tristate "ams AS3722 RTC driver"
depends on MFD_AS3722
@@ -791,6 +810,14 @@ config RTC_DRV_DS3232
This driver can also be built as a module. If so, the module
will be called rtc-ds3232.
+config RTC_DRV_DS3232_HWMON
+ bool "HWMON support for Dallas/Maxim DS3232/DS3234"
+ depends on RTC_DRV_DS3232 && HWMON && !(RTC_DRV_DS3232=y && HWMON=m)
+ default y
+ help
+ Say Y here if you want to expose temperature sensor data on
+ rtc-ds3232
+
config RTC_DRV_PCF2127
tristate "NXP PCF2127"
depends on RTC_I2C_AND_SPI
@@ -1484,16 +1511,16 @@ config RTC_DRV_ARMADA38X
This driver can also be built as a module. If so, the module
will be called armada38x-rtc.
-config RTC_DRV_GEMINI
- tristate "Gemini SoC RTC"
- depends on ARCH_GEMINI || COMPILE_TEST
+config RTC_DRV_FTRTC010
+ tristate "Faraday Technology FTRTC010 RTC"
depends on HAS_IOMEM
+ default ARCH_GEMINI
help
If you say Y here you will get support for the
- RTC found on Gemini SoC's.
+ Faraday Technolog FTRTC010 found on e.g. Gemini SoC's.
This driver can also be built as a module. If so, the module
- will be called rtc-gemini.
+ will be called rtc-ftrtc010.
config RTC_DRV_PS3
tristate "PS3 RTC"
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 13857d2fce09..acd366b41c85 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -15,6 +15,7 @@ ifdef CONFIG_RTC_DRV_EFI
rtc-core-y += rtc-efi-platform.o
endif
+rtc-core-$(CONFIG_RTC_NVMEM) += nvmem.o
rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
@@ -36,6 +37,7 @@ obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
+obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o
obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
@@ -67,7 +69,7 @@ obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
-obj-$(CONFIG_RTC_DRV_GEMINI) += rtc-gemini.o
+obj-$(CONFIG_RTC_DRV_FTRTC010) += rtc-ftrtc010.o
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
obj-$(CONFIG_RTC_DRV_HYM8563) += rtc-hym8563.o
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 5fb439897fe1..2ed970d61da1 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -150,59 +150,19 @@ static SIMPLE_DEV_PM_OPS(rtc_class_dev_pm_ops, rtc_suspend, rtc_resume);
#define RTC_CLASS_DEV_PM_OPS NULL
#endif
-
-/**
- * rtc_device_register - register w/ RTC class
- * @dev: the device to register
- *
- * rtc_device_unregister() must be called when the class device is no
- * longer needed.
- *
- * Returns the pointer to the new struct class device.
- */
-struct rtc_device *rtc_device_register(const char *name, struct device *dev,
- const struct rtc_class_ops *ops,
- struct module *owner)
+/* Ensure the caller will set the id before releasing the device */
+static struct rtc_device *rtc_allocate_device(void)
{
struct rtc_device *rtc;
- struct rtc_wkalrm alrm;
- int of_id = -1, id = -1, err;
-
- if (dev->of_node)
- of_id = of_alias_get_id(dev->of_node, "rtc");
- else if (dev->parent && dev->parent->of_node)
- of_id = of_alias_get_id(dev->parent->of_node, "rtc");
- if (of_id >= 0) {
- id = ida_simple_get(&rtc_ida, of_id, of_id + 1,
- GFP_KERNEL);
- if (id < 0)
- dev_warn(dev, "/aliases ID %d not available\n",
- of_id);
- }
-
- if (id < 0) {
- id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);
- if (id < 0) {
- err = id;
- goto exit;
- }
- }
-
- rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
- if (rtc == NULL) {
- err = -ENOMEM;
- goto exit_ida;
- }
+ rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return NULL;
device_initialize(&rtc->dev);
- rtc->id = id;
- rtc->ops = ops;
- rtc->owner = owner;
rtc->irq_freq = 1;
rtc->max_user_freq = 64;
- rtc->dev.parent = dev;
rtc->dev.class = rtc_class;
rtc->dev.groups = rtc_get_dev_attribute_groups();
rtc->dev.release = rtc_device_release;
@@ -224,7 +184,64 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
rtc->pie_timer.function = rtc_pie_update_irq;
rtc->pie_enabled = 0;
- strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
+ return rtc;
+}
+
+static int rtc_device_get_id(struct device *dev)
+{
+ int of_id = -1, id = -1;
+
+ if (dev->of_node)
+ of_id = of_alias_get_id(dev->of_node, "rtc");
+ else if (dev->parent && dev->parent->of_node)
+ of_id = of_alias_get_id(dev->parent->of_node, "rtc");
+
+ if (of_id >= 0) {
+ id = ida_simple_get(&rtc_ida, of_id, of_id + 1, GFP_KERNEL);
+ if (id < 0)
+ dev_warn(dev, "/aliases ID %d not available\n", of_id);
+ }
+
+ if (id < 0)
+ id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);
+
+ return id;
+}
+
+/**
+ * rtc_device_register - register w/ RTC class
+ * @dev: the device to register
+ *
+ * rtc_device_unregister() must be called when the class device is no
+ * longer needed.
+ *
+ * Returns the pointer to the new struct class device.
+ */
+struct rtc_device *rtc_device_register(const char *name, struct device *dev,
+ const struct rtc_class_ops *ops,
+ struct module *owner)
+{
+ struct rtc_device *rtc;
+ struct rtc_wkalrm alrm;
+ int id, err;
+
+ id = rtc_device_get_id(dev);
+ if (id < 0) {
+ err = id;
+ goto exit;
+ }
+
+ rtc = rtc_allocate_device();
+ if (!rtc) {
+ err = -ENOMEM;
+ goto exit_ida;
+ }
+
+ rtc->id = id;
+ rtc->ops = ops;
+ rtc->owner = owner;
+ rtc->dev.parent = dev;
+
dev_set_name(&rtc->dev, "rtc%d", id);
/* Check to see if there is an ALARM already set in hw */
@@ -238,20 +255,20 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
err = cdev_device_add(&rtc->char_dev, &rtc->dev);
if (err) {
dev_warn(&rtc->dev, "%s: failed to add char device %d:%d\n",
- rtc->name, MAJOR(rtc->dev.devt), rtc->id);
+ name, MAJOR(rtc->dev.devt), rtc->id);
/* This will free both memory and the ID */
put_device(&rtc->dev);
goto exit;
} else {
- dev_dbg(&rtc->dev, "%s: dev (%d:%d)\n", rtc->name,
+ dev_dbg(&rtc->dev, "%s: dev (%d:%d)\n", name,
MAJOR(rtc->dev.devt), rtc->id);
}
rtc_proc_add_device(rtc);
dev_info(dev, "rtc core: registered %s as %s\n",
- rtc->name, dev_name(&rtc->dev));
+ name, dev_name(&rtc->dev));
return rtc;
@@ -273,6 +290,8 @@ EXPORT_SYMBOL_GPL(rtc_device_register);
*/
void rtc_device_unregister(struct rtc_device *rtc)
{
+ rtc_nvmem_unregister(rtc);
+
mutex_lock(&rtc->ops_lock);
/*
* Remove innards of this RTC, then disable it, before
@@ -356,6 +375,91 @@ void devm_rtc_device_unregister(struct device *dev, struct rtc_device *rtc)
}
EXPORT_SYMBOL_GPL(devm_rtc_device_unregister);
+static void devm_rtc_release_device(struct device *dev, void *res)
+{
+ struct rtc_device *rtc = *(struct rtc_device **)res;
+
+ if (rtc->registered)
+ rtc_device_unregister(rtc);
+ else
+ put_device(&rtc->dev);
+}
+
+struct rtc_device *devm_rtc_allocate_device(struct device *dev)
+{
+ struct rtc_device **ptr, *rtc;
+ int id, err;
+
+ id = rtc_device_get_id(dev);
+ if (id < 0)
+ return ERR_PTR(id);
+
+ ptr = devres_alloc(devm_rtc_release_device, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr) {
+ err = -ENOMEM;
+ goto exit_ida;
+ }
+
+ rtc = rtc_allocate_device();
+ if (!rtc) {
+ err = -ENOMEM;
+ goto exit_devres;
+ }
+
+ *ptr = rtc;
+ devres_add(dev, ptr);
+
+ rtc->id = id;
+ rtc->dev.parent = dev;
+ dev_set_name(&rtc->dev, "rtc%d", id);
+
+ return rtc;
+
+exit_devres:
+ devres_free(ptr);
+exit_ida:
+ ida_simple_remove(&rtc_ida, id);
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(devm_rtc_allocate_device);
+
+int __rtc_register_device(struct module *owner, struct rtc_device *rtc)
+{
+ struct rtc_wkalrm alrm;
+ int err;
+
+ if (!rtc->ops)
+ return -EINVAL;
+
+ rtc->owner = owner;
+
+ /* Check to see if there is an ALARM already set in hw */
+ err = __rtc_read_alarm(rtc, &alrm);
+ if (!err && !rtc_valid_tm(&alrm.time))
+ rtc_initialize_alarm(rtc, &alrm);
+
+ rtc_dev_prepare(rtc);
+
+ err = cdev_device_add(&rtc->char_dev, &rtc->dev);
+ if (err)
+ dev_warn(rtc->dev.parent, "failed to add char device %d:%d\n",
+ MAJOR(rtc->dev.devt), rtc->id);
+ else
+ dev_dbg(rtc->dev.parent, "char device (%d:%d)\n",
+ MAJOR(rtc->dev.devt), rtc->id);
+
+ rtc_proc_add_device(rtc);
+
+ rtc_nvmem_register(rtc);
+
+ rtc->registered = true;
+ dev_info(rtc->dev.parent, "registered as %s\n",
+ dev_name(&rtc->dev));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__rtc_register_device);
+
static int __init rtc_init(void)
{
rtc_class = class_create(THIS_MODULE, "rtc");
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index fc0fa7577636..8cec9a02c0b8 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -227,6 +227,13 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
missing = year;
}
+ /* Can't proceed if alarm is still invalid after replacing
+ * missing fields.
+ */
+ err = rtc_valid_tm(&alarm->time);
+ if (err)
+ goto done;
+
/* with luck, no rollover is needed */
t_now = rtc_tm_to_time64(&now);
t_alm = rtc_tm_to_time64(&alarm->time);
@@ -278,9 +285,9 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
dev_warn(&rtc->dev, "alarm rollover not handled\n");
}
-done:
err = rtc_valid_tm(&alarm->time);
+done:
if (err) {
dev_warn(&rtc->dev, "invalid alarm value: %d-%d-%d %d:%d:%d\n",
alarm->time.tm_year + 1900, alarm->time.tm_mon + 1,
diff --git a/drivers/rtc/nvmem.c b/drivers/rtc/nvmem.c
new file mode 100644
index 000000000000..8567b4ed9ac6
--- /dev/null
+++ b/drivers/rtc/nvmem.c
@@ -0,0 +1,113 @@
+/*
+ * RTC subsystem, nvmem interface
+ *
+ * Copyright (C) 2017 Alexandre Belloni
+ *
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/types.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/rtc.h>
+#include <linux/sysfs.h>
+
+#include "rtc-core.h"
+
+/*
+ * Deprecated ABI compatibility, this should be removed at some point
+ */
+
+static const char nvram_warning[] = "Deprecated ABI, please use nvmem";
+
+static ssize_t
+rtc_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct rtc_device *rtc = attr->private;
+
+ dev_warn_once(kobj_to_dev(kobj), nvram_warning);
+
+ return nvmem_device_read(rtc->nvmem, off, count, buf);
+}
+
+static ssize_t
+rtc_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct rtc_device *rtc = attr->private;
+
+ dev_warn_once(kobj_to_dev(kobj), nvram_warning);
+
+ return nvmem_device_write(rtc->nvmem, off, count, buf);
+}
+
+static int rtc_nvram_register(struct rtc_device *rtc)
+{
+ int err;
+
+ rtc->nvram = devm_kzalloc(rtc->dev.parent,
+ sizeof(struct bin_attribute),
+ GFP_KERNEL);
+ if (!rtc->nvram)
+ return -ENOMEM;
+
+ rtc->nvram->attr.name = "nvram";
+ rtc->nvram->attr.mode = 0644;
+ rtc->nvram->private = rtc;
+
+ sysfs_bin_attr_init(rtc->nvram);
+
+ rtc->nvram->read = rtc_nvram_read;
+ rtc->nvram->write = rtc_nvram_write;
+ rtc->nvram->size = rtc->nvmem_config->size;
+
+ err = sysfs_create_bin_file(&rtc->dev.parent->kobj,
+ rtc->nvram);
+ if (err) {
+ devm_kfree(rtc->dev.parent, rtc->nvram);
+ rtc->nvram = NULL;
+ }
+
+ return err;
+}
+
+static void rtc_nvram_unregister(struct rtc_device *rtc)
+{
+ sysfs_remove_bin_file(&rtc->dev.parent->kobj, rtc->nvram);
+}
+
+/*
+ * New ABI, uses nvmem
+ */
+void rtc_nvmem_register(struct rtc_device *rtc)
+{
+ if (!rtc->nvmem_config)
+ return;
+
+ rtc->nvmem_config->dev = &rtc->dev;
+ rtc->nvmem_config->owner = rtc->owner;
+ rtc->nvmem = nvmem_register(rtc->nvmem_config);
+ if (IS_ERR_OR_NULL(rtc->nvmem))
+ return;
+
+ /* Register the old ABI */
+ if (rtc->nvram_old_abi)
+ rtc_nvram_register(rtc);
+}
+
+void rtc_nvmem_unregister(struct rtc_device *rtc)
+{
+ if (IS_ERR_OR_NULL(rtc->nvmem))
+ return;
+
+ /* unregister the old ABI */
+ if (rtc->nvram)
+ rtc_nvram_unregister(rtc);
+
+ nvmem_unregister(rtc->nvmem);
+}
diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c
index b60fd477778f..e221b78b6f10 100644
--- a/drivers/rtc/rtc-at91rm9200.c
+++ b/drivers/rtc/rtc-at91rm9200.c
@@ -409,6 +409,11 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+ platform_set_drvdata(pdev, rtc);
+
sclk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(sclk))
return PTR_ERR(sclk);
@@ -441,13 +446,10 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
if (!device_can_wakeup(&pdev->dev))
device_init_wakeup(&pdev->dev, 1);
- rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
- &at91_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc)) {
- ret = PTR_ERR(rtc);
+ rtc->ops = &at91_rtc_ops;
+ ret = rtc_register_device(rtc);
+ if (ret)
goto err_clk;
- }
- platform_set_drvdata(pdev, rtc);
/* enable SECEV interrupt in order to initialize at91_rtc_upd_rdy
* completion.
diff --git a/drivers/rtc/rtc-brcmstb-waketimer.c b/drivers/rtc/rtc-brcmstb-waketimer.c
new file mode 100644
index 000000000000..796ac792a381
--- /dev/null
+++ b/drivers/rtc/rtc-brcmstb-waketimer.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright © 2014-2017 Broadcom
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_wakeup.h>
+#include <linux/reboot.h>
+#include <linux/rtc.h>
+#include <linux/stat.h>
+#include <linux/suspend.h>
+
+struct brcmstb_waketmr {
+ struct rtc_device *rtc;
+ struct device *dev;
+ void __iomem *base;
+ int irq;
+ struct notifier_block reboot_notifier;
+ struct clk *clk;
+ u32 rate;
+};
+
+#define BRCMSTB_WKTMR_EVENT 0x00
+#define BRCMSTB_WKTMR_COUNTER 0x04
+#define BRCMSTB_WKTMR_ALARM 0x08
+#define BRCMSTB_WKTMR_PRESCALER 0x0C
+#define BRCMSTB_WKTMR_PRESCALER_VAL 0x10
+
+#define BRCMSTB_WKTMR_DEFAULT_FREQ 27000000
+
+static inline void brcmstb_waketmr_clear_alarm(struct brcmstb_waketmr *timer)
+{
+ writel_relaxed(1, timer->base + BRCMSTB_WKTMR_EVENT);
+ (void)readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT);
+}
+
+static void brcmstb_waketmr_set_alarm(struct brcmstb_waketmr *timer,
+ unsigned int secs)
+{
+ brcmstb_waketmr_clear_alarm(timer);
+
+ writel_relaxed(secs + 1, timer->base + BRCMSTB_WKTMR_ALARM);
+}
+
+static irqreturn_t brcmstb_waketmr_irq(int irq, void *data)
+{
+ struct brcmstb_waketmr *timer = data;
+
+ pm_wakeup_event(timer->dev, 0);
+
+ return IRQ_HANDLED;
+}
+
+struct wktmr_time {
+ u32 sec;
+ u32 pre;
+};
+
+static void wktmr_read(struct brcmstb_waketmr *timer,
+ struct wktmr_time *t)
+{
+ u32 tmp;
+
+ do {
+ t->sec = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER);
+ tmp = readl_relaxed(timer->base + BRCMSTB_WKTMR_PRESCALER_VAL);
+ } while (tmp >= timer->rate);
+
+ t->pre = timer->rate - tmp;
+}
+
+static int brcmstb_waketmr_prepare_suspend(struct brcmstb_waketmr *timer)
+{
+ struct device *dev = timer->dev;
+ int ret = 0;
+
+ if (device_may_wakeup(dev)) {
+ ret = enable_irq_wake(timer->irq);
+ if (ret) {
+ dev_err(dev, "failed to enable wake-up interrupt\n");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+/* If enabled as a wakeup-source, arm the timer when powering off */
+static int brcmstb_waketmr_reboot(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct brcmstb_waketmr *timer;
+
+ timer = container_of(nb, struct brcmstb_waketmr, reboot_notifier);
+
+ /* Set timer for cold boot */
+ if (action == SYS_POWER_OFF)
+ brcmstb_waketmr_prepare_suspend(timer);
+
+ return NOTIFY_DONE;
+}
+
+static int brcmstb_waketmr_gettime(struct device *dev,
+ struct rtc_time *tm)
+{
+ struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
+ struct wktmr_time now;
+
+ wktmr_read(timer, &now);
+
+ rtc_time_to_tm(now.sec, tm);
+
+ return 0;
+}
+
+static int brcmstb_waketmr_settime(struct device *dev,
+ struct rtc_time *tm)
+{
+ struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
+ time64_t sec;
+
+ sec = rtc_tm_to_time64(tm);
+
+ if (sec > U32_MAX || sec < 0)
+ return -EINVAL;
+
+ writel_relaxed(sec, timer->base + BRCMSTB_WKTMR_COUNTER);
+
+ return 0;
+}
+
+static int brcmstb_waketmr_getalarm(struct device *dev,
+ struct rtc_wkalrm *alarm)
+{
+ struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
+ time64_t sec;
+ u32 reg;
+
+ sec = readl_relaxed(timer->base + BRCMSTB_WKTMR_ALARM);
+ if (sec != 0) {
+ /* Alarm is enabled */
+ alarm->enabled = 1;
+ rtc_time64_to_tm(sec, &alarm->time);
+ }
+
+ reg = readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT);
+ alarm->pending = !!(reg & 1);
+
+ return 0;
+}
+
+static int brcmstb_waketmr_setalarm(struct device *dev,
+ struct rtc_wkalrm *alarm)
+{
+ struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
+ time64_t sec;
+
+ if (alarm->enabled)
+ sec = rtc_tm_to_time64(&alarm->time);
+ else
+ sec = 0;
+
+ if (sec > U32_MAX || sec < 0)
+ return -EINVAL;
+
+ brcmstb_waketmr_set_alarm(timer, sec);
+
+ return 0;
+}
+
+/*
+ * Does not do much but keep the RTC class happy. We always support
+ * alarms.
+ */
+static int brcmstb_waketmr_alarm_enable(struct device *dev,
+ unsigned int enabled)
+{
+ return 0;
+}
+
+static const struct rtc_class_ops brcmstb_waketmr_ops = {
+ .read_time = brcmstb_waketmr_gettime,
+ .set_time = brcmstb_waketmr_settime,
+ .read_alarm = brcmstb_waketmr_getalarm,
+ .set_alarm = brcmstb_waketmr_setalarm,
+ .alarm_irq_enable = brcmstb_waketmr_alarm_enable,
+};
+
+static int brcmstb_waketmr_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct brcmstb_waketmr *timer;
+ struct resource *res;
+ int ret;
+
+ timer = devm_kzalloc(dev, sizeof(*timer), GFP_KERNEL);
+ if (!timer)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, timer);
+ timer->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ timer->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(timer->base))
+ return PTR_ERR(timer->base);
+
+ /*
+ * Set wakeup capability before requesting wakeup interrupt, so we can
+ * process boot-time "wakeups" (e.g., from S5 soft-off)
+ */
+ device_set_wakeup_capable(dev, true);
+ device_wakeup_enable(dev);
+
+ timer->irq = platform_get_irq(pdev, 0);
+ if (timer->irq < 0)
+ return -ENODEV;
+
+ timer->clk = devm_clk_get(dev, NULL);
+ if (!IS_ERR(timer->clk)) {
+ ret = clk_prepare_enable(timer->clk);
+ if (ret)
+ return ret;
+ timer->rate = clk_get_rate(timer->clk);
+ if (!timer->rate)
+ timer->rate = BRCMSTB_WKTMR_DEFAULT_FREQ;
+ } else {
+ timer->rate = BRCMSTB_WKTMR_DEFAULT_FREQ;
+ timer->clk = NULL;
+ }
+
+ ret = devm_request_irq(dev, timer->irq, brcmstb_waketmr_irq, 0,
+ "brcmstb-waketimer", timer);
+ if (ret < 0)
+ return ret;
+
+ timer->reboot_notifier.notifier_call = brcmstb_waketmr_reboot;
+ register_reboot_notifier(&timer->reboot_notifier);
+
+ timer->rtc = rtc_device_register("brcmstb-waketmr", dev,
+ &brcmstb_waketmr_ops, THIS_MODULE);
+ if (IS_ERR(timer->rtc)) {
+ dev_err(dev, "unable to register device\n");
+ unregister_reboot_notifier(&timer->reboot_notifier);
+ return PTR_ERR(timer->rtc);
+ }
+
+ dev_info(dev, "registered, with irq %d\n", timer->irq);
+
+ return ret;
+}
+
+static int brcmstb_waketmr_remove(struct platform_device *pdev)
+{
+ struct brcmstb_waketmr *timer = dev_get_drvdata(&pdev->dev);
+
+ unregister_reboot_notifier(&timer->reboot_notifier);
+ rtc_device_unregister(timer->rtc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int brcmstb_waketmr_suspend(struct device *dev)
+{
+ struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
+
+ return brcmstb_waketmr_prepare_suspend(timer);
+}
+
+static int brcmstb_waketmr_resume(struct device *dev)
+{
+ struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
+ int ret;
+
+ if (!device_may_wakeup(dev))
+ return 0;
+
+ ret = disable_irq_wake(timer->irq);
+
+ brcmstb_waketmr_clear_alarm(timer);
+
+ return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(brcmstb_waketmr_pm_ops,
+ brcmstb_waketmr_suspend, brcmstb_waketmr_resume);
+
+static const struct of_device_id brcmstb_waketmr_of_match[] = {
+ { .compatible = "brcm,brcmstb-waketimer" },
+ { /* sentinel */ },
+};
+
+static struct platform_driver brcmstb_waketmr_driver = {
+ .probe = brcmstb_waketmr_probe,
+ .remove = brcmstb_waketmr_remove,
+ .driver = {
+ .name = "brcmstb-waketimer",
+ .pm = &brcmstb_waketmr_pm_ops,
+ .of_match_table = of_match_ptr(brcmstb_waketmr_of_match),
+ }
+};
+module_platform_driver(brcmstb_waketmr_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Brian Norris");
+MODULE_AUTHOR("Markus Mayer");
+MODULE_DESCRIPTION("Wake-up timer driver for STB chips");
diff --git a/drivers/rtc/rtc-core.h b/drivers/rtc/rtc-core.h
index 7a4ed2f7c7d7..ecab76a3207c 100644
--- a/drivers/rtc/rtc-core.h
+++ b/drivers/rtc/rtc-core.h
@@ -45,3 +45,11 @@ static inline const struct attribute_group **rtc_get_dev_attribute_groups(void)
return NULL;
}
#endif
+
+#ifdef CONFIG_RTC_NVMEM
+void rtc_nvmem_register(struct rtc_device *rtc);
+void rtc_nvmem_unregister(struct rtc_device *rtc);
+#else
+static inline void rtc_nvmem_register(struct rtc_device *rtc) {}
+static inline void rtc_nvmem_unregister(struct rtc_device *rtc) {}
+#endif
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c
index e81a8711fea7..794bc4fa4937 100644
--- a/drivers/rtc/rtc-dev.c
+++ b/drivers/rtc/rtc-dev.c
@@ -464,7 +464,7 @@ void rtc_dev_prepare(struct rtc_device *rtc)
return;
if (rtc->id >= RTC_DEV_MAX) {
- dev_dbg(&rtc->dev, "%s: too many RTC devices\n", rtc->name);
+ dev_dbg(&rtc->dev, "too many RTC devices\n");
return;
}
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 77339b3d50a1..4fac49e55d47 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -24,6 +24,7 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/clk-provider.h>
+#include <linux/regmap.h>
/*
* We can't determine type by probing, but if we expect pre-Linux code
@@ -33,6 +34,7 @@
*/
enum ds_type {
ds_1307,
+ ds_1308,
ds_1337,
ds_1338,
ds_1339,
@@ -43,6 +45,7 @@ enum ds_type {
m41t00,
mcp794xx,
rx_8025,
+ rx_8130,
last_ds_type /* always last */
/* rs5c372 too? different address... */
};
@@ -115,17 +118,16 @@ struct ds1307 {
u8 offset; /* register's offset */
u8 regs[11];
u16 nvram_offset;
- struct bin_attribute *nvram;
+ struct nvmem_config nvmem_cfg;
enum ds_type type;
unsigned long flags;
#define HAS_NVRAM 0 /* bit 0 == sysfs file active */
#define HAS_ALARM 1 /* bit 1 == irq claimed */
- struct i2c_client *client;
+ struct device *dev;
+ struct regmap *regmap;
+ const char *name;
+ int irq;
struct rtc_device *rtc;
- s32 (*read_block_data)(const struct i2c_client *client, u8 command,
- u8 length, u8 *values);
- s32 (*write_block_data)(const struct i2c_client *client, u8 command,
- u8 length, const u8 *values);
#ifdef CONFIG_COMMON_CLK
struct clk_hw clks[2];
#endif
@@ -135,21 +137,30 @@ struct chip_desc {
unsigned alarm:1;
u16 nvram_offset;
u16 nvram_size;
+ u8 century_reg;
+ u8 century_enable_bit;
+ u8 century_bit;
u16 trickle_charger_reg;
u8 trickle_charger_setup;
- u8 (*do_trickle_setup)(struct i2c_client *, uint32_t, bool);
+ u8 (*do_trickle_setup)(struct ds1307 *, uint32_t,
+ bool);
};
-static u8 do_trickle_setup_ds1339(struct i2c_client *,
- uint32_t ohms, bool diode);
+static u8 do_trickle_setup_ds1339(struct ds1307 *, uint32_t ohms, bool diode);
static struct chip_desc chips[last_ds_type] = {
[ds_1307] = {
.nvram_offset = 8,
.nvram_size = 56,
},
+ [ds_1308] = {
+ .nvram_offset = 8,
+ .nvram_size = 56,
+ },
[ds_1337] = {
.alarm = 1,
+ .century_reg = DS1307_REG_MONTH,
+ .century_bit = DS1337_BIT_CENTURY,
},
[ds_1338] = {
.nvram_offset = 8,
@@ -157,10 +168,15 @@ static struct chip_desc chips[last_ds_type] = {
},
[ds_1339] = {
.alarm = 1,
+ .century_reg = DS1307_REG_MONTH,
+ .century_bit = DS1337_BIT_CENTURY,
.trickle_charger_reg = 0x10,
.do_trickle_setup = &do_trickle_setup_ds1339,
},
[ds_1340] = {
+ .century_reg = DS1307_REG_HOUR,
+ .century_enable_bit = DS1340_BIT_CENTURY_EN,
+ .century_bit = DS1340_BIT_CENTURY,
.trickle_charger_reg = 0x08,
},
[ds_1388] = {
@@ -168,6 +184,14 @@ static struct chip_desc chips[last_ds_type] = {
},
[ds_3231] = {
.alarm = 1,
+ .century_reg = DS1307_REG_MONTH,
+ .century_bit = DS1337_BIT_CENTURY,
+ },
+ [rx_8130] = {
+ .alarm = 1,
+ /* this is battery backed SRAM */
+ .nvram_offset = 0x20,
+ .nvram_size = 4, /* 32bit (4 word x 8 bit) */
},
[mcp794xx] = {
.alarm = 1,
@@ -179,6 +203,7 @@ static struct chip_desc chips[last_ds_type] = {
static const struct i2c_device_id ds1307_id[] = {
{ "ds1307", ds_1307 },
+ { "ds1308", ds_1308 },
{ "ds1337", ds_1337 },
{ "ds1338", ds_1338 },
{ "ds1339", ds_1339 },
@@ -192,6 +217,7 @@ static const struct i2c_device_id ds1307_id[] = {
{ "pt7c4338", ds_1307 },
{ "rx8025", rx_8025 },
{ "isl12057", ds_1337 },
+ { "rx8130", rx_8130 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ds1307_id);
@@ -203,6 +229,10 @@ static const struct of_device_id ds1307_of_match[] = {
.data = (void *)ds_1307
},
{
+ .compatible = "dallas,ds1308",
+ .data = (void *)ds_1308
+ },
+ {
.compatible = "dallas,ds1337",
.data = (void *)ds_1337
},
@@ -262,6 +292,7 @@ MODULE_DEVICE_TABLE(of, ds1307_of_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id ds1307_acpi_ids[] = {
{ .id = "DS1307", .driver_data = ds_1307 },
+ { .id = "DS1308", .driver_data = ds_1308 },
{ .id = "DS1337", .driver_data = ds_1337 },
{ .id = "DS1338", .driver_data = ds_1338 },
{ .id = "DS1339", .driver_data = ds_1339 },
@@ -280,136 +311,6 @@ static const struct acpi_device_id ds1307_acpi_ids[] = {
MODULE_DEVICE_TABLE(acpi, ds1307_acpi_ids);
#endif
-/*----------------------------------------------------------------------*/
-
-#define BLOCK_DATA_MAX_TRIES 10
-
-static s32 ds1307_read_block_data_once(const struct i2c_client *client,
- u8 command, u8 length, u8 *values)
-{
- s32 i, data;
-
- for (i = 0; i < length; i++) {
- data = i2c_smbus_read_byte_data(client, command + i);
- if (data < 0)
- return data;
- values[i] = data;
- }
- return i;
-}
-
-static s32 ds1307_read_block_data(const struct i2c_client *client, u8 command,
- u8 length, u8 *values)
-{
- u8 oldvalues[255];
- s32 ret;
- int tries = 0;
-
- dev_dbg(&client->dev, "ds1307_read_block_data (length=%d)\n", length);
- ret = ds1307_read_block_data_once(client, command, length, values);
- if (ret < 0)
- return ret;
- do {
- if (++tries > BLOCK_DATA_MAX_TRIES) {
- dev_err(&client->dev,
- "ds1307_read_block_data failed\n");
- return -EIO;
- }
- memcpy(oldvalues, values, length);
- ret = ds1307_read_block_data_once(client, command, length,
- values);
- if (ret < 0)
- return ret;
- } while (memcmp(oldvalues, values, length));
- return length;
-}
-
-static s32 ds1307_write_block_data(const struct i2c_client *client, u8 command,
- u8 length, const u8 *values)
-{
- u8 currvalues[255];
- int tries = 0;
-
- dev_dbg(&client->dev, "ds1307_write_block_data (length=%d)\n", length);
- do {
- s32 i, ret;
-
- if (++tries > BLOCK_DATA_MAX_TRIES) {
- dev_err(&client->dev,
- "ds1307_write_block_data failed\n");
- return -EIO;
- }
- for (i = 0; i < length; i++) {
- ret = i2c_smbus_write_byte_data(client, command + i,
- values[i]);
- if (ret < 0)
- return ret;
- }
- ret = ds1307_read_block_data_once(client, command, length,
- currvalues);
- if (ret < 0)
- return ret;
- } while (memcmp(currvalues, values, length));
- return length;
-}
-
-/*----------------------------------------------------------------------*/
-
-/* These RTC devices are not designed to be connected to a SMbus adapter.
- SMbus limits block operations length to 32 bytes, whereas it's not
- limited on I2C buses. As a result, accesses may exceed 32 bytes;
- in that case, split them into smaller blocks */
-
-static s32 ds1307_native_smbus_write_block_data(const struct i2c_client *client,
- u8 command, u8 length, const u8 *values)
-{
- u8 suboffset = 0;
-
- if (length <= I2C_SMBUS_BLOCK_MAX) {
- s32 retval = i2c_smbus_write_i2c_block_data(client,
- command, length, values);
- if (retval < 0)
- return retval;
- return length;
- }
-
- while (suboffset < length) {
- s32 retval = i2c_smbus_write_i2c_block_data(client,
- command + suboffset,
- min(I2C_SMBUS_BLOCK_MAX, length - suboffset),
- values + suboffset);
- if (retval < 0)
- return retval;
-
- suboffset += I2C_SMBUS_BLOCK_MAX;
- }
- return length;
-}
-
-static s32 ds1307_native_smbus_read_block_data(const struct i2c_client *client,
- u8 command, u8 length, u8 *values)
-{
- u8 suboffset = 0;
-
- if (length <= I2C_SMBUS_BLOCK_MAX)
- return i2c_smbus_read_i2c_block_data(client,
- command, length, values);
-
- while (suboffset < length) {
- s32 retval = i2c_smbus_read_i2c_block_data(client,
- command + suboffset,
- min(I2C_SMBUS_BLOCK_MAX, length - suboffset),
- values + suboffset);
- if (retval < 0)
- return retval;
-
- suboffset += I2C_SMBUS_BLOCK_MAX;
- }
- return length;
-}
-
-/*----------------------------------------------------------------------*/
-
/*
* The ds1337 and ds1339 both have two alarms, but we only use the first
* one (with a "seconds" field). For ds1337 we expect nINTA is our alarm
@@ -417,27 +318,24 @@ static s32 ds1307_native_smbus_read_block_data(const struct i2c_client *client,
*/
static irqreturn_t ds1307_irq(int irq, void *dev_id)
{
- struct i2c_client *client = dev_id;
- struct ds1307 *ds1307 = i2c_get_clientdata(client);
+ struct ds1307 *ds1307 = dev_id;
struct mutex *lock = &ds1307->rtc->ops_lock;
- int stat, control;
+ int stat, ret;
mutex_lock(lock);
- stat = i2c_smbus_read_byte_data(client, DS1337_REG_STATUS);
- if (stat < 0)
+ ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &stat);
+ if (ret)
goto out;
if (stat & DS1337_BIT_A1I) {
stat &= ~DS1337_BIT_A1I;
- i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, stat);
+ regmap_write(ds1307->regmap, DS1337_REG_STATUS, stat);
- control = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL);
- if (control < 0)
+ ret = regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL,
+ DS1337_BIT_A1IE, 0);
+ if (ret)
goto out;
- control &= ~DS1337_BIT_A1IE;
- i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, control);
-
rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
}
@@ -452,14 +350,14 @@ out:
static int ds1307_get_time(struct device *dev, struct rtc_time *t)
{
struct ds1307 *ds1307 = dev_get_drvdata(dev);
- int tmp;
+ int tmp, ret;
+ const struct chip_desc *chip = &chips[ds1307->type];
/* read the RTC date and time registers all at once */
- tmp = ds1307->read_block_data(ds1307->client,
- ds1307->offset, 7, ds1307->regs);
- if (tmp != 7) {
- dev_err(dev, "%s error %d\n", "read", tmp);
- return -EIO;
+ ret = regmap_bulk_read(ds1307->regmap, ds1307->offset, ds1307->regs, 7);
+ if (ret) {
+ dev_err(dev, "%s error %d\n", "read", ret);
+ return ret;
}
dev_dbg(dev, "%s: %7ph\n", "read", ds1307->regs);
@@ -481,22 +379,9 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
t->tm_mon = bcd2bin(tmp) - 1;
t->tm_year = bcd2bin(ds1307->regs[DS1307_REG_YEAR]) + 100;
-#ifdef CONFIG_RTC_DRV_DS1307_CENTURY
- switch (ds1307->type) {
- case ds_1337:
- case ds_1339:
- case ds_3231:
- if (ds1307->regs[DS1307_REG_MONTH] & DS1337_BIT_CENTURY)
- t->tm_year += 100;
- break;
- case ds_1340:
- if (ds1307->regs[DS1307_REG_HOUR] & DS1340_BIT_CENTURY)
- t->tm_year += 100;
- break;
- default:
- break;
- }
-#endif
+ if (ds1307->regs[chip->century_reg] & chip->century_bit &&
+ IS_ENABLED(CONFIG_RTC_DRV_DS1307_CENTURY))
+ t->tm_year += 100;
dev_dbg(dev, "%s secs=%d, mins=%d, "
"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
@@ -511,6 +396,7 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
static int ds1307_set_time(struct device *dev, struct rtc_time *t)
{
struct ds1307 *ds1307 = dev_get_drvdata(dev);
+ const struct chip_desc *chip = &chips[ds1307->type];
int result;
int tmp;
u8 *buf = ds1307->regs;
@@ -521,24 +407,14 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
t->tm_hour, t->tm_mday,
t->tm_mon, t->tm_year, t->tm_wday);
-#ifdef CONFIG_RTC_DRV_DS1307_CENTURY
if (t->tm_year < 100)
return -EINVAL;
- switch (ds1307->type) {
- case ds_1337:
- case ds_1339:
- case ds_3231:
- case ds_1340:
- if (t->tm_year > 299)
- return -EINVAL;
- default:
- if (t->tm_year > 199)
- return -EINVAL;
- break;
- }
+#ifdef CONFIG_RTC_DRV_DS1307_CENTURY
+ if (t->tm_year > (chip->century_bit ? 299 : 199))
+ return -EINVAL;
#else
- if (t->tm_year < 100 || t->tm_year > 199)
+ if (t->tm_year > 199)
return -EINVAL;
#endif
@@ -553,19 +429,12 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
tmp = t->tm_year - 100;
buf[DS1307_REG_YEAR] = bin2bcd(tmp);
- switch (ds1307->type) {
- case ds_1337:
- case ds_1339:
- case ds_3231:
- if (t->tm_year > 199)
- buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY;
- break;
- case ds_1340:
- buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN;
- if (t->tm_year > 199)
- buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY;
- break;
- case mcp794xx:
+ if (chip->century_enable_bit)
+ buf[chip->century_reg] |= chip->century_enable_bit;
+ if (t->tm_year > 199 && chip->century_bit)
+ buf[chip->century_reg] |= chip->century_bit;
+
+ if (ds1307->type == mcp794xx) {
/*
* these bits were cleared when preparing the date/time
* values and need to be set again before writing the
@@ -573,16 +442,12 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
*/
buf[DS1307_REG_SECS] |= MCP794XX_BIT_ST;
buf[DS1307_REG_WDAY] |= MCP794XX_BIT_VBATEN;
- break;
- default:
- break;
}
dev_dbg(dev, "%s: %7ph\n", "write", buf);
- result = ds1307->write_block_data(ds1307->client,
- ds1307->offset, 7, buf);
- if (result < 0) {
+ result = regmap_bulk_write(ds1307->regmap, ds1307->offset, buf, 7);
+ if (result) {
dev_err(dev, "%s error %d\n", "write", result);
return result;
}
@@ -591,19 +456,18 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct ds1307 *ds1307 = i2c_get_clientdata(client);
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
int ret;
if (!test_bit(HAS_ALARM, &ds1307->flags))
return -EINVAL;
/* read all ALARM1, ALARM2, and status registers at once */
- ret = ds1307->read_block_data(client,
- DS1339_REG_ALARM1_SECS, 9, ds1307->regs);
- if (ret != 9) {
+ ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS,
+ ds1307->regs, 9);
+ if (ret) {
dev_err(dev, "%s error %d\n", "alarm read", ret);
- return -EIO;
+ return ret;
}
dev_dbg(dev, "%s: %4ph, %3ph, %2ph\n", "alarm read",
@@ -633,8 +497,7 @@ static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t)
static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct ds1307 *ds1307 = i2c_get_clientdata(client);
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
unsigned char *buf = ds1307->regs;
u8 control, status;
int ret;
@@ -649,11 +512,10 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t)
t->enabled, t->pending);
/* read current status of both alarms and the chip */
- ret = ds1307->read_block_data(client,
- DS1339_REG_ALARM1_SECS, 9, buf);
- if (ret != 9) {
+ ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, buf, 9);
+ if (ret) {
dev_err(dev, "%s error %d\n", "alarm write", ret);
- return -EIO;
+ return ret;
}
control = ds1307->regs[7];
status = ds1307->regs[8];
@@ -676,9 +538,8 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t)
buf[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE);
buf[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I);
- ret = ds1307->write_block_data(client,
- DS1339_REG_ALARM1_SECS, 9, buf);
- if (ret < 0) {
+ ret = regmap_bulk_write(ds1307->regmap, DS1339_REG_ALARM1_SECS, buf, 9);
+ if (ret) {
dev_err(dev, "can't set alarm time\n");
return ret;
}
@@ -687,7 +548,7 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t)
if (t->enabled) {
dev_dbg(dev, "alarm IRQ armed\n");
buf[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */
- i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, buf[7]);
+ regmap_write(ds1307->regmap, DS1337_REG_CONTROL, buf[7]);
}
return 0;
@@ -695,35 +556,181 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t)
static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct ds1307 *ds1307 = i2c_get_clientdata(client);
- int ret;
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
if (!test_bit(HAS_ALARM, &ds1307->flags))
return -ENOTTY;
- ret = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL);
+ return regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL,
+ DS1337_BIT_A1IE,
+ enabled ? DS1337_BIT_A1IE : 0);
+}
+
+static const struct rtc_class_ops ds13xx_rtc_ops = {
+ .read_time = ds1307_get_time,
+ .set_time = ds1307_set_time,
+ .read_alarm = ds1337_read_alarm,
+ .set_alarm = ds1337_set_alarm,
+ .alarm_irq_enable = ds1307_alarm_irq_enable,
+};
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Alarm support for rx8130 devices.
+ */
+
+#define RX8130_REG_ALARM_MIN 0x07
+#define RX8130_REG_ALARM_HOUR 0x08
+#define RX8130_REG_ALARM_WEEK_OR_DAY 0x09
+#define RX8130_REG_EXTENSION 0x0c
+#define RX8130_REG_EXTENSION_WADA (1 << 3)
+#define RX8130_REG_FLAG 0x0d
+#define RX8130_REG_FLAG_AF (1 << 3)
+#define RX8130_REG_CONTROL0 0x0e
+#define RX8130_REG_CONTROL0_AIE (1 << 3)
+
+static irqreturn_t rx8130_irq(int irq, void *dev_id)
+{
+ struct ds1307 *ds1307 = dev_id;
+ struct mutex *lock = &ds1307->rtc->ops_lock;
+ u8 ctl[3];
+ int ret;
+
+ mutex_lock(lock);
+
+ /* Read control registers. */
+ ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3);
if (ret < 0)
- return ret;
+ goto out;
+ if (!(ctl[1] & RX8130_REG_FLAG_AF))
+ goto out;
+ ctl[1] &= ~RX8130_REG_FLAG_AF;
+ ctl[2] &= ~RX8130_REG_CONTROL0_AIE;
- if (enabled)
- ret |= DS1337_BIT_A1IE;
- else
- ret &= ~DS1337_BIT_A1IE;
+ ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3);
+ if (ret < 0)
+ goto out;
+
+ rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
+
+out:
+ mutex_unlock(lock);
+
+ return IRQ_HANDLED;
+}
- ret = i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, ret);
+static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
+ u8 ald[3], ctl[3];
+ int ret;
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -EINVAL;
+
+ /* Read alarm registers. */
+ ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, 3);
if (ret < 0)
return ret;
+ /* Read control registers. */
+ ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3);
+ if (ret < 0)
+ return ret;
+
+ t->enabled = !!(ctl[2] & RX8130_REG_CONTROL0_AIE);
+ t->pending = !!(ctl[1] & RX8130_REG_FLAG_AF);
+
+ /* Report alarm 0 time assuming 24-hour and day-of-month modes. */
+ t->time.tm_sec = -1;
+ t->time.tm_min = bcd2bin(ald[0] & 0x7f);
+ t->time.tm_hour = bcd2bin(ald[1] & 0x7f);
+ t->time.tm_wday = -1;
+ t->time.tm_mday = bcd2bin(ald[2] & 0x7f);
+ t->time.tm_mon = -1;
+ t->time.tm_year = -1;
+ t->time.tm_yday = -1;
+ t->time.tm_isdst = -1;
+
+ dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d enabled=%d\n",
+ __func__, t->time.tm_sec, t->time.tm_min, t->time.tm_hour,
+ t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled);
+
return 0;
}
-static const struct rtc_class_ops ds13xx_rtc_ops = {
+static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
+ u8 ald[3], ctl[3];
+ int ret;
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -EINVAL;
+
+ dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d "
+ "enabled=%d pending=%d\n", __func__,
+ t->time.tm_sec, t->time.tm_min, t->time.tm_hour,
+ t->time.tm_wday, t->time.tm_mday, t->time.tm_mon,
+ t->enabled, t->pending);
+
+ /* Read control registers. */
+ ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3);
+ if (ret < 0)
+ return ret;
+
+ ctl[0] &= ~RX8130_REG_EXTENSION_WADA;
+ ctl[1] |= RX8130_REG_FLAG_AF;
+ ctl[2] &= ~RX8130_REG_CONTROL0_AIE;
+
+ ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3);
+ if (ret < 0)
+ return ret;
+
+ /* Hardware alarm precision is 1 minute! */
+ ald[0] = bin2bcd(t->time.tm_min);
+ ald[1] = bin2bcd(t->time.tm_hour);
+ ald[2] = bin2bcd(t->time.tm_mday);
+
+ ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, 3);
+ if (ret < 0)
+ return ret;
+
+ if (!t->enabled)
+ return 0;
+
+ ctl[2] |= RX8130_REG_CONTROL0_AIE;
+
+ return regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3);
+}
+
+static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
+ int ret, reg;
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -EINVAL;
+
+ ret = regmap_read(ds1307->regmap, RX8130_REG_CONTROL0, &reg);
+ if (ret < 0)
+ return ret;
+
+ if (enabled)
+ reg |= RX8130_REG_CONTROL0_AIE;
+ else
+ reg &= ~RX8130_REG_CONTROL0_AIE;
+
+ return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, reg);
+}
+
+static const struct rtc_class_ops rx8130_rtc_ops = {
.read_time = ds1307_get_time,
.set_time = ds1307_set_time,
- .read_alarm = ds1337_read_alarm,
- .set_alarm = ds1337_set_alarm,
- .alarm_irq_enable = ds1307_alarm_irq_enable,
+ .read_alarm = rx8130_read_alarm,
+ .set_alarm = rx8130_set_alarm,
+ .alarm_irq_enable = rx8130_alarm_irq_enable,
};
/*----------------------------------------------------------------------*/
@@ -752,31 +759,27 @@ static const struct rtc_class_ops ds13xx_rtc_ops = {
static irqreturn_t mcp794xx_irq(int irq, void *dev_id)
{
- struct i2c_client *client = dev_id;
- struct ds1307 *ds1307 = i2c_get_clientdata(client);
+ struct ds1307 *ds1307 = dev_id;
struct mutex *lock = &ds1307->rtc->ops_lock;
int reg, ret;
mutex_lock(lock);
/* Check and clear alarm 0 interrupt flag. */
- reg = i2c_smbus_read_byte_data(client, MCP794XX_REG_ALARM0_CTRL);
- if (reg < 0)
+ ret = regmap_read(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, &reg);
+ if (ret)
goto out;
if (!(reg & MCP794XX_BIT_ALMX_IF))
goto out;
reg &= ~MCP794XX_BIT_ALMX_IF;
- ret = i2c_smbus_write_byte_data(client, MCP794XX_REG_ALARM0_CTRL, reg);
- if (ret < 0)
+ ret = regmap_write(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, reg);
+ if (ret)
goto out;
/* Disable alarm 0. */
- reg = i2c_smbus_read_byte_data(client, MCP794XX_REG_CONTROL);
- if (reg < 0)
- goto out;
- reg &= ~MCP794XX_BIT_ALM0_EN;
- ret = i2c_smbus_write_byte_data(client, MCP794XX_REG_CONTROL, reg);
- if (ret < 0)
+ ret = regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL,
+ MCP794XX_BIT_ALM0_EN, 0);
+ if (ret)
goto out;
rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
@@ -789,8 +792,7 @@ out:
static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct ds1307 *ds1307 = i2c_get_clientdata(client);
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
u8 *regs = ds1307->regs;
int ret;
@@ -798,8 +800,8 @@ static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t)
return -EINVAL;
/* Read control and alarm 0 registers. */
- ret = ds1307->read_block_data(client, MCP794XX_REG_CONTROL, 10, regs);
- if (ret < 0)
+ ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, 10);
+ if (ret)
return ret;
t->enabled = !!(regs[0] & MCP794XX_BIT_ALM0_EN);
@@ -828,8 +830,7 @@ static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t)
static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct ds1307 *ds1307 = i2c_get_clientdata(client);
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
unsigned char *regs = ds1307->regs;
int ret;
@@ -843,8 +844,8 @@ static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t)
t->enabled, t->pending);
/* Read control and alarm 0 registers. */
- ret = ds1307->read_block_data(client, MCP794XX_REG_CONTROL, 10, regs);
- if (ret < 0)
+ ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, 10);
+ if (ret)
return ret;
/* Set alarm 0, using 24-hour and day-of-month modes. */
@@ -862,35 +863,26 @@ static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t)
/* Disable interrupt. We will not enable until completely programmed */
regs[0] &= ~MCP794XX_BIT_ALM0_EN;
- ret = ds1307->write_block_data(client, MCP794XX_REG_CONTROL, 10, regs);
- if (ret < 0)
+ ret = regmap_bulk_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs, 10);
+ if (ret)
return ret;
if (!t->enabled)
return 0;
regs[0] |= MCP794XX_BIT_ALM0_EN;
- return i2c_smbus_write_byte_data(client, MCP794XX_REG_CONTROL, regs[0]);
+ return regmap_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs[0]);
}
static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct ds1307 *ds1307 = i2c_get_clientdata(client);
- int reg;
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
if (!test_bit(HAS_ALARM, &ds1307->flags))
return -EINVAL;
- reg = i2c_smbus_read_byte_data(client, MCP794XX_REG_CONTROL);
- if (reg < 0)
- return reg;
-
- if (enabled)
- reg |= MCP794XX_BIT_ALM0_EN;
- else
- reg &= ~MCP794XX_BIT_ALM0_EN;
-
- return i2c_smbus_write_byte_data(client, MCP794XX_REG_CONTROL, reg);
+ return regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL,
+ MCP794XX_BIT_ALM0_EN,
+ enabled ? MCP794XX_BIT_ALM0_EN : 0);
}
static const struct rtc_class_ops mcp794xx_rtc_ops = {
@@ -903,50 +895,27 @@ static const struct rtc_class_ops mcp794xx_rtc_ops = {
/*----------------------------------------------------------------------*/
-static ssize_t
-ds1307_nvram_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr,
- char *buf, loff_t off, size_t count)
+static int ds1307_nvram_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
{
- struct i2c_client *client;
- struct ds1307 *ds1307;
- int result;
+ struct ds1307 *ds1307 = priv;
- client = kobj_to_i2c_client(kobj);
- ds1307 = i2c_get_clientdata(client);
-
- result = ds1307->read_block_data(client, ds1307->nvram_offset + off,
- count, buf);
- if (result < 0)
- dev_err(&client->dev, "%s error %d\n", "nvram read", result);
- return result;
+ return regmap_bulk_read(ds1307->regmap, ds1307->nvram_offset + offset,
+ val, bytes);
}
-static ssize_t
-ds1307_nvram_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr,
- char *buf, loff_t off, size_t count)
+static int ds1307_nvram_write(void *priv, unsigned int offset, void *val,
+ size_t bytes)
{
- struct i2c_client *client;
- struct ds1307 *ds1307;
- int result;
+ struct ds1307 *ds1307 = priv;
- client = kobj_to_i2c_client(kobj);
- ds1307 = i2c_get_clientdata(client);
-
- result = ds1307->write_block_data(client, ds1307->nvram_offset + off,
- count, buf);
- if (result < 0) {
- dev_err(&client->dev, "%s error %d\n", "nvram write", result);
- return result;
- }
- return count;
+ return regmap_bulk_write(ds1307->regmap, ds1307->nvram_offset + offset,
+ val, bytes);
}
-
/*----------------------------------------------------------------------*/
-static u8 do_trickle_setup_ds1339(struct i2c_client *client,
+static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307,
uint32_t ohms, bool diode)
{
u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE :
@@ -963,14 +932,14 @@ static u8 do_trickle_setup_ds1339(struct i2c_client *client,
setup |= DS1307_TRICKLE_CHARGER_4K_OHM;
break;
default:
- dev_warn(&client->dev,
+ dev_warn(ds1307->dev,
"Unsupported ohm value %u in dt\n", ohms);
return 0;
}
return setup;
}
-static void ds1307_trickle_init(struct i2c_client *client,
+static void ds1307_trickle_init(struct ds1307 *ds1307,
struct chip_desc *chip)
{
uint32_t ohms = 0;
@@ -978,11 +947,12 @@ static void ds1307_trickle_init(struct i2c_client *client,
if (!chip->do_trickle_setup)
goto out;
- if (device_property_read_u32(&client->dev, "trickle-resistor-ohms", &ohms))
+ if (device_property_read_u32(ds1307->dev, "trickle-resistor-ohms",
+ &ohms))
goto out;
- if (device_property_read_bool(&client->dev, "trickle-diode-disable"))
+ if (device_property_read_bool(ds1307->dev, "trickle-diode-disable"))
diode = false;
- chip->trickle_charger_setup = chip->do_trickle_setup(client,
+ chip->trickle_charger_setup = chip->do_trickle_setup(ds1307,
ohms, diode);
out:
return;
@@ -1009,13 +979,10 @@ static int ds3231_hwmon_read_temp(struct device *dev, s32 *mC)
s16 temp;
int ret;
- ret = ds1307->read_block_data(ds1307->client, DS3231_REG_TEMPERATURE,
- sizeof(temp_buf), temp_buf);
- if (ret < 0)
+ ret = regmap_bulk_read(ds1307->regmap, DS3231_REG_TEMPERATURE,
+ temp_buf, sizeof(temp_buf));
+ if (ret)
return ret;
- if (ret != sizeof(temp_buf))
- return -EIO;
-
/*
* Temperature is represented as a 10-bit code with a resolution of
* 0.25 degree celsius and encoded in two's complement format.
@@ -1055,12 +1022,11 @@ static void ds1307_hwmon_register(struct ds1307 *ds1307)
if (ds1307->type != ds_3231)
return;
- dev = devm_hwmon_device_register_with_groups(&ds1307->client->dev,
- ds1307->client->name,
+ dev = devm_hwmon_device_register_with_groups(ds1307->dev, ds1307->name,
ds1307, ds3231_hwmon_groups);
if (IS_ERR(dev)) {
- dev_warn(&ds1307->client->dev,
- "unable to register hwmon device %ld\n", PTR_ERR(dev));
+ dev_warn(ds1307->dev, "unable to register hwmon device %ld\n",
+ PTR_ERR(dev));
}
}
@@ -1099,24 +1065,12 @@ static int ds3231_clk_sqw_rates[] = {
static int ds1337_write_control(struct ds1307 *ds1307, u8 mask, u8 value)
{
- struct i2c_client *client = ds1307->client;
struct mutex *lock = &ds1307->rtc->ops_lock;
- int control;
int ret;
mutex_lock(lock);
-
- control = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL);
- if (control < 0) {
- ret = control;
- goto out;
- }
-
- control &= ~mask;
- control |= value;
-
- ret = i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, control);
-out:
+ ret = regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL,
+ mask, value);
mutex_unlock(lock);
return ret;
@@ -1126,12 +1080,12 @@ static unsigned long ds3231_clk_sqw_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
- int control;
+ int control, ret;
int rate_sel = 0;
- control = i2c_smbus_read_byte_data(ds1307->client, DS1337_REG_CONTROL);
- if (control < 0)
- return control;
+ ret = regmap_read(ds1307->regmap, DS1337_REG_CONTROL, &control);
+ if (ret)
+ return ret;
if (control & DS1337_BIT_RS1)
rate_sel += 1;
if (control & DS1337_BIT_RS2)
@@ -1195,11 +1149,11 @@ static void ds3231_clk_sqw_unprepare(struct clk_hw *hw)
static int ds3231_clk_sqw_is_prepared(struct clk_hw *hw)
{
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
- int control;
+ int control, ret;
- control = i2c_smbus_read_byte_data(ds1307->client, DS1337_REG_CONTROL);
- if (control < 0)
- return control;
+ ret = regmap_read(ds1307->regmap, DS1337_REG_CONTROL, &control);
+ if (ret)
+ return ret;
return !(control & DS1337_BIT_INTCN);
}
@@ -1221,26 +1175,13 @@ static unsigned long ds3231_clk_32khz_recalc_rate(struct clk_hw *hw,
static int ds3231_clk_32khz_control(struct ds1307 *ds1307, bool enable)
{
- struct i2c_client *client = ds1307->client;
struct mutex *lock = &ds1307->rtc->ops_lock;
- int status;
int ret;
mutex_lock(lock);
-
- status = i2c_smbus_read_byte_data(client, DS1337_REG_STATUS);
- if (status < 0) {
- ret = status;
- goto out;
- }
-
- if (enable)
- status |= DS3231_BIT_EN32KHZ;
- else
- status &= ~DS3231_BIT_EN32KHZ;
-
- ret = i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, status);
-out:
+ ret = regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS,
+ DS3231_BIT_EN32KHZ,
+ enable ? DS3231_BIT_EN32KHZ : 0);
mutex_unlock(lock);
return ret;
@@ -1263,11 +1204,11 @@ static void ds3231_clk_32khz_unprepare(struct clk_hw *hw)
static int ds3231_clk_32khz_is_prepared(struct clk_hw *hw)
{
struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw);
- int status;
+ int status, ret;
- status = i2c_smbus_read_byte_data(ds1307->client, DS1337_REG_STATUS);
- if (status < 0)
- return status;
+ ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &status);
+ if (ret)
+ return ret;
return !!(status & DS3231_BIT_EN32KHZ);
}
@@ -1292,18 +1233,17 @@ static struct clk_init_data ds3231_clks_init[] = {
static int ds3231_clks_register(struct ds1307 *ds1307)
{
- struct i2c_client *client = ds1307->client;
- struct device_node *node = client->dev.of_node;
+ struct device_node *node = ds1307->dev->of_node;
struct clk_onecell_data *onecell;
int i;
- onecell = devm_kzalloc(&client->dev, sizeof(*onecell), GFP_KERNEL);
+ onecell = devm_kzalloc(ds1307->dev, sizeof(*onecell), GFP_KERNEL);
if (!onecell)
return -ENOMEM;
onecell->clk_num = ARRAY_SIZE(ds3231_clks_init);
- onecell->clks = devm_kcalloc(&client->dev, onecell->clk_num,
- sizeof(onecell->clks[0]), GFP_KERNEL);
+ onecell->clks = devm_kcalloc(ds1307->dev, onecell->clk_num,
+ sizeof(onecell->clks[0]), GFP_KERNEL);
if (!onecell->clks)
return -ENOMEM;
@@ -1322,8 +1262,8 @@ static int ds3231_clks_register(struct ds1307 *ds1307)
&init.name);
ds1307->clks[i].init = &init;
- onecell->clks[i] = devm_clk_register(&client->dev,
- &ds1307->clks[i]);
+ onecell->clks[i] = devm_clk_register(ds1307->dev,
+ &ds1307->clks[i]);
if (IS_ERR(onecell->clks[i]))
return PTR_ERR(onecell->clks[i]);
}
@@ -1345,8 +1285,8 @@ static void ds1307_clks_register(struct ds1307 *ds1307)
ret = ds3231_clks_register(ds1307);
if (ret) {
- dev_warn(&ds1307->client->dev,
- "unable to register clock device %d\n", ret);
+ dev_warn(ds1307->dev, "unable to register clock device %d\n",
+ ret);
}
}
@@ -1358,6 +1298,12 @@ static void ds1307_clks_register(struct ds1307 *ds1307)
#endif /* CONFIG_COMMON_CLK */
+static const struct regmap_config regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x12,
+};
+
static int ds1307_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -1365,7 +1311,6 @@ static int ds1307_probe(struct i2c_client *client,
int err = -ENODEV;
int tmp, wday;
struct chip_desc *chip;
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
bool want_irq = false;
bool ds1307_can_wakeup_device = false;
unsigned char *buf;
@@ -1382,17 +1327,22 @@ static int ds1307_probe(struct i2c_client *client,
};
const struct rtc_class_ops *rtc_ops = &ds13xx_rtc_ops;
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)
- && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
- return -EIO;
-
ds1307 = devm_kzalloc(&client->dev, sizeof(struct ds1307), GFP_KERNEL);
if (!ds1307)
return -ENOMEM;
- i2c_set_clientdata(client, ds1307);
+ dev_set_drvdata(&client->dev, ds1307);
+ ds1307->dev = &client->dev;
+ ds1307->name = client->name;
+ ds1307->irq = client->irq;
- ds1307->client = client;
+ ds1307->regmap = devm_regmap_init_i2c(client, &regmap_config);
+ if (IS_ERR(ds1307->regmap)) {
+ dev_err(ds1307->dev, "regmap allocation failed\n");
+ return PTR_ERR(ds1307->regmap);
+ }
+
+ i2c_set_clientdata(client, ds1307);
if (client->dev.of_node) {
ds1307->type = (enum ds_type)
@@ -1405,7 +1355,7 @@ static int ds1307_probe(struct i2c_client *client,
const struct acpi_device_id *acpi_id;
acpi_id = acpi_match_device(ACPI_PTR(ds1307_acpi_ids),
- &client->dev);
+ ds1307->dev);
if (!acpi_id)
return -ENODEV;
chip = &chips[acpi_id->driver_data];
@@ -1413,27 +1363,21 @@ static int ds1307_probe(struct i2c_client *client,
}
if (!pdata)
- ds1307_trickle_init(client, chip);
+ ds1307_trickle_init(ds1307, chip);
else if (pdata->trickle_charger_setup)
chip->trickle_charger_setup = pdata->trickle_charger_setup;
if (chip->trickle_charger_setup && chip->trickle_charger_reg) {
- dev_dbg(&client->dev, "writing trickle charger info 0x%x to 0x%x\n",
+ dev_dbg(ds1307->dev,
+ "writing trickle charger info 0x%x to 0x%x\n",
DS13XX_TRICKLE_CHARGER_MAGIC | chip->trickle_charger_setup,
chip->trickle_charger_reg);
- i2c_smbus_write_byte_data(client, chip->trickle_charger_reg,
+ regmap_write(ds1307->regmap, chip->trickle_charger_reg,
DS13XX_TRICKLE_CHARGER_MAGIC |
chip->trickle_charger_setup);
}
buf = ds1307->regs;
- if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {
- ds1307->read_block_data = ds1307_native_smbus_read_block_data;
- ds1307->write_block_data = ds1307_native_smbus_write_block_data;
- } else {
- ds1307->read_block_data = ds1307_read_block_data;
- ds1307->write_block_data = ds1307_write_block_data;
- }
#ifdef CONFIG_OF
/*
@@ -1459,11 +1403,10 @@ static int ds1307_probe(struct i2c_client *client,
case ds_1339:
case ds_3231:
/* get registers that the "rtc" read below won't read... */
- tmp = ds1307->read_block_data(ds1307->client,
- DS1337_REG_CONTROL, 2, buf);
- if (tmp != 2) {
- dev_dbg(&client->dev, "read error %d\n", tmp);
- err = -EIO;
+ err = regmap_bulk_read(ds1307->regmap, DS1337_REG_CONTROL,
+ buf, 2);
+ if (err) {
+ dev_dbg(ds1307->dev, "read error %d\n", err);
goto exit;
}
@@ -1477,8 +1420,8 @@ static int ds1307_probe(struct i2c_client *client,
* For some variants, be sure alarms can trigger when we're
* running on Vbackup (BBSQI/BBSQW)
*/
- if (chip->alarm && (ds1307->client->irq > 0 ||
- ds1307_can_wakeup_device)) {
+ if (chip->alarm && (ds1307->irq > 0 ||
+ ds1307_can_wakeup_device)) {
ds1307->regs[0] |= DS1337_BIT_INTCN
| bbsqi_bitpos[ds1307->type];
ds1307->regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE);
@@ -1486,50 +1429,49 @@ static int ds1307_probe(struct i2c_client *client,
want_irq = true;
}
- i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL,
- ds1307->regs[0]);
+ regmap_write(ds1307->regmap, DS1337_REG_CONTROL,
+ ds1307->regs[0]);
/* oscillator fault? clear flag, and warn */
if (ds1307->regs[1] & DS1337_BIT_OSF) {
- i2c_smbus_write_byte_data(client, DS1337_REG_STATUS,
- ds1307->regs[1] & ~DS1337_BIT_OSF);
- dev_warn(&client->dev, "SET TIME!\n");
+ regmap_write(ds1307->regmap, DS1337_REG_STATUS,
+ ds1307->regs[1] & ~DS1337_BIT_OSF);
+ dev_warn(ds1307->dev, "SET TIME!\n");
}
break;
case rx_8025:
- tmp = i2c_smbus_read_i2c_block_data(ds1307->client,
- RX8025_REG_CTRL1 << 4 | 0x08, 2, buf);
- if (tmp != 2) {
- dev_dbg(&client->dev, "read error %d\n", tmp);
- err = -EIO;
+ err = regmap_bulk_read(ds1307->regmap,
+ RX8025_REG_CTRL1 << 4 | 0x08, buf, 2);
+ if (err) {
+ dev_dbg(ds1307->dev, "read error %d\n", err);
goto exit;
}
/* oscillator off? turn it on, so clock can tick. */
if (!(ds1307->regs[1] & RX8025_BIT_XST)) {
ds1307->regs[1] |= RX8025_BIT_XST;
- i2c_smbus_write_byte_data(client,
- RX8025_REG_CTRL2 << 4 | 0x08,
- ds1307->regs[1]);
- dev_warn(&client->dev,
+ regmap_write(ds1307->regmap,
+ RX8025_REG_CTRL2 << 4 | 0x08,
+ ds1307->regs[1]);
+ dev_warn(ds1307->dev,
"oscillator stop detected - SET TIME!\n");
}
if (ds1307->regs[1] & RX8025_BIT_PON) {
ds1307->regs[1] &= ~RX8025_BIT_PON;
- i2c_smbus_write_byte_data(client,
- RX8025_REG_CTRL2 << 4 | 0x08,
- ds1307->regs[1]);
- dev_warn(&client->dev, "power-on detected\n");
+ regmap_write(ds1307->regmap,
+ RX8025_REG_CTRL2 << 4 | 0x08,
+ ds1307->regs[1]);
+ dev_warn(ds1307->dev, "power-on detected\n");
}
if (ds1307->regs[1] & RX8025_BIT_VDET) {
ds1307->regs[1] &= ~RX8025_BIT_VDET;
- i2c_smbus_write_byte_data(client,
- RX8025_REG_CTRL2 << 4 | 0x08,
- ds1307->regs[1]);
- dev_warn(&client->dev, "voltage drop detected\n");
+ regmap_write(ds1307->regmap,
+ RX8025_REG_CTRL2 << 4 | 0x08,
+ ds1307->regs[1]);
+ dev_warn(ds1307->dev, "voltage drop detected\n");
}
/* make sure we are running in 24hour mode */
@@ -1537,16 +1479,15 @@ static int ds1307_probe(struct i2c_client *client,
u8 hour;
/* switch to 24 hour mode */
- i2c_smbus_write_byte_data(client,
- RX8025_REG_CTRL1 << 4 | 0x08,
- ds1307->regs[0] |
- RX8025_BIT_2412);
-
- tmp = i2c_smbus_read_i2c_block_data(ds1307->client,
- RX8025_REG_CTRL1 << 4 | 0x08, 2, buf);
- if (tmp != 2) {
- dev_dbg(&client->dev, "read error %d\n", tmp);
- err = -EIO;
+ regmap_write(ds1307->regmap,
+ RX8025_REG_CTRL1 << 4 | 0x08,
+ ds1307->regs[0] | RX8025_BIT_2412);
+
+ err = regmap_bulk_read(ds1307->regmap,
+ RX8025_REG_CTRL1 << 4 | 0x08,
+ buf, 2);
+ if (err) {
+ dev_dbg(ds1307->dev, "read error %d\n", err);
goto exit;
}
@@ -1557,9 +1498,16 @@ static int ds1307_probe(struct i2c_client *client,
if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM)
hour += 12;
- i2c_smbus_write_byte_data(client,
- DS1307_REG_HOUR << 4 | 0x08,
- hour);
+ regmap_write(ds1307->regmap,
+ DS1307_REG_HOUR << 4 | 0x08, hour);
+ }
+ break;
+ case rx_8130:
+ ds1307->offset = 0x10; /* Seconds starts at 0x10 */
+ rtc_ops = &rx8130_rtc_ops;
+ if (chip->alarm && ds1307->irq > 0) {
+ irq_handler = rx8130_irq;
+ want_irq = true;
}
break;
case ds_1388:
@@ -1567,7 +1515,8 @@ static int ds1307_probe(struct i2c_client *client,
break;
case mcp794xx:
rtc_ops = &mcp794xx_rtc_ops;
- if (ds1307->client->irq > 0 && chip->alarm) {
+ if (chip->alarm && (ds1307->irq > 0 ||
+ ds1307_can_wakeup_device)) {
irq_handler = mcp794xx_irq;
want_irq = true;
}
@@ -1578,10 +1527,9 @@ static int ds1307_probe(struct i2c_client *client,
read_rtc:
/* read RTC registers */
- tmp = ds1307->read_block_data(ds1307->client, ds1307->offset, 8, buf);
- if (tmp != 8) {
- dev_dbg(&client->dev, "read error %d\n", tmp);
- err = -EIO;
+ err = regmap_bulk_read(ds1307->regmap, ds1307->offset, buf, 8);
+ if (err) {
+ dev_dbg(ds1307->dev, "read error %d\n", err);
goto exit;
}
@@ -1597,56 +1545,56 @@ read_rtc:
case m41t00:
/* clock halted? turn it on, so clock can tick. */
if (tmp & DS1307_BIT_CH) {
- i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0);
- dev_warn(&client->dev, "SET TIME!\n");
+ regmap_write(ds1307->regmap, DS1307_REG_SECS, 0);
+ dev_warn(ds1307->dev, "SET TIME!\n");
goto read_rtc;
}
break;
+ case ds_1308:
case ds_1338:
/* clock halted? turn it on, so clock can tick. */
if (tmp & DS1307_BIT_CH)
- i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0);
+ regmap_write(ds1307->regmap, DS1307_REG_SECS, 0);
/* oscillator fault? clear flag, and warn */
if (ds1307->regs[DS1307_REG_CONTROL] & DS1338_BIT_OSF) {
- i2c_smbus_write_byte_data(client, DS1307_REG_CONTROL,
- ds1307->regs[DS1307_REG_CONTROL]
- & ~DS1338_BIT_OSF);
- dev_warn(&client->dev, "SET TIME!\n");
+ regmap_write(ds1307->regmap, DS1307_REG_CONTROL,
+ ds1307->regs[DS1307_REG_CONTROL] &
+ ~DS1338_BIT_OSF);
+ dev_warn(ds1307->dev, "SET TIME!\n");
goto read_rtc;
}
break;
case ds_1340:
/* clock halted? turn it on, so clock can tick. */
if (tmp & DS1340_BIT_nEOSC)
- i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0);
+ regmap_write(ds1307->regmap, DS1307_REG_SECS, 0);
- tmp = i2c_smbus_read_byte_data(client, DS1340_REG_FLAG);
- if (tmp < 0) {
- dev_dbg(&client->dev, "read error %d\n", tmp);
- err = -EIO;
+ err = regmap_read(ds1307->regmap, DS1340_REG_FLAG, &tmp);
+ if (err) {
+ dev_dbg(ds1307->dev, "read error %d\n", err);
goto exit;
}
/* oscillator fault? clear flag, and warn */
if (tmp & DS1340_BIT_OSF) {
- i2c_smbus_write_byte_data(client, DS1340_REG_FLAG, 0);
- dev_warn(&client->dev, "SET TIME!\n");
+ regmap_write(ds1307->regmap, DS1340_REG_FLAG, 0);
+ dev_warn(ds1307->dev, "SET TIME!\n");
}
break;
case mcp794xx:
/* make sure that the backup battery is enabled */
if (!(ds1307->regs[DS1307_REG_WDAY] & MCP794XX_BIT_VBATEN)) {
- i2c_smbus_write_byte_data(client, DS1307_REG_WDAY,
- ds1307->regs[DS1307_REG_WDAY]
- | MCP794XX_BIT_VBATEN);
+ regmap_write(ds1307->regmap, DS1307_REG_WDAY,
+ ds1307->regs[DS1307_REG_WDAY] |
+ MCP794XX_BIT_VBATEN);
}
/* clock halted? turn it on, so clock can tick. */
if (!(tmp & MCP794XX_BIT_ST)) {
- i2c_smbus_write_byte_data(client, DS1307_REG_SECS,
- MCP794XX_BIT_ST);
- dev_warn(&client->dev, "SET TIME!\n");
+ regmap_write(ds1307->regmap, DS1307_REG_SECS,
+ MCP794XX_BIT_ST);
+ dev_warn(ds1307->dev, "SET TIME!\n");
goto read_rtc;
}
@@ -1680,16 +1628,15 @@ read_rtc:
tmp = 0;
if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM)
tmp += 12;
- i2c_smbus_write_byte_data(client,
- ds1307->offset + DS1307_REG_HOUR,
- bin2bcd(tmp));
+ regmap_write(ds1307->regmap, ds1307->offset + DS1307_REG_HOUR,
+ bin2bcd(tmp));
}
/*
* Some IPs have weekday reset value = 0x1 which might not correct
* hence compute the wday using the current date/month/year values
*/
- ds1307_get_time(&client->dev, &tm);
+ ds1307_get_time(ds1307->dev, &tm);
wday = tm.tm_wday;
timestamp = rtc_tm_to_time64(&tm);
rtc_time64_to_tm(timestamp, &tm);
@@ -1699,78 +1646,63 @@ read_rtc:
* If different then set the wday which we computed using
* timestamp
*/
- if (wday != tm.tm_wday) {
- wday = i2c_smbus_read_byte_data(client, MCP794XX_REG_WEEKDAY);
- wday = wday & ~MCP794XX_REG_WEEKDAY_WDAY_MASK;
- wday = wday | (tm.tm_wday + 1);
- i2c_smbus_write_byte_data(client, MCP794XX_REG_WEEKDAY, wday);
- }
+ if (wday != tm.tm_wday)
+ regmap_update_bits(ds1307->regmap, MCP794XX_REG_WEEKDAY,
+ MCP794XX_REG_WEEKDAY_WDAY_MASK,
+ tm.tm_wday + 1);
if (want_irq) {
- device_set_wakeup_capable(&client->dev, true);
+ device_set_wakeup_capable(ds1307->dev, true);
set_bit(HAS_ALARM, &ds1307->flags);
}
- ds1307->rtc = devm_rtc_device_register(&client->dev, client->name,
- rtc_ops, THIS_MODULE);
+
+ ds1307->rtc = devm_rtc_allocate_device(ds1307->dev);
if (IS_ERR(ds1307->rtc)) {
return PTR_ERR(ds1307->rtc);
}
- if (ds1307_can_wakeup_device && ds1307->client->irq <= 0) {
+ if (ds1307_can_wakeup_device && ds1307->irq <= 0) {
/* Disable request for an IRQ */
want_irq = false;
- dev_info(&client->dev, "'wakeup-source' is set, request for an IRQ is disabled!\n");
+ dev_info(ds1307->dev,
+ "'wakeup-source' is set, request for an IRQ is disabled!\n");
/* We cannot support UIE mode if we do not have an IRQ line */
ds1307->rtc->uie_unsupported = 1;
}
if (want_irq) {
- err = devm_request_threaded_irq(&client->dev,
- client->irq, NULL, irq_handler,
+ err = devm_request_threaded_irq(ds1307->dev,
+ ds1307->irq, NULL, irq_handler,
IRQF_SHARED | IRQF_ONESHOT,
- ds1307->rtc->name, client);
+ ds1307->name, ds1307);
if (err) {
client->irq = 0;
- device_set_wakeup_capable(&client->dev, false);
+ device_set_wakeup_capable(ds1307->dev, false);
clear_bit(HAS_ALARM, &ds1307->flags);
- dev_err(&client->dev, "unable to request IRQ!\n");
+ dev_err(ds1307->dev, "unable to request IRQ!\n");
} else
- dev_dbg(&client->dev, "got IRQ %d\n", client->irq);
+ dev_dbg(ds1307->dev, "got IRQ %d\n", client->irq);
}
if (chip->nvram_size) {
-
- ds1307->nvram = devm_kzalloc(&client->dev,
- sizeof(struct bin_attribute),
- GFP_KERNEL);
- if (!ds1307->nvram) {
- dev_err(&client->dev, "cannot allocate memory for nvram sysfs\n");
- } else {
-
- ds1307->nvram->attr.name = "nvram";
- ds1307->nvram->attr.mode = S_IRUGO | S_IWUSR;
-
- sysfs_bin_attr_init(ds1307->nvram);
-
- ds1307->nvram->read = ds1307_nvram_read;
- ds1307->nvram->write = ds1307_nvram_write;
- ds1307->nvram->size = chip->nvram_size;
- ds1307->nvram_offset = chip->nvram_offset;
-
- err = sysfs_create_bin_file(&client->dev.kobj,
- ds1307->nvram);
- if (err) {
- dev_err(&client->dev,
- "unable to create sysfs file: %s\n",
- ds1307->nvram->attr.name);
- } else {
- set_bit(HAS_NVRAM, &ds1307->flags);
- dev_info(&client->dev, "%zu bytes nvram\n",
- ds1307->nvram->size);
- }
- }
+ ds1307->nvmem_cfg.name = "ds1307_nvram";
+ ds1307->nvmem_cfg.word_size = 1;
+ ds1307->nvmem_cfg.stride = 1;
+ ds1307->nvmem_cfg.size = chip->nvram_size;
+ ds1307->nvmem_cfg.reg_read = ds1307_nvram_read;
+ ds1307->nvmem_cfg.reg_write = ds1307_nvram_write;
+ ds1307->nvmem_cfg.priv = ds1307;
+ ds1307->nvram_offset = chip->nvram_offset;
+
+ ds1307->rtc->nvmem_config = &ds1307->nvmem_cfg;
+ ds1307->rtc->nvram_old_abi = true;
}
+ ds1307->rtc->ops = rtc_ops;
+ err = rtc_register_device(ds1307->rtc);
+ if (err)
+ return err;
+
ds1307_hwmon_register(ds1307);
ds1307_clks_register(ds1307);
@@ -1780,16 +1712,6 @@ exit:
return err;
}
-static int ds1307_remove(struct i2c_client *client)
-{
- struct ds1307 *ds1307 = i2c_get_clientdata(client);
-
- if (test_and_clear_bit(HAS_NVRAM, &ds1307->flags))
- sysfs_remove_bin_file(&client->dev.kobj, ds1307->nvram);
-
- return 0;
-}
-
static struct i2c_driver ds1307_driver = {
.driver = {
.name = "rtc-ds1307",
@@ -1797,7 +1719,6 @@ static struct i2c_driver ds1307_driver = {
.acpi_match_table = ACPI_PTR(ds1307_acpi_ids),
},
.probe = ds1307_probe,
- .remove = ds1307_remove,
.id_table = ds1307_id,
};
diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
index deff431a37c4..0550f7ba464f 100644
--- a/drivers/rtc/rtc-ds3232.c
+++ b/drivers/rtc/rtc-ds3232.c
@@ -22,6 +22,7 @@
#include <linux/bcd.h>
#include <linux/slab.h>
#include <linux/regmap.h>
+#include <linux/hwmon.h>
#define DS3232_REG_SECONDS 0x00
#define DS3232_REG_MINUTES 0x01
@@ -46,6 +47,8 @@
# define DS3232_REG_SR_A2F 0x02
# define DS3232_REG_SR_A1F 0x01
+#define DS3232_REG_TEMPERATURE 0x11
+
struct ds3232 {
struct device *dev;
struct regmap *regmap;
@@ -275,6 +278,120 @@ static int ds3232_update_alarm(struct device *dev, unsigned int enabled)
return ret;
}
+/*
+ * Temperature sensor support for ds3232/ds3234 devices.
+ * A user-initiated temperature conversion is not started by this function,
+ * so the temperature is updated once every 64 seconds.
+ */
+static int ds3232_hwmon_read_temp(struct device *dev, long int *mC)
+{
+ struct ds3232 *ds3232 = dev_get_drvdata(dev);
+ u8 temp_buf[2];
+ s16 temp;
+ int ret;
+
+ ret = regmap_bulk_read(ds3232->regmap, DS3232_REG_TEMPERATURE, temp_buf,
+ sizeof(temp_buf));
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Temperature is represented as a 10-bit code with a resolution of
+ * 0.25 degree celsius and encoded in two's complement format.
+ */
+ temp = (temp_buf[0] << 8) | temp_buf[1];
+ temp >>= 6;
+ *mC = temp * 250;
+
+ return 0;
+}
+
+static umode_t ds3232_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ if (type != hwmon_temp)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ default:
+ return 0;
+ }
+}
+
+static int ds3232_hwmon_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *temp)
+{
+ int err;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ err = ds3232_hwmon_read_temp(dev, temp);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ return err;
+}
+
+static u32 ds3232_hwmon_chip_config[] = {
+ HWMON_C_REGISTER_TZ,
+ 0
+};
+
+static const struct hwmon_channel_info ds3232_hwmon_chip = {
+ .type = hwmon_chip,
+ .config = ds3232_hwmon_chip_config,
+};
+
+static u32 ds3232_hwmon_temp_config[] = {
+ HWMON_T_INPUT,
+ 0
+};
+
+static const struct hwmon_channel_info ds3232_hwmon_temp = {
+ .type = hwmon_temp,
+ .config = ds3232_hwmon_temp_config,
+};
+
+static const struct hwmon_channel_info *ds3232_hwmon_info[] = {
+ &ds3232_hwmon_chip,
+ &ds3232_hwmon_temp,
+ NULL
+};
+
+static const struct hwmon_ops ds3232_hwmon_hwmon_ops = {
+ .is_visible = ds3232_hwmon_is_visible,
+ .read = ds3232_hwmon_read,
+};
+
+static const struct hwmon_chip_info ds3232_hwmon_chip_info = {
+ .ops = &ds3232_hwmon_hwmon_ops,
+ .info = ds3232_hwmon_info,
+};
+
+static void ds3232_hwmon_register(struct device *dev, const char *name)
+{
+ struct ds3232 *ds3232 = dev_get_drvdata(dev);
+ struct device *hwmon_dev;
+
+ if (!IS_ENABLED(CONFIG_RTC_DRV_DS3232_HWMON))
+ return;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, name, ds3232,
+ &ds3232_hwmon_chip_info,
+ NULL);
+ if (IS_ERR(hwmon_dev)) {
+ dev_err(dev, "unable to register hwmon device %ld\n",
+ PTR_ERR(hwmon_dev));
+ }
+}
+
static int ds3232_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct ds3232 *ds3232 = dev_get_drvdata(dev);
@@ -366,6 +483,8 @@ static int ds3232_probe(struct device *dev, struct regmap *regmap, int irq,
if (ds3232->irq > 0)
device_init_wakeup(dev, 1);
+ ds3232_hwmon_register(dev, name);
+
ds3232->rtc = devm_rtc_device_register(dev, name, &ds3232_rtc_ops,
THIS_MODULE);
if (IS_ERR(ds3232->rtc))
diff --git a/drivers/rtc/rtc-gemini.c b/drivers/rtc/rtc-ftrtc010.c
index 5279390bb42d..af8d6beae20c 100644
--- a/drivers/rtc/rtc-gemini.c
+++ b/drivers/rtc/rtc-ftrtc010.c
@@ -1,5 +1,5 @@
/*
- * Gemini OnChip RTC
+ * Faraday Technology FTRTC010 driver
*
* Copyright (C) 2009 Janos Laube <janos.dev@gmail.com>
*
@@ -26,33 +26,36 @@
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/clk.h>
-#define DRV_NAME "rtc-gemini"
+#define DRV_NAME "rtc-ftrtc010"
MODULE_AUTHOR("Hans Ulli Kroll <ulli.kroll@googlemail.com>");
MODULE_DESCRIPTION("RTC driver for Gemini SoC");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
-struct gemini_rtc {
+struct ftrtc010_rtc {
struct rtc_device *rtc_dev;
void __iomem *rtc_base;
int rtc_irq;
+ struct clk *pclk;
+ struct clk *extclk;
};
-enum gemini_rtc_offsets {
- GEMINI_RTC_SECOND = 0x00,
- GEMINI_RTC_MINUTE = 0x04,
- GEMINI_RTC_HOUR = 0x08,
- GEMINI_RTC_DAYS = 0x0C,
- GEMINI_RTC_ALARM_SECOND = 0x10,
- GEMINI_RTC_ALARM_MINUTE = 0x14,
- GEMINI_RTC_ALARM_HOUR = 0x18,
- GEMINI_RTC_RECORD = 0x1C,
- GEMINI_RTC_CR = 0x20
+enum ftrtc010_rtc_offsets {
+ FTRTC010_RTC_SECOND = 0x00,
+ FTRTC010_RTC_MINUTE = 0x04,
+ FTRTC010_RTC_HOUR = 0x08,
+ FTRTC010_RTC_DAYS = 0x0C,
+ FTRTC010_RTC_ALARM_SECOND = 0x10,
+ FTRTC010_RTC_ALARM_MINUTE = 0x14,
+ FTRTC010_RTC_ALARM_HOUR = 0x18,
+ FTRTC010_RTC_RECORD = 0x1C,
+ FTRTC010_RTC_CR = 0x20,
};
-static irqreturn_t gemini_rtc_interrupt(int irq, void *dev)
+static irqreturn_t ftrtc010_rtc_interrupt(int irq, void *dev)
{
return IRQ_HANDLED;
}
@@ -66,18 +69,18 @@ static irqreturn_t gemini_rtc_interrupt(int irq, void *dev)
* the same thing, without the rtc-lib.c calls.
*/
-static int gemini_rtc_read_time(struct device *dev, struct rtc_time *tm)
+static int ftrtc010_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- struct gemini_rtc *rtc = dev_get_drvdata(dev);
+ struct ftrtc010_rtc *rtc = dev_get_drvdata(dev);
unsigned int days, hour, min, sec;
unsigned long offset, time;
- sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND);
- min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE);
- hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR);
- days = readl(rtc->rtc_base + GEMINI_RTC_DAYS);
- offset = readl(rtc->rtc_base + GEMINI_RTC_RECORD);
+ sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND);
+ min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE);
+ hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR);
+ days = readl(rtc->rtc_base + FTRTC010_RTC_DAYS);
+ offset = readl(rtc->rtc_base + FTRTC010_RTC_RECORD);
time = offset + days * 86400 + hour * 3600 + min * 60 + sec;
@@ -86,9 +89,9 @@ static int gemini_rtc_read_time(struct device *dev, struct rtc_time *tm)
return 0;
}
-static int gemini_rtc_set_time(struct device *dev, struct rtc_time *tm)
+static int ftrtc010_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
- struct gemini_rtc *rtc = dev_get_drvdata(dev);
+ struct ftrtc010_rtc *rtc = dev_get_drvdata(dev);
unsigned int sec, min, hour, day;
unsigned long offset, time;
@@ -97,27 +100,27 @@ static int gemini_rtc_set_time(struct device *dev, struct rtc_time *tm)
rtc_tm_to_time(tm, &time);
- sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND);
- min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE);
- hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR);
- day = readl(rtc->rtc_base + GEMINI_RTC_DAYS);
+ sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND);
+ min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE);
+ hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR);
+ day = readl(rtc->rtc_base + FTRTC010_RTC_DAYS);
offset = time - (day * 86400 + hour * 3600 + min * 60 + sec);
- writel(offset, rtc->rtc_base + GEMINI_RTC_RECORD);
- writel(0x01, rtc->rtc_base + GEMINI_RTC_CR);
+ writel(offset, rtc->rtc_base + FTRTC010_RTC_RECORD);
+ writel(0x01, rtc->rtc_base + FTRTC010_RTC_CR);
return 0;
}
-static const struct rtc_class_ops gemini_rtc_ops = {
- .read_time = gemini_rtc_read_time,
- .set_time = gemini_rtc_set_time,
+static const struct rtc_class_ops ftrtc010_rtc_ops = {
+ .read_time = ftrtc010_rtc_read_time,
+ .set_time = ftrtc010_rtc_set_time,
};
-static int gemini_rtc_probe(struct platform_device *pdev)
+static int ftrtc010_rtc_probe(struct platform_device *pdev)
{
- struct gemini_rtc *rtc;
+ struct ftrtc010_rtc *rtc;
struct device *dev = &pdev->dev;
struct resource *res;
int ret;
@@ -127,6 +130,27 @@ static int gemini_rtc_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, rtc);
+ rtc->pclk = devm_clk_get(dev, "PCLK");
+ if (IS_ERR(rtc->pclk)) {
+ dev_err(dev, "could not get PCLK\n");
+ } else {
+ ret = clk_prepare_enable(rtc->pclk);
+ if (ret) {
+ dev_err(dev, "failed to enable PCLK\n");
+ return ret;
+ }
+ }
+ rtc->extclk = devm_clk_get(dev, "EXTCLK");
+ if (IS_ERR(rtc->extclk)) {
+ dev_err(dev, "could not get EXTCLK\n");
+ } else {
+ ret = clk_prepare_enable(rtc->extclk);
+ if (ret) {
+ dev_err(dev, "failed to enable EXTCLK\n");
+ return ret;
+ }
+ }
+
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res)
return -ENODEV;
@@ -142,38 +166,43 @@ static int gemini_rtc_probe(struct platform_device *pdev)
if (!rtc->rtc_base)
return -ENOMEM;
- ret = devm_request_irq(dev, rtc->rtc_irq, gemini_rtc_interrupt,
+ ret = devm_request_irq(dev, rtc->rtc_irq, ftrtc010_rtc_interrupt,
IRQF_SHARED, pdev->name, dev);
if (unlikely(ret))
return ret;
rtc->rtc_dev = rtc_device_register(pdev->name, dev,
- &gemini_rtc_ops, THIS_MODULE);
+ &ftrtc010_rtc_ops, THIS_MODULE);
return PTR_ERR_OR_ZERO(rtc->rtc_dev);
}
-static int gemini_rtc_remove(struct platform_device *pdev)
+static int ftrtc010_rtc_remove(struct platform_device *pdev)
{
- struct gemini_rtc *rtc = platform_get_drvdata(pdev);
+ struct ftrtc010_rtc *rtc = platform_get_drvdata(pdev);
+ if (!IS_ERR(rtc->extclk))
+ clk_disable_unprepare(rtc->extclk);
+ if (!IS_ERR(rtc->pclk))
+ clk_disable_unprepare(rtc->pclk);
rtc_device_unregister(rtc->rtc_dev);
return 0;
}
-static const struct of_device_id gemini_rtc_dt_match[] = {
+static const struct of_device_id ftrtc010_rtc_dt_match[] = {
{ .compatible = "cortina,gemini-rtc" },
+ { .compatible = "faraday,ftrtc010" },
{ }
};
-MODULE_DEVICE_TABLE(of, gemini_rtc_dt_match);
+MODULE_DEVICE_TABLE(of, ftrtc010_rtc_dt_match);
-static struct platform_driver gemini_rtc_driver = {
+static struct platform_driver ftrtc010_rtc_driver = {
.driver = {
.name = DRV_NAME,
- .of_match_table = gemini_rtc_dt_match,
+ .of_match_table = ftrtc010_rtc_dt_match,
},
- .probe = gemini_rtc_probe,
- .remove = gemini_rtc_remove,
+ .probe = ftrtc010_rtc_probe,
+ .remove = ftrtc010_rtc_remove,
};
-module_platform_driver_probe(gemini_rtc_driver, gemini_rtc_probe);
+module_platform_driver_probe(ftrtc010_rtc_driver, ftrtc010_rtc_probe);
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 5ec4653022ff..8940e9e43ea0 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -16,6 +16,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bcd.h>
+#include <linux/clk-provider.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -53,6 +54,8 @@
#define M41T80_ALARM_REG_SIZE \
(M41T80_REG_ALARM_SEC + 1 - M41T80_REG_ALARM_MON)
+#define M41T80_SQW_MAX_FREQ 32768
+
#define M41T80_SEC_ST BIT(7) /* ST: Stop Bit */
#define M41T80_ALMON_AFE BIT(7) /* AFE: AF Enable Bit */
#define M41T80_ALMON_SQWE BIT(6) /* SQWE: SQW Enable Bit */
@@ -147,7 +150,11 @@ MODULE_DEVICE_TABLE(of, m41t80_of_match);
struct m41t80_data {
unsigned long features;
+ struct i2c_client *client;
struct rtc_device *rtc;
+#ifdef CONFIG_COMMON_CLK
+ struct clk_hw sqw;
+#endif
};
static irqreturn_t m41t80_handle_irq(int irq, void *dev_id)
@@ -227,6 +234,7 @@ static int m41t80_get_datetime(struct i2c_client *client,
/* Sets the given date and time to the real time clock. */
static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{
+ struct m41t80_data *clientdata = i2c_get_clientdata(client);
unsigned char buf[8];
int err, flags;
@@ -242,6 +250,17 @@ static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year - 100);
buf[M41T80_REG_WDAY] = tm->tm_wday;
+ /* If the square wave output is controlled in the weekday register */
+ if (clientdata->features & M41T80_FEATURE_SQ_ALT) {
+ int val;
+
+ val = i2c_smbus_read_byte_data(client, M41T80_REG_WDAY);
+ if (val < 0)
+ return val;
+
+ buf[M41T80_REG_WDAY] |= (val & 0xf0);
+ }
+
err = i2c_smbus_write_i2c_block_data(client, M41T80_REG_SSEC,
sizeof(buf), buf);
if (err < 0) {
@@ -332,6 +351,9 @@ static int m41t80_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
return err;
}
+ /* Keep SQWE bit value */
+ alarmvals[0] |= (ret & M41T80_ALMON_SQWE);
+
ret = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
if (ret < 0)
return ret;
@@ -431,103 +453,175 @@ static ssize_t flags_show(struct device *dev,
}
static DEVICE_ATTR_RO(flags);
-static ssize_t sqwfreq_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static struct attribute *attrs[] = {
+ &dev_attr_flags.attr,
+ NULL,
+};
+
+static struct attribute_group attr_group = {
+ .attrs = attrs,
+};
+
+#ifdef CONFIG_COMMON_CLK
+#define sqw_to_m41t80_data(_hw) container_of(_hw, struct m41t80_data, sqw)
+
+static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct m41t80_data *clientdata = i2c_get_clientdata(client);
- int val, reg_sqw;
+ struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
+ struct i2c_client *client = m41t80->client;
+ int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ?
+ M41T80_REG_WDAY : M41T80_REG_SQW;
+ int ret = i2c_smbus_read_byte_data(client, reg_sqw);
+ unsigned long val = M41T80_SQW_MAX_FREQ;
- if (!(clientdata->features & M41T80_FEATURE_SQ))
- return -EINVAL;
+ if (ret < 0)
+ return 0;
- reg_sqw = M41T80_REG_SQW;
- if (clientdata->features & M41T80_FEATURE_SQ_ALT)
- reg_sqw = M41T80_REG_WDAY;
- val = i2c_smbus_read_byte_data(client, reg_sqw);
- if (val < 0)
- return val;
- val = (val >> 4) & 0xf;
- switch (val) {
- case 0:
- break;
- case 1:
- val = 32768;
- break;
- default:
- val = 32768 >> val;
- }
- return sprintf(buf, "%d\n", val);
+ ret >>= 4;
+ if (ret == 0)
+ val = 0;
+ else if (ret > 1)
+ val = val / (1 << ret);
+
+ return val;
}
-static ssize_t sqwfreq_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static long m41t80_sqw_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct m41t80_data *clientdata = i2c_get_clientdata(client);
- int almon, sqw, reg_sqw, rc;
- unsigned long val;
+ int i, freq = M41T80_SQW_MAX_FREQ;
- rc = kstrtoul(buf, 0, &val);
- if (rc < 0)
- return rc;
+ if (freq <= rate)
+ return freq;
- if (!(clientdata->features & M41T80_FEATURE_SQ))
- return -EINVAL;
+ for (i = 2; i <= ilog2(M41T80_SQW_MAX_FREQ); i++) {
+ freq /= 1 << i;
+ if (freq <= rate)
+ return freq;
+ }
- if (val) {
- if (!is_power_of_2(val))
+ return 0;
+}
+
+static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
+ struct i2c_client *client = m41t80->client;
+ int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ?
+ M41T80_REG_WDAY : M41T80_REG_SQW;
+ int reg, ret, val = 0;
+
+ if (rate) {
+ if (!is_power_of_2(rate))
return -EINVAL;
- val = ilog2(val);
- if (val == 15)
+ val = ilog2(rate);
+ if (val == ilog2(M41T80_SQW_MAX_FREQ))
val = 1;
- else if (val < 14)
- val = 15 - val;
+ else if (val < (ilog2(M41T80_SQW_MAX_FREQ) - 1))
+ val = ilog2(M41T80_SQW_MAX_FREQ) - val;
else
return -EINVAL;
}
- /* disable SQW, set SQW frequency & re-enable */
- almon = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
- if (almon < 0)
- return almon;
- reg_sqw = M41T80_REG_SQW;
- if (clientdata->features & M41T80_FEATURE_SQ_ALT)
- reg_sqw = M41T80_REG_WDAY;
- sqw = i2c_smbus_read_byte_data(client, reg_sqw);
- if (sqw < 0)
- return sqw;
- sqw = (sqw & 0x0f) | (val << 4);
-
- rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
- almon & ~M41T80_ALMON_SQWE);
- if (rc < 0)
- return rc;
- if (val) {
- rc = i2c_smbus_write_byte_data(client, reg_sqw, sqw);
- if (rc < 0)
- return rc;
+ reg = i2c_smbus_read_byte_data(client, reg_sqw);
+ if (reg < 0)
+ return reg;
- rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
- almon | M41T80_ALMON_SQWE);
- if (rc < 0)
- return rc;
- }
- return count;
+ reg = (reg & 0x0f) | (val << 4);
+
+ ret = i2c_smbus_write_byte_data(client, reg_sqw, reg);
+ if (ret < 0)
+ return ret;
+
+ return -EINVAL;
}
-static DEVICE_ATTR_RW(sqwfreq);
-static struct attribute *attrs[] = {
- &dev_attr_flags.attr,
- &dev_attr_sqwfreq.attr,
- NULL,
-};
+static int m41t80_sqw_control(struct clk_hw *hw, bool enable)
+{
+ struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
+ struct i2c_client *client = m41t80->client;
+ int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
-static struct attribute_group attr_group = {
- .attrs = attrs,
+ if (ret < 0)
+ return ret;
+
+ if (enable)
+ ret |= M41T80_ALMON_SQWE;
+ else
+ ret &= ~M41T80_ALMON_SQWE;
+
+ return i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, ret);
+}
+
+static int m41t80_sqw_prepare(struct clk_hw *hw)
+{
+ return m41t80_sqw_control(hw, 1);
+}
+
+static void m41t80_sqw_unprepare(struct clk_hw *hw)
+{
+ m41t80_sqw_control(hw, 0);
+}
+
+static int m41t80_sqw_is_prepared(struct clk_hw *hw)
+{
+ struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
+ struct i2c_client *client = m41t80->client;
+ int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
+
+ if (ret < 0)
+ return ret;
+
+ return !!(ret & M41T80_ALMON_SQWE);
+}
+
+static const struct clk_ops m41t80_sqw_ops = {
+ .prepare = m41t80_sqw_prepare,
+ .unprepare = m41t80_sqw_unprepare,
+ .is_prepared = m41t80_sqw_is_prepared,
+ .recalc_rate = m41t80_sqw_recalc_rate,
+ .round_rate = m41t80_sqw_round_rate,
+ .set_rate = m41t80_sqw_set_rate,
};
+static struct clk *m41t80_sqw_register_clk(struct m41t80_data *m41t80)
+{
+ struct i2c_client *client = m41t80->client;
+ struct device_node *node = client->dev.of_node;
+ struct clk *clk;
+ struct clk_init_data init;
+ int ret;
+
+ /* First disable the clock */
+ ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ ret = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
+ ret & ~(M41T80_ALMON_SQWE));
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ init.name = "m41t80-sqw";
+ init.ops = &m41t80_sqw_ops;
+ init.flags = 0;
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ m41t80->sqw.init = &init;
+
+ /* optional override of the clockname */
+ of_property_read_string(node, "clock-output-names", &init.name);
+
+ /* register the clock */
+ clk = clk_register(&client->dev, &m41t80->sqw);
+ if (!IS_ERR(clk))
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+
+ return clk;
+}
+#endif
+
#ifdef CONFIG_RTC_DRV_M41T80_WDT
/*
*****************************************************************************
@@ -845,6 +939,7 @@ static int m41t80_probe(struct i2c_client *client,
if (!m41t80_data)
return -ENOMEM;
+ m41t80_data->client = client;
if (client->dev.of_node)
m41t80_data->features = (unsigned long)
of_device_get_match_data(&client->dev);
@@ -937,6 +1032,10 @@ static int m41t80_probe(struct i2c_client *client,
}
}
#endif
+#ifdef CONFIG_COMMON_CLK
+ if (m41t80_data->features & M41T80_FEATURE_SQ)
+ m41t80_sqw_register_clk(m41t80_data);
+#endif
return 0;
}
diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c
index 77319122642a..401f46d8f21b 100644
--- a/drivers/rtc/rtc-mxc.c
+++ b/drivers/rtc/rtc-mxc.c
@@ -43,17 +43,6 @@
#define MAX_PIE_NUM 9
#define MAX_PIE_FREQ 512
-static const u32 PIE_BIT_DEF[MAX_PIE_NUM][2] = {
- { 2, RTC_2HZ_BIT },
- { 4, RTC_SAM0_BIT },
- { 8, RTC_SAM1_BIT },
- { 16, RTC_SAM2_BIT },
- { 32, RTC_SAM3_BIT },
- { 64, RTC_SAM4_BIT },
- { 128, RTC_SAM5_BIT },
- { 256, RTC_SAM6_BIT },
- { MAX_PIE_FREQ, RTC_SAM7_BIT },
-};
#define MXC_RTC_TIME 0
#define MXC_RTC_ALARM 1
diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c
index b1b6b3041bfb..4ed81117cf5f 100644
--- a/drivers/rtc/rtc-nuc900.c
+++ b/drivers/rtc/rtc-nuc900.c
@@ -93,7 +93,7 @@ static int *check_rtc_access_enable(struct nuc900_rtc *nuc900_rtc)
__raw_writel(AERPOWERON, nuc900_rtc->rtc_reg + REG_RTC_AER);
while (!(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
- && timeout--)
+ && --timeout)
mdelay(1);
if (!timeout)
diff --git a/drivers/rtc/rtc-opal.c b/drivers/rtc/rtc-opal.c
index ea20f627dabe..e2a946c0e667 100644
--- a/drivers/rtc/rtc-opal.c
+++ b/drivers/rtc/rtc-opal.c
@@ -142,6 +142,16 @@ static int opal_get_tpo_time(struct device *dev, struct rtc_wkalrm *alarm)
y_m_d = be32_to_cpu(__y_m_d);
h_m_s_ms = ((u64)be32_to_cpu(__h_m) << 32);
+
+ /* check if no alarm is set */
+ if (y_m_d == 0 && h_m_s_ms == 0) {
+ pr_debug("No alarm is set\n");
+ rc = -ENOENT;
+ goto exit;
+ } else {
+ pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms);
+ }
+
opal_to_tm(y_m_d, h_m_s_ms, &alarm->time);
exit:
@@ -157,7 +167,14 @@ static int opal_set_tpo_time(struct device *dev, struct rtc_wkalrm *alarm)
u32 y_m_d = 0;
int token, rc;
- tm_to_opal(&alarm->time, &y_m_d, &h_m_s_ms);
+ /* if alarm is enabled */
+ if (alarm->enabled) {
+ tm_to_opal(&alarm->time, &y_m_d, &h_m_s_ms);
+ pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms);
+
+ } else {
+ pr_debug("Alarm getting disabled\n");
+ }
token = opal_async_get_token_interruptible();
if (token < 0) {
@@ -190,6 +207,18 @@ exit:
return rc;
}
+int opal_tpo_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct rtc_wkalrm alarm = { .enabled = 0 };
+
+ /*
+ * TPO is automatically enabled when opal_set_tpo_time() is called with
+ * non-zero rtc-time. We only handle disable case which needs to be
+ * explicitly told to opal.
+ */
+ return enabled ? 0 : opal_set_tpo_time(dev, &alarm);
+}
+
static struct rtc_class_ops opal_rtc_ops = {
.read_time = opal_get_rtc_time,
.set_time = opal_set_rtc_time,
@@ -205,6 +234,7 @@ static int opal_rtc_probe(struct platform_device *pdev)
device_set_wakeup_capable(&pdev->dev, true);
opal_rtc_ops.read_alarm = opal_get_tpo_time;
opal_rtc_ops.set_alarm = opal_set_tpo_time;
+ opal_rtc_ops.alarm_irq_enable = opal_tpo_alarm_irq_enable;
}
rtc = devm_rtc_device_register(&pdev->dev, DRVNAME, &opal_rtc_ops,
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index 1227ceab61ee..cea6ea4df970 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -606,7 +606,7 @@ static int pcf8563_probe(struct i2c_client *client,
err = devm_request_threaded_irq(&client->dev, client->irq,
NULL, pcf8563_irq,
IRQF_SHARED|IRQF_ONESHOT|IRQF_TRIGGER_FALLING,
- pcf8563->rtc->name, client);
+ pcf8563_driver.driver.name, client);
if (err) {
dev_err(&client->dev, "unable to request IRQ %d\n",
client->irq);
diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c
index 9ad97ab29866..aae2576741a6 100644
--- a/drivers/rtc/rtc-rv8803.c
+++ b/drivers/rtc/rtc-rv8803.c
@@ -68,6 +68,7 @@ struct rv8803_data {
struct mutex flags_lock;
u8 ctrl;
enum rv8803_type type;
+ struct nvmem_config nvmem_cfg;
};
static int rv8803_read_reg(const struct i2c_client *client, u8 reg)
@@ -460,48 +461,32 @@ static int rv8803_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
}
}
-static ssize_t rv8803_nvram_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr,
- char *buf, loff_t off, size_t count)
+static int rv8803_nvram_write(void *priv, unsigned int offset, void *val,
+ size_t bytes)
{
- struct device *dev = kobj_to_dev(kobj);
- struct i2c_client *client = to_i2c_client(dev);
int ret;
- ret = rv8803_write_reg(client, RV8803_RAM, buf[0]);
+ ret = rv8803_write_reg(priv, RV8803_RAM, *(u8 *)val);
if (ret)
return ret;
- return 1;
+ return 0;
}
-static ssize_t rv8803_nvram_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr,
- char *buf, loff_t off, size_t count)
+static int rv8803_nvram_read(void *priv, unsigned int offset,
+ void *val, size_t bytes)
{
- struct device *dev = kobj_to_dev(kobj);
- struct i2c_client *client = to_i2c_client(dev);
int ret;
- ret = rv8803_read_reg(client, RV8803_RAM);
+ ret = rv8803_read_reg(priv, RV8803_RAM);
if (ret < 0)
return ret;
- buf[0] = ret;
+ *(u8 *)val = ret;
- return 1;
+ return 0;
}
-static struct bin_attribute rv8803_nvram_attr = {
- .attr = {
- .name = "nvram",
- .mode = S_IRUGO | S_IWUSR,
- },
- .size = 1,
- .read = rv8803_nvram_read,
- .write = rv8803_nvram_write,
-};
-
static struct rtc_class_ops rv8803_rtc_ops = {
.read_time = rv8803_get_time,
.set_time = rv8803_set_time,
@@ -577,6 +562,11 @@ static int rv8803_probe(struct i2c_client *client,
if (flags & RV8803_FLAG_AF)
dev_warn(&client->dev, "An alarm maybe have been missed.\n");
+ rv8803->rtc = devm_rtc_allocate_device(&client->dev);
+ if (IS_ERR(rv8803->rtc)) {
+ return PTR_ERR(rv8803->rtc);
+ }
+
if (client->irq > 0) {
err = devm_request_threaded_irq(&client->dev, client->irq,
NULL, rv8803_handle_irq,
@@ -592,12 +582,20 @@ static int rv8803_probe(struct i2c_client *client,
}
}
- rv8803->rtc = devm_rtc_device_register(&client->dev, client->name,
- &rv8803_rtc_ops, THIS_MODULE);
- if (IS_ERR(rv8803->rtc)) {
- dev_err(&client->dev, "unable to register the class device\n");
- return PTR_ERR(rv8803->rtc);
- }
+ rv8803->nvmem_cfg.name = "rv8803_nvram",
+ rv8803->nvmem_cfg.word_size = 1,
+ rv8803->nvmem_cfg.stride = 1,
+ rv8803->nvmem_cfg.size = 1,
+ rv8803->nvmem_cfg.reg_read = rv8803_nvram_read,
+ rv8803->nvmem_cfg.reg_write = rv8803_nvram_write,
+ rv8803->nvmem_cfg.priv = client;
+
+ rv8803->rtc->ops = &rv8803_rtc_ops;
+ rv8803->rtc->nvmem_config = &rv8803->nvmem_cfg;
+ rv8803->rtc->nvram_old_abi = true;
+ err = rtc_register_device(rv8803->rtc);
+ if (err)
+ return err;
err = rv8803_write_reg(rv8803->client, RV8803_EXT, RV8803_EXT_WADA);
if (err)
@@ -609,22 +607,11 @@ static int rv8803_probe(struct i2c_client *client,
return err;
}
- err = device_create_bin_file(&client->dev, &rv8803_nvram_attr);
- if (err)
- return err;
-
rv8803->rtc->max_user_freq = 1;
return 0;
}
-static int rv8803_remove(struct i2c_client *client)
-{
- device_remove_bin_file(&client->dev, &rv8803_nvram_attr);
-
- return 0;
-}
-
static const struct i2c_device_id rv8803_id[] = {
{ "rv8803", rv_8803 },
{ "rx8900", rx_8900 },
@@ -651,7 +638,6 @@ static struct i2c_driver rv8803_driver = {
.of_match_table = of_match_ptr(rv8803_of_match),
},
.probe = rv8803_probe,
- .remove = rv8803_remove,
.id_table = rv8803_id,
};
module_i2c_driver(rv8803_driver);
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index d44fb34df8fe..a8992c227f61 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -41,7 +41,7 @@ struct s3c_rtc {
struct clk *rtc_src_clk;
bool clk_disabled;
- struct s3c_rtc_data *data;
+ const struct s3c_rtc_data *data;
int irq_alarm;
int irq_tick;
@@ -49,7 +49,8 @@ struct s3c_rtc {
spinlock_t pie_lock;
spinlock_t alarm_clk_lock;
- int ticnt_save, ticnt_en_save;
+ int ticnt_save;
+ int ticnt_en_save;
bool wake_en;
};
@@ -67,18 +68,32 @@ struct s3c_rtc_data {
void (*disable) (struct s3c_rtc *info);
};
-static void s3c_rtc_enable_clk(struct s3c_rtc *info)
+static int s3c_rtc_enable_clk(struct s3c_rtc *info)
{
unsigned long irq_flags;
+ int ret = 0;
spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
+
if (info->clk_disabled) {
- clk_enable(info->rtc_clk);
- if (info->data->needs_src_clk)
- clk_enable(info->rtc_src_clk);
+ ret = clk_enable(info->rtc_clk);
+ if (ret)
+ goto out;
+
+ if (info->data->needs_src_clk) {
+ ret = clk_enable(info->rtc_src_clk);
+ if (ret) {
+ clk_disable(info->rtc_clk);
+ goto out;
+ }
+ }
info->clk_disabled = false;
}
+
+out:
spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
+
+ return ret;
}
static void s3c_rtc_disable_clk(struct s3c_rtc *info)
@@ -121,10 +136,13 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
unsigned int tmp;
+ int ret;
dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled);
- s3c_rtc_enable_clk(info);
+ ret = s3c_rtc_enable_clk(info);
+ if (ret)
+ return ret;
tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
@@ -135,10 +153,13 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
s3c_rtc_disable_clk(info);
- if (enabled)
- s3c_rtc_enable_clk(info);
- else
+ if (enabled) {
+ ret = s3c_rtc_enable_clk(info);
+ if (ret)
+ return ret;
+ } else {
s3c_rtc_disable_clk(info);
+ }
return 0;
}
@@ -146,10 +167,14 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
/* Set RTC frequency */
static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq)
{
+ int ret;
+
if (!is_power_of_2(freq))
return -EINVAL;
- s3c_rtc_enable_clk(info);
+ ret = s3c_rtc_enable_clk(info);
+ if (ret)
+ return ret;
spin_lock_irq(&info->pie_lock);
if (info->data->set_freq)
@@ -166,10 +191,13 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
unsigned int have_retried = 0;
+ int ret;
- s3c_rtc_enable_clk(info);
+ ret = s3c_rtc_enable_clk(info);
+ if (ret)
+ return ret;
- retry_get_time:
+retry_get_time:
rtc_tm->tm_min = readb(info->base + S3C2410_RTCMIN);
rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR);
rtc_tm->tm_mday = readb(info->base + S3C2410_RTCDATE);
@@ -199,8 +227,8 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
rtc_tm->tm_year += 100;
dev_dbg(dev, "read time %04d.%02d.%02d %02d:%02d:%02d\n",
- 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
- rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
+ 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
+ rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
rtc_tm->tm_mon -= 1;
@@ -211,10 +239,11 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
int year = tm->tm_year - 100;
+ int ret;
dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n",
- 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
/* we get around y2k by simply not supporting it */
@@ -223,7 +252,9 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
return -EINVAL;
}
- s3c_rtc_enable_clk(info);
+ ret = s3c_rtc_enable_clk(info);
+ if (ret)
+ return ret;
writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_RTCSEC);
writeb(bin2bcd(tm->tm_min), info->base + S3C2410_RTCMIN);
@@ -242,8 +273,11 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
struct s3c_rtc *info = dev_get_drvdata(dev);
struct rtc_time *alm_tm = &alrm->time;
unsigned int alm_en;
+ int ret;
- s3c_rtc_enable_clk(info);
+ ret = s3c_rtc_enable_clk(info);
+ if (ret)
+ return ret;
alm_tm->tm_sec = readb(info->base + S3C2410_ALMSEC);
alm_tm->tm_min = readb(info->base + S3C2410_ALMMIN);
@@ -259,9 +293,9 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
dev_dbg(dev, "read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n",
- alm_en,
- 1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
- alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
+ alm_en,
+ 1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
+ alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
/* decode the alarm enable field */
if (alm_en & S3C2410_RTCALM_SECEN)
@@ -292,14 +326,17 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
struct s3c_rtc *info = dev_get_drvdata(dev);
struct rtc_time *tm = &alrm->time;
unsigned int alrm_en;
+ int ret;
int year = tm->tm_year - 100;
dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
- alrm->enabled,
- 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ alrm->enabled,
+ 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
- s3c_rtc_enable_clk(info);
+ ret = s3c_rtc_enable_clk(info);
+ if (ret)
+ return ret;
alrm_en = readb(info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
writeb(0x00, info->base + S3C2410_RTCALM);
@@ -348,8 +385,11 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
+ int ret;
- s3c_rtc_enable_clk(info);
+ ret = s3c_rtc_enable_clk(info);
+ if (ret)
+ return ret;
if (info->data->enable_tick)
info->data->enable_tick(info, seq);
@@ -378,8 +418,7 @@ static void s3c24xx_rtc_enable(struct s3c_rtc *info)
dev_info(info->dev, "rtc disabled, re-enabling\n");
tmp = readw(info->base + S3C2410_RTCCON);
- writew(tmp | S3C2410_RTCCON_RTCEN,
- info->base + S3C2410_RTCCON);
+ writew(tmp | S3C2410_RTCCON_RTCEN, info->base + S3C2410_RTCCON);
}
if (con & S3C2410_RTCCON_CNTSEL) {
@@ -387,7 +426,7 @@ static void s3c24xx_rtc_enable(struct s3c_rtc *info)
tmp = readw(info->base + S3C2410_RTCCON);
writew(tmp & ~S3C2410_RTCCON_CNTSEL,
- info->base + S3C2410_RTCCON);
+ info->base + S3C2410_RTCCON);
}
if (con & S3C2410_RTCCON_CLKRST) {
@@ -395,7 +434,7 @@ static void s3c24xx_rtc_enable(struct s3c_rtc *info)
tmp = readw(info->base + S3C2410_RTCCON);
writew(tmp & ~S3C2410_RTCCON_CLKRST,
- info->base + S3C2410_RTCCON);
+ info->base + S3C2410_RTCCON);
}
}
@@ -437,12 +476,12 @@ static int s3c_rtc_remove(struct platform_device *pdev)
static const struct of_device_id s3c_rtc_dt_match[];
-static struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev)
+static const struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev)
{
const struct of_device_id *match;
match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node);
- return (struct s3c_rtc_data *)match->data;
+ return match->data;
}
static int s3c_rtc_probe(struct platform_device *pdev)
@@ -481,7 +520,7 @@ static int s3c_rtc_probe(struct platform_device *pdev)
}
dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n",
- info->irq_tick, info->irq_alarm);
+ info->irq_tick, info->irq_alarm);
/* get the memory region */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -498,7 +537,9 @@ static int s3c_rtc_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "probe deferred due to missing rtc clk\n");
return ret;
}
- clk_prepare_enable(info->rtc_clk);
+ ret = clk_prepare_enable(info->rtc_clk);
+ if (ret)
+ return ret;
if (info->data->needs_src_clk) {
info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src");
@@ -510,10 +551,11 @@ static int s3c_rtc_probe(struct platform_device *pdev)
else
dev_dbg(&pdev->dev,
"probe deferred due to missing rtc src clk\n");
- clk_disable_unprepare(info->rtc_clk);
- return ret;
+ goto err_src_clk;
}
- clk_prepare_enable(info->rtc_src_clk);
+ ret = clk_prepare_enable(info->rtc_src_clk);
+ if (ret)
+ goto err_src_clk;
}
/* check to see if everything is setup correctly */
@@ -521,7 +563,7 @@ static int s3c_rtc_probe(struct platform_device *pdev)
info->data->enable(info);
dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n",
- readw(info->base + S3C2410_RTCCON));
+ readw(info->base + S3C2410_RTCCON));
device_init_wakeup(&pdev->dev, 1);
@@ -541,7 +583,7 @@ static int s3c_rtc_probe(struct platform_device *pdev)
/* register RTC and exit */
info->rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops,
- THIS_MODULE);
+ THIS_MODULE);
if (IS_ERR(info->rtc)) {
dev_err(&pdev->dev, "cannot attach rtc\n");
ret = PTR_ERR(info->rtc);
@@ -549,14 +591,14 @@ static int s3c_rtc_probe(struct platform_device *pdev)
}
ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq,
- 0, "s3c2410-rtc alarm", info);
+ 0, "s3c2410-rtc alarm", info);
if (ret) {
dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_alarm, ret);
goto err_nortc;
}
ret = devm_request_irq(&pdev->dev, info->irq_tick, s3c_rtc_tickirq,
- 0, "s3c2410-rtc tick", info);
+ 0, "s3c2410-rtc tick", info);
if (ret) {
dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_tick, ret);
goto err_nortc;
@@ -569,12 +611,13 @@ static int s3c_rtc_probe(struct platform_device *pdev)
return 0;
- err_nortc:
+err_nortc:
if (info->data->disable)
info->data->disable(info);
if (info->data->needs_src_clk)
clk_disable_unprepare(info->rtc_src_clk);
+err_src_clk:
clk_disable_unprepare(info->rtc_clk);
return ret;
@@ -585,8 +628,11 @@ static int s3c_rtc_probe(struct platform_device *pdev)
static int s3c_rtc_suspend(struct device *dev)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
+ int ret;
- s3c_rtc_enable_clk(info);
+ ret = s3c_rtc_enable_clk(info);
+ if (ret)
+ return ret;
/* save TICNT for anyone using periodic interrupts */
if (info->data->save_tick_cnt)
@@ -747,8 +793,7 @@ static void s3c6410_rtc_restore_tick_cnt(struct s3c_rtc *info)
writel(info->ticnt_save, info->base + S3C2410_TICNT);
if (info->ticnt_en_save) {
con = readw(info->base + S3C2410_RTCCON);
- writew(con | info->ticnt_en_save,
- info->base + S3C2410_RTCCON);
+ writew(con | info->ticnt_en_save, info->base + S3C2410_RTCCON);
}
}
@@ -802,19 +847,19 @@ static struct s3c_rtc_data const s3c6410_rtc_data = {
static const struct of_device_id s3c_rtc_dt_match[] = {
{
.compatible = "samsung,s3c2410-rtc",
- .data = (void *)&s3c2410_rtc_data,
+ .data = &s3c2410_rtc_data,
}, {
.compatible = "samsung,s3c2416-rtc",
- .data = (void *)&s3c2416_rtc_data,
+ .data = &s3c2416_rtc_data,
}, {
.compatible = "samsung,s3c2443-rtc",
- .data = (void *)&s3c2443_rtc_data,
+ .data = &s3c2443_rtc_data,
}, {
.compatible = "samsung,s3c6410-rtc",
- .data = (void *)&s3c6410_rtc_data,
+ .data = &s3c6410_rtc_data,
}, {
.compatible = "samsung,exynos3250-rtc",
- .data = (void *)&s3c6410_rtc_data,
+ .data = &s3c6410_rtc_data,
},
{ /* sentinel */ },
};
diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c
index 74c0a336ceea..82b0af159a28 100644
--- a/drivers/rtc/rtc-st-lpc.c
+++ b/drivers/rtc/rtc-st-lpc.c
@@ -99,7 +99,7 @@ static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb;
do_div(lpt, rtc->clkrate);
- rtc_time_to_tm(lpt, tm);
+ rtc_time64_to_tm(lpt, tm);
return 0;
}
@@ -107,13 +107,10 @@ static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int st_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct st_rtc *rtc = dev_get_drvdata(dev);
- unsigned long long lpt;
- unsigned long secs, flags;
- int ret;
+ unsigned long long lpt, secs;
+ unsigned long flags;
- ret = rtc_tm_to_time(tm, &secs);
- if (ret)
- return ret;
+ secs = rtc_tm_to_time64(tm);
lpt = (unsigned long long)secs * rtc->clkrate;
@@ -161,13 +158,13 @@ static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
{
struct st_rtc *rtc = dev_get_drvdata(dev);
struct rtc_time now;
- unsigned long now_secs;
- unsigned long alarm_secs;
+ unsigned long long now_secs;
+ unsigned long long alarm_secs;
unsigned long long lpa;
st_rtc_read_time(dev, &now);
- rtc_tm_to_time(&now, &now_secs);
- rtc_tm_to_time(&t->time, &alarm_secs);
+ now_secs = rtc_tm_to_time64(&now);
+ alarm_secs = rtc_tm_to_time64(&t->time);
/* Invalid alarm time */
if (now_secs > alarm_secs)
diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c
index bd57eb1029e1..3a5c3d7d0c77 100644
--- a/drivers/rtc/rtc-stm32.c
+++ b/drivers/rtc/rtc-stm32.c
@@ -94,11 +94,17 @@
/* STM32_PWR_CR bit field */
#define PWR_CR_DBP BIT(8)
+struct stm32_rtc_data {
+ bool has_pclk;
+};
+
struct stm32_rtc {
struct rtc_device *rtc_dev;
void __iomem *base;
struct regmap *dbp;
- struct clk *ck_rtc;
+ struct stm32_rtc_data *data;
+ struct clk *pclk;
+ struct clk *rtc_ck;
int irq_alarm;
};
@@ -122,9 +128,9 @@ static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc)
writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
/*
- * It takes around 2 ck_rtc clock cycles to enter in
+ * It takes around 2 rtc_ck clock cycles to enter in
* initialization phase mode (and have INITF flag set). As
- * slowest ck_rtc frequency may be 32kHz and highest should be
+ * slowest rtc_ck frequency may be 32kHz and highest should be
* 1MHz, we poll every 10 us with a timeout of 100ms.
*/
return readl_relaxed_poll_timeout_atomic(
@@ -153,7 +159,7 @@ static int stm32_rtc_wait_sync(struct stm32_rtc *rtc)
/*
* Wait for RSF to be set to ensure the calendar registers are
- * synchronised, it takes around 2 ck_rtc clock cycles
+ * synchronised, it takes around 2 rtc_ck clock cycles
*/
return readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
isr,
@@ -456,7 +462,7 @@ static int stm32_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
/*
* Poll Alarm write flag to be sure that Alarm update is allowed: it
- * takes around 2 ck_rtc clock cycles
+ * takes around 2 rtc_ck clock cycles
*/
ret = readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
isr,
@@ -490,8 +496,17 @@ static const struct rtc_class_ops stm32_rtc_ops = {
.alarm_irq_enable = stm32_rtc_alarm_irq_enable,
};
+static const struct stm32_rtc_data stm32_rtc_data = {
+ .has_pclk = false,
+};
+
+static const struct stm32_rtc_data stm32h7_rtc_data = {
+ .has_pclk = true,
+};
+
static const struct of_device_id stm32_rtc_of_match[] = {
- { .compatible = "st,stm32-rtc" },
+ { .compatible = "st,stm32-rtc", .data = &stm32_rtc_data },
+ { .compatible = "st,stm32h7-rtc", .data = &stm32h7_rtc_data },
{}
};
MODULE_DEVICE_TABLE(of, stm32_rtc_of_match);
@@ -503,7 +518,7 @@ static int stm32_rtc_init(struct platform_device *pdev,
unsigned int rate;
int ret = 0;
- rate = clk_get_rate(rtc->ck_rtc);
+ rate = clk_get_rate(rtc->rtc_ck);
/* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */
pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT;
@@ -524,7 +539,7 @@ static int stm32_rtc_init(struct platform_device *pdev,
pred_a = pred_a_max;
pred_s = (rate / (pred_a + 1)) - 1;
- dev_warn(&pdev->dev, "ck_rtc is %s\n",
+ dev_warn(&pdev->dev, "rtc_ck is %s\n",
(rate < ((pred_a + 1) * (pred_s + 1))) ?
"fast" : "slow");
}
@@ -561,6 +576,7 @@ static int stm32_rtc_probe(struct platform_device *pdev)
{
struct stm32_rtc *rtc;
struct resource *res;
+ const struct of_device_id *match;
int ret;
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
@@ -579,15 +595,34 @@ static int stm32_rtc_probe(struct platform_device *pdev)
return PTR_ERR(rtc->dbp);
}
- rtc->ck_rtc = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(rtc->ck_rtc)) {
- dev_err(&pdev->dev, "no ck_rtc clock");
- return PTR_ERR(rtc->ck_rtc);
+ match = of_match_device(stm32_rtc_of_match, &pdev->dev);
+ rtc->data = (struct stm32_rtc_data *)match->data;
+
+ if (!rtc->data->has_pclk) {
+ rtc->pclk = NULL;
+ rtc->rtc_ck = devm_clk_get(&pdev->dev, NULL);
+ } else {
+ rtc->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(rtc->pclk)) {
+ dev_err(&pdev->dev, "no pclk clock");
+ return PTR_ERR(rtc->pclk);
+ }
+ rtc->rtc_ck = devm_clk_get(&pdev->dev, "rtc_ck");
+ }
+ if (IS_ERR(rtc->rtc_ck)) {
+ dev_err(&pdev->dev, "no rtc_ck clock");
+ return PTR_ERR(rtc->rtc_ck);
+ }
+
+ if (rtc->data->has_pclk) {
+ ret = clk_prepare_enable(rtc->pclk);
+ if (ret)
+ return ret;
}
- ret = clk_prepare_enable(rtc->ck_rtc);
+ ret = clk_prepare_enable(rtc->rtc_ck);
if (ret)
- return ret;
+ goto err;
regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, PWR_CR_DBP);
@@ -595,7 +630,7 @@ static int stm32_rtc_probe(struct platform_device *pdev)
* After a system reset, RTC_ISR.INITS flag can be read to check if
* the calendar has been initalized or not. INITS flag is reset by a
* power-on reset (no vbat, no power-supply). It is not reset if
- * ck_rtc parent clock has changed (so RTC prescalers need to be
+ * rtc_ck parent clock has changed (so RTC prescalers need to be
* changed). That's why we cannot rely on this flag to know if RTC
* init has to be done.
*/
@@ -646,7 +681,9 @@ static int stm32_rtc_probe(struct platform_device *pdev)
return 0;
err:
- clk_disable_unprepare(rtc->ck_rtc);
+ if (rtc->data->has_pclk)
+ clk_disable_unprepare(rtc->pclk);
+ clk_disable_unprepare(rtc->rtc_ck);
regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, 0);
@@ -667,7 +704,9 @@ static int stm32_rtc_remove(struct platform_device *pdev)
writel_relaxed(cr, rtc->base + STM32_RTC_CR);
stm32_rtc_wpr_lock(rtc);
- clk_disable_unprepare(rtc->ck_rtc);
+ clk_disable_unprepare(rtc->rtc_ck);
+ if (rtc->data->has_pclk)
+ clk_disable_unprepare(rtc->pclk);
/* Enable backup domain write protection */
regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, 0);
@@ -682,6 +721,9 @@ static int stm32_rtc_suspend(struct device *dev)
{
struct stm32_rtc *rtc = dev_get_drvdata(dev);
+ if (rtc->data->has_pclk)
+ clk_disable_unprepare(rtc->pclk);
+
if (device_may_wakeup(dev))
return enable_irq_wake(rtc->irq_alarm);
@@ -693,6 +735,12 @@ static int stm32_rtc_resume(struct device *dev)
struct stm32_rtc *rtc = dev_get_drvdata(dev);
int ret = 0;
+ if (rtc->data->has_pclk) {
+ ret = clk_prepare_enable(rtc->pclk);
+ if (ret)
+ return ret;
+ }
+
ret = stm32_rtc_wait_sync(rtc);
if (ret < 0)
return ret;
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
index 1218d5d4224d..e364550eb9a7 100644
--- a/drivers/rtc/rtc-sysfs.c
+++ b/drivers/rtc/rtc-sysfs.c
@@ -27,7 +27,8 @@
static ssize_t
name_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- return sprintf(buf, "%s\n", to_rtc_device(dev)->name);
+ return sprintf(buf, "%s %s\n", dev_driver_string(dev->parent),
+ dev_name(dev->parent));
}
static DEVICE_ATTR_RO(name);
diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c
index e389009fca42..a4e3ae8f0c85 100644
--- a/drivers/staging/mt29f_spinand/mt29f_spinand.c
+++ b/drivers/staging/mt29f_spinand/mt29f_spinand.c
@@ -915,6 +915,8 @@ static int spinand_probe(struct spi_device *spi_nand)
chip->waitfunc = spinand_wait;
chip->options |= NAND_CACHEPRG;
chip->select_chip = spinand_select_chip;
+ chip->onfi_set_features = nand_onfi_get_set_features_notsupp;
+ chip->onfi_get_features = nand_onfi_get_set_features_notsupp;
mtd = nand_to_mtd(chip);
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 324c52e3a1a4..063c1ce6fa42 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -195,11 +195,11 @@ static bool vfio_pci_nointx(struct pci_dev *pdev)
switch (pdev->vendor) {
case PCI_VENDOR_ID_INTEL:
switch (pdev->device) {
- /* All i40e (XL710/X710) 10/20/40GbE NICs */
+ /* All i40e (XL710/X710/XXV710) 10/20/25/40GbE NICs */
case 0x1572:
case 0x1574:
case 0x1580 ... 0x1581:
- case 0x1583 ... 0x1589:
+ case 0x1583 ... 0x158b:
case 0x37d0 ... 0x37d2:
return true;
default:
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index 561084ab387f..330d50582f40 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -382,7 +382,7 @@ static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group)
if (IS_ERR(dev)) {
vfio_free_group_minor(minor);
vfio_group_unlock_and_free(group);
- return (struct vfio_group *)dev; /* ERR_PTR */
+ return ERR_CAST(dev);
}
group->minor = minor;
@@ -423,6 +423,34 @@ static void vfio_group_put(struct vfio_group *group)
kref_put_mutex(&group->kref, vfio_group_release, &vfio.group_lock);
}
+struct vfio_group_put_work {
+ struct work_struct work;
+ struct vfio_group *group;
+};
+
+static void vfio_group_put_bg(struct work_struct *work)
+{
+ struct vfio_group_put_work *do_work;
+
+ do_work = container_of(work, struct vfio_group_put_work, work);
+
+ vfio_group_put(do_work->group);
+ kfree(do_work);
+}
+
+static void vfio_group_schedule_put(struct vfio_group *group)
+{
+ struct vfio_group_put_work *do_work;
+
+ do_work = kmalloc(sizeof(*do_work), GFP_KERNEL);
+ if (WARN_ON(!do_work))
+ return;
+
+ INIT_WORK(&do_work->work, vfio_group_put_bg);
+ do_work->group = group;
+ schedule_work(&do_work->work);
+}
+
/* Assume group_lock or group reference is held */
static void vfio_group_get(struct vfio_group *group)
{
@@ -762,7 +790,14 @@ static int vfio_iommu_group_notifier(struct notifier_block *nb,
break;
}
- vfio_group_put(group);
+ /*
+ * If we're the last reference to the group, the group will be
+ * released, which includes unregistering the iommu group notifier.
+ * We hold a read-lock on that notifier list, unregistering needs
+ * a write-lock... deadlock. Release our reference asynchronously
+ * to avoid that situation.
+ */
+ vfio_group_schedule_put(group);
return NOTIFY_OK;
}
@@ -1140,15 +1175,11 @@ static long vfio_fops_unl_ioctl(struct file *filep,
ret = vfio_ioctl_set_iommu(container, arg);
break;
default:
- down_read(&container->group_lock);
-
driver = container->iommu_driver;
data = container->iommu_data;
if (driver) /* passthrough all unrecognized ioctls */
ret = driver->ops->ioctl(data, cmd, arg);
-
- up_read(&container->group_lock);
}
return ret;
@@ -1202,15 +1233,11 @@ static ssize_t vfio_fops_read(struct file *filep, char __user *buf,
struct vfio_iommu_driver *driver;
ssize_t ret = -EINVAL;
- down_read(&container->group_lock);
-
driver = container->iommu_driver;
if (likely(driver && driver->ops->read))
ret = driver->ops->read(container->iommu_data,
buf, count, ppos);
- up_read(&container->group_lock);
-
return ret;
}
@@ -1221,15 +1248,11 @@ static ssize_t vfio_fops_write(struct file *filep, const char __user *buf,
struct vfio_iommu_driver *driver;
ssize_t ret = -EINVAL;
- down_read(&container->group_lock);
-
driver = container->iommu_driver;
if (likely(driver && driver->ops->write))
ret = driver->ops->write(container->iommu_data,
buf, count, ppos);
- up_read(&container->group_lock);
-
return ret;
}
@@ -1239,14 +1262,10 @@ static int vfio_fops_mmap(struct file *filep, struct vm_area_struct *vma)
struct vfio_iommu_driver *driver;
int ret = -EINVAL;
- down_read(&container->group_lock);
-
driver = container->iommu_driver;
if (likely(driver && driver->ops->mmap))
ret = driver->ops->mmap(container->iommu_data, vma);
- up_read(&container->group_lock);
-
return ret;
}
@@ -1741,6 +1760,15 @@ void vfio_group_put_external_user(struct vfio_group *group)
}
EXPORT_SYMBOL_GPL(vfio_group_put_external_user);
+bool vfio_external_group_match_file(struct vfio_group *test_group,
+ struct file *filep)
+{
+ struct vfio_group *group = filep->private_data;
+
+ return (filep->f_op == &vfio_group_fops) && (group == test_group);
+}
+EXPORT_SYMBOL_GPL(vfio_external_group_match_file);
+
int vfio_external_user_iommu_id(struct vfio_group *group)
{
return iommu_group_id(group->iommu_group);
@@ -1949,8 +1977,6 @@ int vfio_pin_pages(struct device *dev, unsigned long *user_pfn, int npage,
goto err_pin_pages;
container = group->container;
- down_read(&container->group_lock);
-
driver = container->iommu_driver;
if (likely(driver && driver->ops->pin_pages))
ret = driver->ops->pin_pages(container->iommu_data, user_pfn,
@@ -1958,7 +1984,6 @@ int vfio_pin_pages(struct device *dev, unsigned long *user_pfn, int npage,
else
ret = -ENOTTY;
- up_read(&container->group_lock);
vfio_group_try_dissolve_container(group);
err_pin_pages:
@@ -1998,8 +2023,6 @@ int vfio_unpin_pages(struct device *dev, unsigned long *user_pfn, int npage)
goto err_unpin_pages;
container = group->container;
- down_read(&container->group_lock);
-
driver = container->iommu_driver;
if (likely(driver && driver->ops->unpin_pages))
ret = driver->ops->unpin_pages(container->iommu_data, user_pfn,
@@ -2007,7 +2030,6 @@ int vfio_unpin_pages(struct device *dev, unsigned long *user_pfn, int npage)
else
ret = -ENOTTY;
- up_read(&container->group_lock);
vfio_group_try_dissolve_container(group);
err_unpin_pages:
@@ -2029,8 +2051,6 @@ static int vfio_register_iommu_notifier(struct vfio_group *group,
return -EINVAL;
container = group->container;
- down_read(&container->group_lock);
-
driver = container->iommu_driver;
if (likely(driver && driver->ops->register_notifier))
ret = driver->ops->register_notifier(container->iommu_data,
@@ -2038,7 +2058,6 @@ static int vfio_register_iommu_notifier(struct vfio_group *group,
else
ret = -ENOTTY;
- up_read(&container->group_lock);
vfio_group_try_dissolve_container(group);
return ret;
@@ -2056,8 +2075,6 @@ static int vfio_unregister_iommu_notifier(struct vfio_group *group,
return -EINVAL;
container = group->container;
- down_read(&container->group_lock);
-
driver = container->iommu_driver;
if (likely(driver && driver->ops->unregister_notifier))
ret = driver->ops->unregister_notifier(container->iommu_data,
@@ -2065,7 +2082,6 @@ static int vfio_unregister_iommu_notifier(struct vfio_group *group,
else
ret = -ENOTTY;
- up_read(&container->group_lock);
vfio_group_try_dissolve_container(group);
return ret;
@@ -2083,7 +2099,6 @@ static int vfio_register_group_notifier(struct vfio_group *group,
unsigned long *events,
struct notifier_block *nb)
{
- struct vfio_container *container;
int ret;
bool set_kvm = false;
@@ -2101,9 +2116,6 @@ static int vfio_register_group_notifier(struct vfio_group *group,
if (ret)
return -EINVAL;
- container = group->container;
- down_read(&container->group_lock);
-
ret = blocking_notifier_chain_register(&group->notifier, nb);
/*
@@ -2114,7 +2126,6 @@ static int vfio_register_group_notifier(struct vfio_group *group,
blocking_notifier_call_chain(&group->notifier,
VFIO_GROUP_NOTIFY_SET_KVM, group->kvm);
- up_read(&container->group_lock);
vfio_group_try_dissolve_container(group);
return ret;
@@ -2123,19 +2134,14 @@ static int vfio_register_group_notifier(struct vfio_group *group,
static int vfio_unregister_group_notifier(struct vfio_group *group,
struct notifier_block *nb)
{
- struct vfio_container *container;
int ret;
ret = vfio_group_add_container_user(group);
if (ret)
return -EINVAL;
- container = group->container;
- down_read(&container->group_lock);
-
ret = blocking_notifier_chain_unregister(&group->notifier, nb);
- up_read(&container->group_lock);
vfio_group_try_dissolve_container(group);
return ret;
diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c
index ec192a1bf297..d0d427a2f1a3 100644
--- a/drivers/video/console/mdacon.c
+++ b/drivers/video/console/mdacon.c
@@ -48,7 +48,7 @@ static DEFINE_SPINLOCK(mda_lock);
/* description of the hardware layout */
-static unsigned long mda_vram_base; /* Base of video memory */
+static u16 *mda_vram_base; /* Base of video memory */
static unsigned long mda_vram_len; /* Size of video memory */
static unsigned int mda_num_columns; /* Number of text columns */
static unsigned int mda_num_lines; /* Number of text lines */
@@ -205,13 +205,20 @@ static int mda_detect(void)
/* do a memory check */
- p = (u16 *) mda_vram_base;
- q = (u16 *) (mda_vram_base + 0x01000);
+ p = mda_vram_base;
+ q = mda_vram_base + 0x01000 / 2;
- p_save = scr_readw(p); q_save = scr_readw(q);
+ p_save = scr_readw(p);
+ q_save = scr_readw(q);
+
+ scr_writew(0xAA55, p);
+ if (scr_readw(p) == 0xAA55)
+ count++;
+
+ scr_writew(0x55AA, p);
+ if (scr_readw(p) == 0x55AA)
+ count++;
- scr_writew(0xAA55, p); if (scr_readw(p) == 0xAA55) count++;
- scr_writew(0x55AA, p); if (scr_readw(p) == 0x55AA) count++;
scr_writew(p_save, p);
if (count != 2) {
@@ -220,13 +227,18 @@ static int mda_detect(void)
/* check if we have 4K or 8K */
- scr_writew(0xA55A, q); scr_writew(0x0000, p);
- if (scr_readw(q) == 0xA55A) count++;
+ scr_writew(0xA55A, q);
+ scr_writew(0x0000, p);
+ if (scr_readw(q) == 0xA55A)
+ count++;
- scr_writew(0x5AA5, q); scr_writew(0x0000, p);
- if (scr_readw(q) == 0x5AA5) count++;
+ scr_writew(0x5AA5, q);
+ scr_writew(0x0000, p);
+ if (scr_readw(q) == 0x5AA5)
+ count++;
- scr_writew(p_save, p); scr_writew(q_save, q);
+ scr_writew(p_save, p);
+ scr_writew(q_save, q);
if (count == 4) {
mda_vram_len = 0x02000;
@@ -240,14 +252,12 @@ static int mda_detect(void)
/* Edward: These two mess `tests' mess up my cursor on bootup */
/* cursor low register */
- if (! test_mda_b(0x66, 0x0f)) {
+ if (!test_mda_b(0x66, 0x0f))
return 0;
- }
/* cursor low register */
- if (! test_mda_b(0x99, 0x0f)) {
+ if (!test_mda_b(0x99, 0x0f))
return 0;
- }
#endif
/* See if the card is a Hercules, by checking whether the vsync
@@ -257,25 +267,25 @@ static int mda_detect(void)
p_save = q_save = inb_p(mda_status_port) & MDA_STATUS_VSYNC;
- for (count=0; count < 50000 && p_save == q_save; count++) {
+ for (count = 0; count < 50000 && p_save == q_save; count++) {
q_save = inb(mda_status_port) & MDA_STATUS_VSYNC;
udelay(2);
}
if (p_save != q_save) {
switch (inb_p(mda_status_port) & 0x70) {
- case 0x10:
- mda_type = TYPE_HERCPLUS;
- mda_type_name = "HerculesPlus";
- break;
- case 0x50:
- mda_type = TYPE_HERCCOLOR;
- mda_type_name = "HerculesColor";
- break;
- default:
- mda_type = TYPE_HERC;
- mda_type_name = "Hercules";
- break;
+ case 0x10:
+ mda_type = TYPE_HERCPLUS;
+ mda_type_name = "HerculesPlus";
+ break;
+ case 0x50:
+ mda_type = TYPE_HERCCOLOR;
+ mda_type_name = "HerculesColor";
+ break;
+ default:
+ mda_type = TYPE_HERC;
+ mda_type_name = "Hercules";
+ break;
}
}
@@ -313,7 +323,7 @@ static const char *mdacon_startup(void)
mda_num_lines = 25;
mda_vram_len = 0x01000;
- mda_vram_base = VGA_MAP_MEM(0xb0000, mda_vram_len);
+ mda_vram_base = (u16 *)VGA_MAP_MEM(0xb0000, mda_vram_len);
mda_index_port = 0x3b4;
mda_value_port = 0x3b5;
@@ -410,17 +420,20 @@ static void mdacon_invert_region(struct vc_data *c, u16 *p, int count)
}
}
-#define MDA_ADDR(x,y) ((u16 *) mda_vram_base + (y)*mda_num_columns + (x))
+static inline u16 *mda_addr(unsigned int x, unsigned int y)
+{
+ return mda_vram_base + y * mda_num_columns + x;
+}
static void mdacon_putc(struct vc_data *c, int ch, int y, int x)
{
- scr_writew(mda_convert_attr(ch), MDA_ADDR(x, y));
+ scr_writew(mda_convert_attr(ch), mda_addr(x, y));
}
static void mdacon_putcs(struct vc_data *c, const unsigned short *s,
int count, int y, int x)
{
- u16 *dest = MDA_ADDR(x, y);
+ u16 *dest = mda_addr(x, y);
for (; count > 0; count--) {
scr_writew(mda_convert_attr(scr_readw(s++)), dest++);
@@ -430,7 +443,7 @@ static void mdacon_putcs(struct vc_data *c, const unsigned short *s,
static void mdacon_clear(struct vc_data *c, int y, int x,
int height, int width)
{
- u16 *dest = MDA_ADDR(x, y);
+ u16 *dest = mda_addr(x, y);
u16 eattr = mda_convert_attr(c->vc_video_erase_char);
if (width <= 0 || height <= 0)
@@ -453,7 +466,7 @@ static int mdacon_blank(struct vc_data *c, int blank, int mode_switch)
{
if (mda_type == TYPE_MDA) {
if (blank)
- scr_memsetw((void *)mda_vram_base,
+ scr_memsetw(mda_vram_base,
mda_convert_attr(c->vc_video_erase_char),
c->vc_screenbuf_size);
/* Tell console.c that it has to restore the screen itself */
@@ -502,16 +515,16 @@ static bool mdacon_scroll(struct vc_data *c, unsigned int t, unsigned int b,
switch (dir) {
case SM_UP:
- scr_memmovew(MDA_ADDR(0,t), MDA_ADDR(0,t+lines),
+ scr_memmovew(mda_addr(0, t), mda_addr(0, t + lines),
(b-t-lines)*mda_num_columns*2);
- scr_memsetw(MDA_ADDR(0,b-lines), eattr,
+ scr_memsetw(mda_addr(0, b - lines), eattr,
lines*mda_num_columns*2);
break;
case SM_DOWN:
- scr_memmovew(MDA_ADDR(0,t+lines), MDA_ADDR(0,t),
+ scr_memmovew(mda_addr(0, t + lines), mda_addr(0, t),
(b-t-lines)*mda_num_columns*2);
- scr_memsetw(MDA_ADDR(0,t), eattr, lines*mda_num_columns*2);
+ scr_memsetw(mda_addr(0, t), eattr, lines*mda_num_columns*2);
break;
}
diff --git a/drivers/video/fbdev/aty/atyfb_base.c b/drivers/video/fbdev/aty/atyfb_base.c
index 11026e726b68..b55fdac9c9f5 100644
--- a/drivers/video/fbdev/aty/atyfb_base.c
+++ b/drivers/video/fbdev/aty/atyfb_base.c
@@ -802,7 +802,7 @@ static int aty_var_to_crtc(const struct fb_info *info,
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp;
- u32 sync, vmode, vdisplay;
+ u32 sync, vmode;
u32 h_total, h_disp, h_sync_strt, h_sync_end, h_sync_dly, h_sync_wid, h_sync_pol;
u32 v_total, v_disp, v_sync_strt, v_sync_end, v_sync_wid, v_sync_pol, c_sync;
u32 pix_width, dp_pix_width, dp_chain_mask;
@@ -984,12 +984,6 @@ static int aty_var_to_crtc(const struct fb_info *info,
v_total <<= 1;
}
- vdisplay = yres;
-#ifdef CONFIG_FB_ATY_GENERIC_LCD
- if ((par->lcd_table != 0) && (crtc->lcd_gen_cntl & LCD_ON))
- vdisplay = par->lcd_height;
-#endif
-
v_disp--;
v_sync_strt--;
v_sync_end--;
@@ -1036,7 +1030,7 @@ static int aty_var_to_crtc(const struct fb_info *info,
crtc->gen_cntl |= CRTC_INTERLACE_EN;
#ifdef CONFIG_FB_ATY_GENERIC_LCD
if (par->lcd_table != 0) {
- vdisplay = yres;
+ u32 vdisplay = yres;
if (vmode & FB_VMODE_DOUBLE)
vdisplay <<= 1;
crtc->gen_cntl &= ~(CRTC2_EN | CRTC2_PIX_WIDTH);
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
index 5324358f110f..7a42238db446 100644
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -1483,7 +1483,7 @@ __releases(&info->lock)
return 0;
}
-#ifdef CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA
+#if defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && !defined(CONFIG_MMU)
unsigned long get_fb_unmapped_area(struct file *filp,
unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags)
@@ -1510,7 +1510,8 @@ static const struct file_operations fb_fops = {
.open = fb_open,
.release = fb_release,
#if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \
- defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA)
+ (defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \
+ !defined(CONFIG_MMU))
.get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
diff --git a/drivers/video/fbdev/fsl-diu-fb.c b/drivers/video/fbdev/fsl-diu-fb.c
index ca3d6b366471..25abbcf38913 100644
--- a/drivers/video/fbdev/fsl-diu-fb.c
+++ b/drivers/video/fbdev/fsl-diu-fb.c
@@ -388,7 +388,7 @@ struct fsl_diu_data {
/* Determine the DMA address of a member of the fsl_diu_data structure */
#define DMA_ADDR(p, f) ((p)->dma_addr + offsetof(struct fsl_diu_data, f))
-static struct mfb_info mfb_template[] = {
+static const struct mfb_info mfb_template[] = {
{
.index = PLANE0,
.id = "Panel0",
@@ -1868,7 +1868,7 @@ static int __init fsl_diu_setup(char *options)
}
#endif
-static struct of_device_id fsl_diu_match[] = {
+static const struct of_device_id fsl_diu_match[] = {
#ifdef CONFIG_PPC_MPC512x
{
.compatible = "fsl,mpc5121-diu",
diff --git a/drivers/video/fbdev/matrox/matroxfb_base.c b/drivers/video/fbdev/matrox/matroxfb_base.c
index 11eb094396ae..f6a0b9af97a9 100644
--- a/drivers/video/fbdev/matrox/matroxfb_base.c
+++ b/drivers/video/fbdev/matrox/matroxfb_base.c
@@ -2001,7 +2001,7 @@ static void matroxfb_register_device(struct matrox_fb_info* minfo) {
for (drv = matroxfb_driver_l(matroxfb_driver_list.next);
drv != matroxfb_driver_l(&matroxfb_driver_list);
drv = matroxfb_driver_l(drv->node.next)) {
- if (drv && drv->probe) {
+ if (drv->probe) {
void *p = drv->probe(minfo);
if (p) {
minfo->drivers_data[i] = p;
diff --git a/drivers/video/fbdev/omap/omapfb_main.c b/drivers/video/fbdev/omap/omapfb_main.c
index f4cbfb3b8a09..3479a47a3082 100644
--- a/drivers/video/fbdev/omap/omapfb_main.c
+++ b/drivers/video/fbdev/omap/omapfb_main.c
@@ -62,7 +62,7 @@ struct caps_table_struct {
const char *name;
};
-static struct caps_table_struct ctrl_caps[] = {
+static const struct caps_table_struct ctrl_caps[] = {
{ OMAPFB_CAPS_MANUAL_UPDATE, "manual update" },
{ OMAPFB_CAPS_TEARSYNC, "tearing synchronization" },
{ OMAPFB_CAPS_PLANE_RELOCATE_MEM, "relocate plane memory" },
@@ -74,7 +74,7 @@ static struct caps_table_struct ctrl_caps[] = {
{ OMAPFB_CAPS_SET_BACKLIGHT, "backlight setting" },
};
-static struct caps_table_struct color_caps[] = {
+static const struct caps_table_struct color_caps[] = {
{ 1 << OMAPFB_COLOR_RGB565, "RGB565", },
{ 1 << OMAPFB_COLOR_YUV422, "YUV422", },
{ 1 << OMAPFB_COLOR_YUV420, "YUV420", },
@@ -1384,7 +1384,7 @@ static struct attribute *panel_attrs[] = {
NULL,
};
-static struct attribute_group panel_attr_grp = {
+static const struct attribute_group panel_attr_grp = {
.name = "panel",
.attrs = panel_attrs,
};
@@ -1406,7 +1406,7 @@ static struct attribute *ctrl_attrs[] = {
NULL,
};
-static struct attribute_group ctrl_attr_grp = {
+static const struct attribute_group ctrl_attr_grp = {
.name = "ctrl",
.attrs = ctrl_attrs,
};
diff --git a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
index fd2b372d0264..bef431530090 100644
--- a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
+++ b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
@@ -100,7 +100,7 @@ static void hw_guard_wait(struct panel_drv_data *ddata)
{
unsigned long wait = ddata->hw_guard_end - jiffies;
- if ((long)wait > 0 && wait <= ddata->hw_guard_wait) {
+ if ((long)wait > 0 && time_before_eq(wait, ddata->hw_guard_wait)) {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(wait);
}
@@ -559,7 +559,7 @@ static struct attribute *dsicm_attrs[] = {
NULL,
};
-static struct attribute_group dsicm_attr_group = {
+static const struct attribute_group dsicm_attr_group = {
.attrs = dsicm_attrs,
};
diff --git a/drivers/video/fbdev/omap2/omapfb/dss/manager-sysfs.c b/drivers/video/fbdev/omap2/omapfb/dss/manager-sysfs.c
index 9e2a67fdf4d2..44b96af4ef4e 100644
--- a/drivers/video/fbdev/omap2/omapfb/dss/manager-sysfs.c
+++ b/drivers/video/fbdev/omap2/omapfb/dss/manager-sysfs.c
@@ -182,22 +182,16 @@ static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr,
static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
- enum omap_dss_trans_key_type key_type;
struct omap_overlay_manager_info info;
int r;
- for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
- key_type < ARRAY_SIZE(trans_key_type_str); key_type++) {
- if (sysfs_streq(buf, trans_key_type_str[key_type]))
- break;
- }
-
- if (key_type == ARRAY_SIZE(trans_key_type_str))
- return -EINVAL;
+ r = sysfs_match_string(trans_key_type_str, buf);
+ if (r < 0)
+ return r;
mgr->get_manager_info(mgr, &info);
- info.trans_key_type = key_type;
+ info.trans_key_type = r;
r = mgr->set_manager_info(mgr, &info);
if (r)
diff --git a/drivers/video/fbdev/pxafb.c b/drivers/video/fbdev/pxafb.c
index b21a89b03fb4..c3d49e13643c 100644
--- a/drivers/video/fbdev/pxafb.c
+++ b/drivers/video/fbdev/pxafb.c
@@ -1436,7 +1436,10 @@ static void pxafb_enable_controller(struct pxafb_info *fbi)
pr_debug("reg_lccr3 0x%08x\n", (unsigned int) fbi->reg_lccr3);
/* enable LCD controller clock */
- clk_prepare_enable(fbi->clk);
+ if (clk_prepare_enable(fbi->clk)) {
+ pr_err("%s: Failed to prepare clock\n", __func__);
+ return;
+ }
if (fbi->lccr0 & LCCR0_LCDT)
return;
diff --git a/drivers/video/fbdev/sh_mobile_lcdcfb.c b/drivers/video/fbdev/sh_mobile_lcdcfb.c
index 885ee3a563aa..c3a46506e47e 100644
--- a/drivers/video/fbdev/sh_mobile_lcdcfb.c
+++ b/drivers/video/fbdev/sh_mobile_lcdcfb.c
@@ -2301,7 +2301,7 @@ static int sh_mobile_lcdc_check_fb(struct backlight_device *bdev,
return (info->bl_dev == bdev);
}
-static struct backlight_ops sh_mobile_lcdc_bl_ops = {
+static const struct backlight_ops sh_mobile_lcdc_bl_ops = {
.options = BL_CORE_SUSPENDRESUME,
.update_status = sh_mobile_lcdc_update_bl,
.get_brightness = sh_mobile_lcdc_get_brightness,
diff --git a/drivers/video/fbdev/uvesafb.c b/drivers/video/fbdev/uvesafb.c
index 98af9e02959b..dc0e8d90d9cc 100644
--- a/drivers/video/fbdev/uvesafb.c
+++ b/drivers/video/fbdev/uvesafb.c
@@ -5,6 +5,9 @@
* Loosely based upon the vesafb driver.
*
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -149,8 +152,8 @@ static int uvesafb_exec(struct uvesafb_ktask *task)
* allowed by connector.
*/
if (sizeof(*m) + len > CONNECTOR_MAX_MSG_SIZE) {
- printk(KERN_WARNING "uvesafb: message too long (%d), "
- "can't execute task\n", (int)(sizeof(*m) + len));
+ pr_warn("message too long (%d), can't execute task\n",
+ (int)(sizeof(*m) + len));
return -E2BIG;
}
@@ -198,10 +201,8 @@ static int uvesafb_exec(struct uvesafb_ktask *task)
*/
err = uvesafb_helper_start();
if (err) {
- printk(KERN_ERR "uvesafb: failed to execute %s\n",
- v86d_path);
- printk(KERN_ERR "uvesafb: make sure that the v86d "
- "helper is installed and executable\n");
+ pr_err("failed to execute %s\n", v86d_path);
+ pr_err("make sure that the v86d helper is installed and executable\n");
} else {
v86d_started = 1;
err = cn_netlink_send(m, 0, 0, gfp_any());
@@ -375,9 +376,8 @@ static u8 *uvesafb_vbe_state_save(struct uvesafb_par *par)
err = uvesafb_exec(task);
if (err || (task->t.regs.eax & 0xffff) != 0x004f) {
- printk(KERN_WARNING "uvesafb: VBE get state call "
- "failed (eax=0x%x, err=%d)\n",
- task->t.regs.eax, err);
+ pr_warn("VBE get state call failed (eax=0x%x, err=%d)\n",
+ task->t.regs.eax, err);
kfree(state);
state = NULL;
}
@@ -407,9 +407,8 @@ static void uvesafb_vbe_state_restore(struct uvesafb_par *par, u8 *state_buf)
err = uvesafb_exec(task);
if (err || (task->t.regs.eax & 0xffff) != 0x004f)
- printk(KERN_WARNING "uvesafb: VBE state restore call "
- "failed (eax=0x%x, err=%d)\n",
- task->t.regs.eax, err);
+ pr_warn("VBE state restore call failed (eax=0x%x, err=%d)\n",
+ task->t.regs.eax, err);
uvesafb_free(task);
}
@@ -427,24 +426,22 @@ static int uvesafb_vbe_getinfo(struct uvesafb_ktask *task,
err = uvesafb_exec(task);
if (err || (task->t.regs.eax & 0xffff) != 0x004f) {
- printk(KERN_ERR "uvesafb: Getting VBE info block failed "
- "(eax=0x%x, err=%d)\n", (u32)task->t.regs.eax,
- err);
+ pr_err("Getting VBE info block failed (eax=0x%x, err=%d)\n",
+ (u32)task->t.regs.eax, err);
return -EINVAL;
}
if (par->vbe_ib.vbe_version < 0x0200) {
- printk(KERN_ERR "uvesafb: Sorry, pre-VBE 2.0 cards are "
- "not supported.\n");
+ pr_err("Sorry, pre-VBE 2.0 cards are not supported\n");
return -EINVAL;
}
if (!par->vbe_ib.mode_list_ptr) {
- printk(KERN_ERR "uvesafb: Missing mode list!\n");
+ pr_err("Missing mode list!\n");
return -EINVAL;
}
- printk(KERN_INFO "uvesafb: ");
+ pr_info("");
/*
* Convert string pointers and the mode list pointer into
@@ -452,23 +449,24 @@ static int uvesafb_vbe_getinfo(struct uvesafb_ktask *task,
* video adapter and its vendor.
*/
if (par->vbe_ib.oem_vendor_name_ptr)
- printk("%s, ",
+ pr_cont("%s, ",
((char *)task->buf) + par->vbe_ib.oem_vendor_name_ptr);
if (par->vbe_ib.oem_product_name_ptr)
- printk("%s, ",
+ pr_cont("%s, ",
((char *)task->buf) + par->vbe_ib.oem_product_name_ptr);
if (par->vbe_ib.oem_product_rev_ptr)
- printk("%s, ",
+ pr_cont("%s, ",
((char *)task->buf) + par->vbe_ib.oem_product_rev_ptr);
if (par->vbe_ib.oem_string_ptr)
- printk("OEM: %s, ",
+ pr_cont("OEM: %s, ",
((char *)task->buf) + par->vbe_ib.oem_string_ptr);
- printk("VBE v%d.%d\n", ((par->vbe_ib.vbe_version & 0xff00) >> 8),
- par->vbe_ib.vbe_version & 0xff);
+ pr_cont("VBE v%d.%d\n",
+ (par->vbe_ib.vbe_version & 0xff00) >> 8,
+ par->vbe_ib.vbe_version & 0xff);
return 0;
}
@@ -507,8 +505,7 @@ static int uvesafb_vbe_getmodes(struct uvesafb_ktask *task,
err = uvesafb_exec(task);
if (err || (task->t.regs.eax & 0xffff) != 0x004f) {
- printk(KERN_WARNING "uvesafb: Getting mode info block "
- "for mode 0x%x failed (eax=0x%x, err=%d)\n",
+ pr_warn("Getting mode info block for mode 0x%x failed (eax=0x%x, err=%d)\n",
*mode, (u32)task->t.regs.eax, err);
mode++;
par->vbe_modes_cnt--;
@@ -569,23 +566,20 @@ static int uvesafb_vbe_getpmi(struct uvesafb_ktask *task,
+ task->t.regs.edi);
par->pmi_start = (u8 *)par->pmi_base + par->pmi_base[1];
par->pmi_pal = (u8 *)par->pmi_base + par->pmi_base[2];
- printk(KERN_INFO "uvesafb: protected mode interface info at "
- "%04x:%04x\n",
- (u16)task->t.regs.es, (u16)task->t.regs.edi);
- printk(KERN_INFO "uvesafb: pmi: set display start = %p, "
- "set palette = %p\n", par->pmi_start,
- par->pmi_pal);
+ pr_info("protected mode interface info at %04x:%04x\n",
+ (u16)task->t.regs.es, (u16)task->t.regs.edi);
+ pr_info("pmi: set display start = %p, set palette = %p\n",
+ par->pmi_start, par->pmi_pal);
if (par->pmi_base[3]) {
- printk(KERN_INFO "uvesafb: pmi: ports = ");
+ pr_info("pmi: ports =");
for (i = par->pmi_base[3]/2;
par->pmi_base[i] != 0xffff; i++)
- printk("%x ", par->pmi_base[i]);
- printk("\n");
+ pr_cont(" %x", par->pmi_base[i]);
+ pr_cont("\n");
if (par->pmi_base[i] != 0xffff) {
- printk(KERN_INFO "uvesafb: can't handle memory"
- " requests, pmi disabled\n");
+ pr_info("can't handle memory requests, pmi disabled\n");
par->ypan = par->pmi_setpal = 0;
}
}
@@ -634,17 +628,13 @@ static int uvesafb_vbe_getedid(struct uvesafb_ktask *task, struct fb_info *info)
return -EINVAL;
if ((task->t.regs.ebx & 0x3) == 3) {
- printk(KERN_INFO "uvesafb: VBIOS/hardware supports both "
- "DDC1 and DDC2 transfers\n");
+ pr_info("VBIOS/hardware supports both DDC1 and DDC2 transfers\n");
} else if ((task->t.regs.ebx & 0x3) == 2) {
- printk(KERN_INFO "uvesafb: VBIOS/hardware supports DDC2 "
- "transfers\n");
+ pr_info("VBIOS/hardware supports DDC2 transfers\n");
} else if ((task->t.regs.ebx & 0x3) == 1) {
- printk(KERN_INFO "uvesafb: VBIOS/hardware supports DDC1 "
- "transfers\n");
+ pr_info("VBIOS/hardware supports DDC1 transfers\n");
} else {
- printk(KERN_INFO "uvesafb: VBIOS/hardware doesn't support "
- "DDC transfers\n");
+ pr_info("VBIOS/hardware doesn't support DDC transfers\n");
return -EINVAL;
}
@@ -718,14 +708,12 @@ static void uvesafb_vbe_getmonspecs(struct uvesafb_ktask *task,
}
if (info->monspecs.gtf)
- printk(KERN_INFO
- "uvesafb: monitor limits: vf = %d Hz, hf = %d kHz, "
- "clk = %d MHz\n", info->monspecs.vfmax,
+ pr_info("monitor limits: vf = %d Hz, hf = %d kHz, clk = %d MHz\n",
+ info->monspecs.vfmax,
(int)(info->monspecs.hfmax / 1000),
(int)(info->monspecs.dclkmax / 1000000));
else
- printk(KERN_INFO "uvesafb: no monitor limits have been set, "
- "default refresh rate will be used\n");
+ pr_info("no monitor limits have been set, default refresh rate will be used\n");
/* Add VBE modes to the modelist. */
for (i = 0; i < par->vbe_modes_cnt; i++) {
@@ -779,8 +767,7 @@ static void uvesafb_vbe_getstatesize(struct uvesafb_ktask *task,
err = uvesafb_exec(task);
if (err || (task->t.regs.eax & 0xffff) != 0x004f) {
- printk(KERN_WARNING "uvesafb: VBE state buffer size "
- "cannot be determined (eax=0x%x, err=%d)\n",
+ pr_warn("VBE state buffer size cannot be determined (eax=0x%x, err=%d)\n",
task->t.regs.eax, err);
par->vbe_state_size = 0;
return;
@@ -815,8 +802,7 @@ static int uvesafb_vbe_init(struct fb_info *info)
if (par->pmi_setpal || par->ypan) {
if (__supported_pte_mask & _PAGE_NX) {
par->pmi_setpal = par->ypan = 0;
- printk(KERN_WARNING "uvesafb: NX protection is active, "
- "better not use the PMI.\n");
+ pr_warn("NX protection is active, better not use the PMI\n");
} else {
uvesafb_vbe_getpmi(task, par);
}
@@ -859,8 +845,7 @@ static int uvesafb_vbe_init_mode(struct fb_info *info)
goto gotmode;
}
}
- printk(KERN_INFO "uvesafb: requested VBE mode 0x%x is "
- "unavailable\n", vbemode);
+ pr_info("requested VBE mode 0x%x is unavailable\n", vbemode);
vbemode = 0;
}
@@ -1181,8 +1166,8 @@ static int uvesafb_open(struct fb_info *info, int user)
if (!cnt && par->vbe_state_size) {
buf = uvesafb_vbe_state_save(par);
if (IS_ERR(buf)) {
- printk(KERN_WARNING "uvesafb: save hardware state"
- "failed, error code is %ld!\n", PTR_ERR(buf));
+ pr_warn("save hardware state failed, error code is %ld!\n",
+ PTR_ERR(buf));
} else {
par->vbe_state_orig = buf;
}
@@ -1293,17 +1278,16 @@ setmode:
* use our own timings. Try again with the default timings.
*/
if (crtc != NULL) {
- printk(KERN_WARNING "uvesafb: mode switch failed "
- "(eax=0x%x, err=%d). Trying again with "
- "default timings.\n", task->t.regs.eax, err);
+ pr_warn("mode switch failed (eax=0x%x, err=%d) - trying again with default timings\n",
+ task->t.regs.eax, err);
uvesafb_reset(task);
kfree(crtc);
crtc = NULL;
info->var.pixclock = 0;
goto setmode;
} else {
- printk(KERN_ERR "uvesafb: mode switch failed (eax="
- "0x%x, err=%d)\n", task->t.regs.eax, err);
+ pr_err("mode switch failed (eax=0x%x, err=%d)\n",
+ task->t.regs.eax, err);
err = -EINVAL;
goto out;
}
@@ -1510,13 +1494,11 @@ static void uvesafb_init_info(struct fb_info *info, struct vbe_mode_ib *mode)
mode->bytes_per_scan_line;
if (par->ypan && info->var.yres_virtual > info->var.yres) {
- printk(KERN_INFO "uvesafb: scrolling: %s "
- "using protected mode interface, "
- "yres_virtual=%d\n",
+ pr_info("scrolling: %s using protected mode interface, yres_virtual=%d\n",
(par->ypan > 1) ? "ywrap" : "ypan",
info->var.yres_virtual);
} else {
- printk(KERN_INFO "uvesafb: scrolling: redraw\n");
+ pr_info("scrolling: redraw\n");
info->var.yres_virtual = info->var.yres;
par->ypan = 0;
}
@@ -1704,7 +1686,7 @@ static int uvesafb_probe(struct platform_device *dev)
err = uvesafb_vbe_init(info);
if (err) {
- printk(KERN_ERR "uvesafb: vbe_init() failed with %d\n", err);
+ pr_err("vbe_init() failed with %d\n", err);
goto out;
}
@@ -1726,15 +1708,15 @@ static int uvesafb_probe(struct platform_device *dev)
uvesafb_init_info(info, mode);
if (!request_region(0x3c0, 32, "uvesafb")) {
- printk(KERN_ERR "uvesafb: request region 0x3c0-0x3e0 failed\n");
+ pr_err("request region 0x3c0-0x3e0 failed\n");
err = -EIO;
goto out_mode;
}
if (!request_mem_region(info->fix.smem_start, info->fix.smem_len,
"uvesafb")) {
- printk(KERN_ERR "uvesafb: cannot reserve video memory at "
- "0x%lx\n", info->fix.smem_start);
+ pr_err("cannot reserve video memory at 0x%lx\n",
+ info->fix.smem_start);
err = -EIO;
goto out_reg;
}
@@ -1743,10 +1725,8 @@ static int uvesafb_probe(struct platform_device *dev)
uvesafb_ioremap(info);
if (!info->screen_base) {
- printk(KERN_ERR
- "uvesafb: abort, cannot ioremap 0x%x bytes of video "
- "memory at 0x%lx\n",
- info->fix.smem_len, info->fix.smem_start);
+ pr_err("abort, cannot ioremap 0x%x bytes of video memory at 0x%lx\n",
+ info->fix.smem_len, info->fix.smem_start);
err = -EIO;
goto out_mem;
}
@@ -1754,16 +1734,14 @@ static int uvesafb_probe(struct platform_device *dev)
platform_set_drvdata(dev, info);
if (register_framebuffer(info) < 0) {
- printk(KERN_ERR
- "uvesafb: failed to register framebuffer device\n");
+ pr_err("failed to register framebuffer device\n");
err = -EINVAL;
goto out_unmap;
}
- printk(KERN_INFO "uvesafb: framebuffer at 0x%lx, mapped to 0x%p, "
- "using %dk, total %dk\n", info->fix.smem_start,
- info->screen_base, info->fix.smem_len/1024,
- par->vbe_ib.total_memory * 64);
+ pr_info("framebuffer at 0x%lx, mapped to 0x%p, using %dk, total %dk\n",
+ info->fix.smem_start, info->screen_base,
+ info->fix.smem_len / 1024, par->vbe_ib.total_memory * 64);
fb_info(info, "%s frame buffer device\n", info->fix.id);
err = sysfs_create_group(&dev->dev.kobj, &uvesafb_dev_attgrp);
@@ -1871,8 +1849,7 @@ static int uvesafb_setup(char *options)
else if (this_opt[0] >= '0' && this_opt[0] <= '9') {
mode_option = this_opt;
} else {
- printk(KERN_WARNING
- "uvesafb: unrecognized option %s\n", this_opt);
+ pr_warn("unrecognized option %s\n", this_opt);
}
}
@@ -1931,8 +1908,7 @@ static int uvesafb_init(void)
err = driver_create_file(&uvesafb_driver.driver,
&driver_attr_v86d);
if (err) {
- printk(KERN_WARNING "uvesafb: failed to register "
- "attributes\n");
+ pr_warn("failed to register attributes\n");
err = 0;
}
}
diff --git a/drivers/video/fbdev/vermilion/cr_pll.c b/drivers/video/fbdev/vermilion/cr_pll.c
index ebc6e6e0dd0f..ba105c876bed 100644
--- a/drivers/video/fbdev/vermilion/cr_pll.c
+++ b/drivers/video/fbdev/vermilion/cr_pll.c
@@ -185,6 +185,7 @@ static int __init cr_pll_init(void)
if (err) {
printk(KERN_ERR
"Carillo Ranch failed to initialize vml_sys.\n");
+ iounmap(mch_regs_base);
pci_dev_put(mch_dev);
return err;
}