aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/apei/erst.c1
-rw-r--r--drivers/ata/Kconfig11
-rw-r--r--drivers/ata/Makefile2
-rw-r--r--drivers/ata/pata_mpc52xx.c8
-rw-r--r--drivers/ata/pata_of_platform.c4
-rw-r--r--drivers/ata/pata_pxa.c411
-rw-r--r--drivers/ata/sata_fsl.c8
-rw-r--r--drivers/atm/fore200e.c26
-rw-r--r--drivers/block/xsysace.c4
-rw-r--r--drivers/char/Kconfig2
-rw-r--r--drivers/char/hw_random/n2-drv.c4
-rw-r--r--drivers/char/hw_random/n2rng.h2
-rw-r--r--drivers/char/hw_random/pasemi-rng.c4
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c61
-rw-r--r--drivers/char/n_gsm.c2
-rw-r--r--drivers/char/nozomi.c3
-rw-r--r--drivers/char/rtc.c2
-rw-r--r--drivers/char/xilinx_hwicap/xilinx_hwicap.c4
-rw-r--r--drivers/crypto/amcc/crypto4xx_core.c4
-rw-r--r--drivers/crypto/amcc/crypto4xx_core.h2
-rw-r--r--drivers/crypto/ixp4xx_crypto.c21
-rw-r--r--drivers/crypto/n2_core.c26
-rw-r--r--drivers/crypto/talitos.c6
-rw-r--r--drivers/dma/Kconfig2
-rw-r--r--drivers/dma/fsldma.c4
-rw-r--r--drivers/dma/mpc512x_dma.c4
-rw-r--r--drivers/dma/ppc4xx/adma.c8
-rw-r--r--drivers/dma/shdma.c8
-rw-r--r--drivers/edac/i5000_edac.c2
-rw-r--r--drivers/edac/i5400_edac.c2
-rw-r--r--drivers/edac/mpc85xx_edac.c28
-rw-r--r--drivers/edac/ppc4xx_edac.c12
-rw-r--r--drivers/gpio/Kconfig18
-rw-r--r--drivers/gpio/Makefile2
-rw-r--r--drivers/gpio/gpiolib.c96
-rw-r--r--drivers/gpio/max730x.c22
-rw-r--r--drivers/gpio/pcf857x.c9
-rw-r--r--drivers/gpio/stmpe-gpio.c399
-rw-r--r--drivers/gpio/sx150x.c645
-rw-r--r--drivers/gpio/wm831x-gpio.c32
-rw-r--r--drivers/gpu/drm/ati_pcigart.c2
-rw-r--r--drivers/gpu/drm/drm_bufs.c33
-rw-r--r--drivers/gpu/drm/drm_crtc.c6
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c10
-rw-r--r--drivers/gpu/drm/drm_edid.c840
-rw-r--r--drivers/gpu/drm/drm_edid_modes.h380
-rw-r--r--drivers/gpu/drm/drm_fops.c16
-rw-r--r--drivers/gpu/drm/drm_gem.c2
-rw-r--r--drivers/gpu/drm/drm_ioctl.c1
-rw-r--r--drivers/gpu/drm/i2c/ch7006_drv.c1
-rw-r--r--drivers/gpu/drm/i2c/ch7006_mode.c5
-rw-r--r--drivers/gpu/drm/i2c/ch7006_priv.h1
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c26
-rw-r--r--drivers/gpu/drm/i915/i915_gem_tiling.c4
-rw-r--r--drivers/gpu/drm/i915/intel_display.c15
-rw-r--r--drivers/gpu/drm/i915/intel_fb.c4
-rw-r--r--drivers/gpu/drm/nouveau/Makefile8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.c194
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c19
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c87
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h45
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_encoder.h6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fbcon.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.c8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_hw.c11
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_i2c.c41
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_i2c.h8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_irq.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mem.c15
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_reg.h18
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_state.c91
-rw-r--r--drivers/gpu/drm/nouveau/nv04_crtc.c15
-rw-r--r--drivers/gpu/drm/nouveau/nv04_dfp.c73
-rw-r--r--drivers/gpu/drm/nouveau/nv04_tv.c10
-rw-r--r--drivers/gpu/drm/nouveau/nv10_graph.c175
-rw-r--r--drivers/gpu/drm/nouveau/nv30_fb.c24
-rw-r--r--drivers/gpu/drm/nouveau/nv50_crtc.c34
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fb.c38
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fifo.c96
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_graph.c75
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_instmem.c232
-rw-r--r--drivers/gpu/drm/radeon/r600_cp.c2
-rw-r--r--drivers/gpu/drm/radeon/r600_cs.c33
-rw-r--r--drivers/gpu/drm/radeon/radeon.h2
-rw-r--r--drivers/gpu/drm/radeon/radeon_atombios.c180
-rw-r--r--drivers/gpu/drm/radeon/radeon_clocks.c81
-rw-r--r--drivers/gpu/drm/radeon/radeon_combios.c388
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c45
-rw-r--r--drivers/gpu/drm/radeon/radeon_cs.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_cursor.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c34
-rw-r--r--drivers/gpu/drm/radeon/radeon_gem.c12
-rw-r--r--drivers/gpu/drm/radeon/radeon_i2c.c82
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c28
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h24
-rw-r--r--drivers/gpu/drm/radeon/radeon_pm.c24
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/rv5151
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.c8
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c2
-rw-r--r--drivers/hwmon/ams/ams.h2
-rw-r--r--drivers/hwmon/coretemp.c3
-rw-r--r--drivers/hwmon/mc13783-adc.c17
-rw-r--r--drivers/hwmon/ultra45_env.c4
-rw-r--r--drivers/i2c/busses/Kconfig13
-rw-r--r--drivers/i2c/busses/i2c-cpm.c8
-rw-r--r--drivers/i2c/busses/i2c-ibm_iic.c6
-rw-r--r--drivers/i2c/busses/i2c-mpc.c4
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c121
-rw-r--r--drivers/infiniband/hw/ehca/ehca_classes.h2
-rw-r--r--drivers/infiniband/hw/ehca/ehca_main.c4
-rw-r--r--drivers/input/keyboard/Kconfig10
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/stmpe-keypad.c386
-rw-r--r--drivers/input/misc/sparcspkr.c10
-rw-r--r--drivers/input/serio/i8042-sparcio.h8
-rw-r--r--drivers/input/serio/i8042.c25
-rw-r--r--drivers/input/serio/xilinx_ps2.c4
-rw-r--r--drivers/input/touchscreen/Kconfig10
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/stmpe-ts.c397
-rw-r--r--drivers/leds/leds-gpio.c4
-rw-r--r--drivers/macintosh/macio_sysfs.c6
-rw-r--r--drivers/macintosh/smu.c6
-rw-r--r--drivers/macintosh/therm_adt746x.c2
-rw-r--r--drivers/macintosh/therm_pm72.c6
-rw-r--r--drivers/macintosh/therm_windtunnel.c6
-rw-r--r--drivers/media/video/fsl-viu.c8
-rw-r--r--drivers/memstick/core/mspro_block.c6
-rw-r--r--drivers/message/fusion/mptbase.c238
-rw-r--r--drivers/message/fusion/mptbase.h25
-rw-r--r--drivers/message/i2o/exec-osm.c8
-rw-r--r--drivers/message/i2o/i2o_block.c3
-rw-r--r--drivers/message/i2o/i2o_config.c18
-rw-r--r--drivers/message/i2o/i2o_scsi.c3
-rw-r--r--drivers/mfd/88pm860x-core.c84
-rw-r--r--drivers/mfd/Kconfig75
-rw-r--r--drivers/mfd/Makefile5
-rw-r--r--drivers/mfd/ab3100-otp.c16
-rw-r--r--drivers/mfd/ab3550-core.c23
-rw-r--r--drivers/mfd/ab8500-core.c4
-rw-r--r--drivers/mfd/ab8500-spi.c7
-rw-r--r--drivers/mfd/abx500-core.c2
-rw-r--r--drivers/mfd/davinci_voicecodec.c6
-rw-r--r--drivers/mfd/janz-cmodio.c1
-rw-r--r--drivers/mfd/jz4740-adc.c394
-rw-r--r--drivers/mfd/max8925-core.c27
-rw-r--r--drivers/mfd/max8998.c158
-rw-r--r--drivers/mfd/mc13783-core.c30
-rw-r--r--drivers/mfd/menelaus.c75
-rw-r--r--drivers/mfd/mfd-core.c4
-rw-r--r--drivers/mfd/stmpe.c985
-rw-r--r--drivers/mfd/stmpe.h183
-rw-r--r--drivers/mfd/t7l66xb.c3
-rw-r--r--drivers/mfd/tc6387xb.c16
-rw-r--r--drivers/mfd/tc6393xb.c4
-rw-r--r--drivers/mfd/tps6507x.c4
-rw-r--r--drivers/mfd/tps6586x.c375
-rw-r--r--drivers/mfd/twl6030-pwm.c163
-rw-r--r--drivers/mfd/ucb1400_core.c2
-rw-r--r--drivers/mfd/wm831x-core.c18
-rw-r--r--drivers/mfd/wm8350-core.c6
-rw-r--r--drivers/mfd/wm8994-core.c8
-rw-r--r--drivers/misc/cs5535-mfgpt.c11
-rw-r--r--drivers/mmc/card/block.c82
-rw-r--r--drivers/mmc/card/mmc_test.c811
-rw-r--r--drivers/mmc/card/queue.c19
-rw-r--r--drivers/mmc/core/bus.c9
-rw-r--r--drivers/mmc/core/core.c441
-rw-r--r--drivers/mmc/core/core.h2
-rw-r--r--drivers/mmc/core/host.c4
-rw-r--r--drivers/mmc/core/mmc.c75
-rw-r--r--drivers/mmc/core/sd.c331
-rw-r--r--drivers/mmc/core/sd.h17
-rw-r--r--drivers/mmc/core/sd_ops.c48
-rw-r--r--drivers/mmc/core/sd_ops.h1
-rw-r--r--drivers/mmc/core/sdio.c210
-rw-r--r--drivers/mmc/host/Kconfig9
-rw-r--r--drivers/mmc/host/Makefile5
-rw-r--r--drivers/mmc/host/mmc_spi.c1
-rw-r--r--drivers/mmc/host/msm_sdcc.c25
-rw-r--r--drivers/mmc/host/msm_sdcc.h4
-rw-r--r--drivers/mmc/host/omap_hsmmc.c47
-rw-r--r--drivers/mmc/host/sdhci-cns3xxx.c97
-rw-r--r--drivers/mmc/host/sdhci-of-core.c12
-rw-r--r--drivers/mmc/host/sdhci-pci.c49
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c17
-rw-r--r--drivers/mmc/host/sdhci-pltfm.h18
-rw-r--r--drivers/mmc/host/sdhci-s3c.c123
-rw-r--r--drivers/mmc/host/sdhci.c53
-rw-r--r--drivers/mmc/host/sdhci.h10
-rw-r--r--drivers/mtd/maps/physmap_of.c8
-rw-r--r--drivers/mtd/maps/sun_uflash.c6
-rw-r--r--drivers/mtd/nand/fsl_elbc_nand.c4
-rw-r--r--drivers/mtd/nand/fsl_upm.c4
-rw-r--r--drivers/mtd/nand/mpc5121_nfc.c4
-rw-r--r--drivers/mtd/nand/nand_base.c39
-rw-r--r--drivers/mtd/nand/ndfc.c6
-rw-r--r--drivers/mtd/nand/pasemi_nand.c4
-rw-r--r--drivers/mtd/nand/socrates_nand.c4
-rw-r--r--drivers/net/arm/ixp4xx_eth.c2
-rw-r--r--drivers/net/can/mscan/mpc5xxx_can.c18
-rw-r--r--drivers/net/can/sja1000/sja1000_of_platform.c4
-rw-r--r--drivers/net/ehea/ehea.h4
-rw-r--r--drivers/net/ehea/ehea_main.c12
-rw-r--r--drivers/net/fec_mpc52xx.c8
-rw-r--r--drivers/net/fec_mpc52xx_phy.c4
-rw-r--r--drivers/net/fs_enet/fs_enet-main.c4
-rw-r--r--drivers/net/fs_enet/mac-fcc.c2
-rw-r--r--drivers/net/fs_enet/mac-fec.c2
-rw-r--r--drivers/net/fs_enet/mac-scc.c2
-rw-r--r--drivers/net/fs_enet/mii-bitbang.c4
-rw-r--r--drivers/net/fs_enet/mii-fec.c4
-rw-r--r--drivers/net/fsl_pq_mdio.c4
-rw-r--r--drivers/net/gianfar.c10
-rw-r--r--drivers/net/gianfar.h2
-rw-r--r--drivers/net/greth.c6
-rw-r--r--drivers/net/greth.h2
-rw-r--r--drivers/net/ibm_newemac/core.c6
-rw-r--r--drivers/net/ibm_newemac/core.h12
-rw-r--r--drivers/net/ibm_newemac/mal.c4
-rw-r--r--drivers/net/ibm_newemac/mal.h2
-rw-r--r--drivers/net/ibm_newemac/rgmii.c18
-rw-r--r--drivers/net/ibm_newemac/rgmii.h16
-rw-r--r--drivers/net/ibm_newemac/tah.c14
-rw-r--r--drivers/net/ibm_newemac/tah.h12
-rw-r--r--drivers/net/ibm_newemac/zmii.c18
-rw-r--r--drivers/net/ibm_newemac/zmii.h16
-rw-r--r--drivers/net/irda/sh_irda.c6
-rw-r--r--drivers/net/ll_temac_main.c8
-rw-r--r--drivers/net/myri_sbus.c4
-rw-r--r--drivers/net/myri_sbus.h2
-rw-r--r--drivers/net/niu.c8
-rw-r--r--drivers/net/phy/mdio-gpio.c4
-rw-r--r--drivers/net/sunbmac.c18
-rw-r--r--drivers/net/sunbmac.h4
-rw-r--r--drivers/net/sunhme.c22
-rw-r--r--drivers/net/sunhme.h2
-rw-r--r--drivers/net/sunlance.c20
-rw-r--r--drivers/net/sunqe.c16
-rw-r--r--drivers/net/sunqe.h4
-rw-r--r--drivers/net/ucc_geth.c8
-rw-r--r--drivers/net/wan/ixp4xx_hss.c2
-rw-r--r--drivers/net/wireless/libertas/cfg.c2
-rw-r--r--drivers/net/xilinx_emaclite.c6
-rw-r--r--drivers/of/device.c2
-rw-r--r--drivers/parport/parport_serial.c1
-rw-r--r--drivers/parport/parport_sunbpp.c4
-rw-r--r--drivers/pcmcia/Kconfig2
-rw-r--r--drivers/pcmcia/Makefile1
-rw-r--r--drivers/pcmcia/electra_cf.c6
-rw-r--r--drivers/pcmcia/m8xx_pcmcia.c4
-rw-r--r--drivers/pcmcia/pxa2xx_balloon3.c158
-rw-r--r--drivers/power/Kconfig13
-rw-r--r--drivers/power/Makefile2
-rw-r--r--drivers/power/intel_mid_battery.c799
-rw-r--r--drivers/power/olpc_battery.c2
-rw-r--r--drivers/power/s3c_adc_battery.c431
-rw-r--r--drivers/power/wm97xx_battery.c16
-rw-r--r--drivers/regulator/Kconfig34
-rw-r--r--drivers/regulator/Makefile5
-rw-r--r--drivers/regulator/ab8500.c427
-rw-r--r--drivers/regulator/ad5398.c288
-rw-r--r--drivers/regulator/isl6271a-regulator.c236
-rw-r--r--drivers/regulator/lp3971.c10
-rw-r--r--drivers/regulator/max1586.c10
-rw-r--r--drivers/regulator/max8660.c10
-rw-r--r--drivers/regulator/max8998.c635
-rw-r--r--drivers/regulator/tps65023-regulator.c2
-rw-r--r--drivers/regulator/tps6507x-regulator.c1
-rw-r--r--drivers/regulator/tps6586x-regulator.c396
-rw-r--r--drivers/regulator/wm8994-regulator.c5
-rw-r--r--drivers/rtc/Kconfig38
-rw-r--r--drivers/rtc/Makefile3
-rw-r--r--drivers/rtc/rtc-cmos.c6
-rw-r--r--drivers/rtc/rtc-ds3232.c326
-rw-r--r--drivers/rtc/rtc-fm3130.c181
-rw-r--r--drivers/rtc/rtc-imxdi.c519
-rw-r--r--drivers/rtc/rtc-isl12022.c327
-rw-r--r--drivers/rtc/rtc-m41t80.c4
-rw-r--r--drivers/rtc/rtc-m48t59.c5
-rw-r--r--drivers/rtc/rtc-m48t86.c2
-rw-r--r--drivers/rtc/rtc-max6900.c2
-rw-r--r--drivers/rtc/rtc-mpc5121.c4
-rw-r--r--drivers/rtc/rtc-mxc.c6
-rw-r--r--drivers/rtc/rtc-nuc900.c64
-rw-r--r--drivers/rtc/rtc-pcf8563.c8
-rw-r--r--drivers/rtc/rtc-pl031.c1
-rw-r--r--drivers/rtc/rtc-pxa.c42
-rw-r--r--drivers/rtc/rtc-rp5c01.c89
-rw-r--r--drivers/rtc/rtc-s3c.c44
-rw-r--r--drivers/sbus/char/bbc_envctrl.c6
-rw-r--r--drivers/sbus/char/bbc_i2c.c18
-rw-r--r--drivers/sbus/char/bbc_i2c.h10
-rw-r--r--drivers/sbus/char/display7seg.c4
-rw-r--r--drivers/sbus/char/envctrl.c4
-rw-r--r--drivers/sbus/char/flash.c4
-rw-r--r--drivers/sbus/char/uctrl.c4
-rw-r--r--drivers/scsi/53c700.c3
-rw-r--r--drivers/scsi/Kconfig2
-rw-r--r--drivers/scsi/NCR5380.c4
-rw-r--r--drivers/scsi/aacraid/rx.c5
-rw-r--r--drivers/scsi/aic94xx/aic94xx_init.c4
-rw-r--r--drivers/scsi/bfa/bfa_fcport.c2
-rw-r--r--drivers/scsi/bfa/bfad_im.c4
-rw-r--r--drivers/scsi/bfa/include/protocol/fcp.h4
-rw-r--r--drivers/scsi/bnx2i/Kconfig3
-rw-r--r--drivers/scsi/ch.c89
-rw-r--r--drivers/scsi/dc395x.c2
-rw-r--r--drivers/scsi/fnic/fnic.h2
-rw-r--r--drivers/scsi/g_NCR5380.c47
-rw-r--r--drivers/scsi/g_NCR5380.h6
-rw-r--r--drivers/scsi/gdth.c2
-rw-r--r--drivers/scsi/initio.c1
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c23
-rw-r--r--drivers/scsi/osst.c3
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c4
-rw-r--r--drivers/scsi/qlogicpti.c14
-rw-r--r--drivers/scsi/qlogicpti.h2
-rw-r--r--drivers/scsi/scsi_lib.c6
-rw-r--r--drivers/scsi/scsi_transport_fc.c12
-rw-r--r--drivers/scsi/sd.c12
-rw-r--r--drivers/scsi/sg.c11
-rw-r--r--drivers/scsi/sun_esp.c44
-rw-r--r--drivers/serial/Kconfig4
-rw-r--r--drivers/serial/apbuart.c2
-rw-r--r--drivers/serial/cpm_uart/cpm_uart_core.c4
-rw-r--r--drivers/serial/mpc52xx_uart.c8
-rw-r--r--drivers/serial/nwpserial.c2
-rw-r--r--drivers/serial/of_serial.c6
-rw-r--r--drivers/serial/s5pv210.c8
-rw-r--r--drivers/serial/samsung.c9
-rw-r--r--drivers/serial/sh-sci.c42
-rw-r--r--drivers/serial/sh-sci.h29
-rw-r--r--drivers/serial/sunhv.c4
-rw-r--r--drivers/serial/sunsab.c8
-rw-r--r--drivers/serial/sunsu.c8
-rw-r--r--drivers/serial/sunzilog.c6
-rw-r--r--drivers/serial/uartlite.c4
-rw-r--r--drivers/serial/ucc_uart.c4
-rw-r--r--drivers/sh/Makefile5
-rw-r--r--drivers/sh/clk-cpg.c58
-rw-r--r--drivers/spi/mpc512x_psc_spi.c4
-rw-r--r--drivers/spi/mpc52xx_psc_spi.c4
-rw-r--r--drivers/spi/mpc52xx_spi.c4
-rw-r--r--drivers/spi/spi_mpc8xxx.c4
-rw-r--r--drivers/spi/spi_ppc4xx.c6
-rw-r--r--drivers/spi/xilinx_spi_of.c6
-rw-r--r--drivers/usb/gadget/fsl_qe_udc.c10
-rw-r--r--drivers/usb/host/ehci-ppc-of.c6
-rw-r--r--drivers/usb/host/ehci-xilinx-of.c12
-rw-r--r--drivers/usb/host/fhci-hcd.c4
-rw-r--r--drivers/usb/host/isp1760-if.c4
-rw-r--r--drivers/usb/host/ohci-ppc-of.c6
-rw-r--r--drivers/video/Kconfig17
-rw-r--r--drivers/video/Makefile2
-rw-r--r--drivers/video/bw2.c4
-rw-r--r--drivers/video/cg14.c6
-rw-r--r--drivers/video/cg3.c4
-rw-r--r--drivers/video/cg6.c6
-rw-r--r--drivers/video/console/bitblit.c2
-rw-r--r--drivers/video/console/fbcon.c2
-rw-r--r--drivers/video/console/fbcon_ccw.c2
-rw-r--r--drivers/video/console/fbcon_cw.c2
-rw-r--r--drivers/video/console/fbcon_ud.c2
-rw-r--r--drivers/video/efifb.c8
-rw-r--r--drivers/video/fbmem.c6
-rw-r--r--drivers/video/ffb.c4
-rw-r--r--drivers/video/fsl-diu-fb.c8
-rw-r--r--drivers/video/igafb.c5
-rw-r--r--drivers/video/imxfb.c19
-rw-r--r--drivers/video/leo.c6
-rw-r--r--drivers/video/mb862xx/mb862xxfb.c4
-rw-r--r--drivers/video/msm/mddi.c4
-rw-r--r--drivers/video/msm/mdp.c1
-rw-r--r--drivers/video/p9100.c4
-rw-r--r--drivers/video/platinumfb.c4
-rw-r--r--drivers/video/s3c-fb.c811
-rw-r--r--drivers/video/sh_mipi_dsi.c505
-rw-r--r--drivers/video/sh_mobile_hdmi.c1028
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c196
-rw-r--r--drivers/video/sunxvr1000.c4
-rw-r--r--drivers/video/tcx.c6
-rw-r--r--drivers/video/xilinxfb.c4
-rw-r--r--drivers/watchdog/cpwd.c4
-rw-r--r--drivers/watchdog/gef_wdt.c2
-rw-r--r--drivers/watchdog/mpc8xxx_wdt.c4
-rw-r--r--drivers/watchdog/riowd.c4
-rw-r--r--drivers/xen/Kconfig5
-rw-r--r--drivers/xen/Makefile1
-rw-r--r--drivers/xen/balloon.c15
-rw-r--r--drivers/xen/swiotlb-xen.c515
396 files changed, 18784 insertions, 2825 deletions
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index 864dd46c346f..18645f4e83cd 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -33,6 +33,7 @@
#include <linux/uaccess.h>
#include <linux/cper.h>
#include <linux/nmi.h>
+#include <linux/hardirq.h>
#include <acpi/apei.h>
#include "apei-internal.h"
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 8fae6afd6a3d..65e3e2708371 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -651,6 +651,17 @@ config PATA_VIA
If unsure, say N.
+config PATA_PXA
+ tristate "PXA DMA-capable PATA support"
+ depends on ARCH_PXA
+ help
+ This option enables support for harddrive attached to PXA CPU's bus.
+
+ NOTE: This driver utilizes PXA DMA controller, in case your hardware
+ is not capable of doing MWDMA, use pata_platform instead.
+
+ If unsure, say N.
+
config PATA_WINBOND
tristate "Winbond SL82C105 PATA support"
depends on PCI
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 6540632bda08..158eaa961b1e 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -91,6 +91,8 @@ obj-$(CONFIG_PATA_RZ1000) += pata_rz1000.o
obj-$(CONFIG_PATA_SAMSUNG_CF) += pata_samsung_cf.o
obj-$(CONFIG_PATA_WINBOND_VLB) += pata_winbond.o
+obj-$(CONFIG_PATA_PXA) += pata_pxa.o
+
# Should be last but two libata driver
obj-$(CONFIG_PATA_ACPI) += pata_acpi.o
# Should be last but one libata driver
diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c
index f087ab55b1df..8cc536e49a0a 100644
--- a/drivers/ata/pata_mpc52xx.c
+++ b/drivers/ata/pata_mpc52xx.c
@@ -680,7 +680,7 @@ mpc52xx_ata_remove_one(struct device *dev)
/* ======================================================================== */
static int __devinit
-mpc52xx_ata_probe(struct of_device *op, const struct of_device_id *match)
+mpc52xx_ata_probe(struct platform_device *op, const struct of_device_id *match)
{
unsigned int ipb_freq;
struct resource res_mem;
@@ -821,7 +821,7 @@ mpc52xx_ata_probe(struct of_device *op, const struct of_device_id *match)
}
static int
-mpc52xx_ata_remove(struct of_device *op)
+mpc52xx_ata_remove(struct platform_device *op)
{
struct mpc52xx_ata_priv *priv;
int task_irq;
@@ -848,7 +848,7 @@ mpc52xx_ata_remove(struct of_device *op)
#ifdef CONFIG_PM
static int
-mpc52xx_ata_suspend(struct of_device *op, pm_message_t state)
+mpc52xx_ata_suspend(struct platform_device *op, pm_message_t state)
{
struct ata_host *host = dev_get_drvdata(&op->dev);
@@ -856,7 +856,7 @@ mpc52xx_ata_suspend(struct of_device *op, pm_message_t state)
}
static int
-mpc52xx_ata_resume(struct of_device *op)
+mpc52xx_ata_resume(struct platform_device *op)
{
struct ata_host *host = dev_get_drvdata(&op->dev);
struct mpc52xx_ata_priv *priv = host->private_data;
diff --git a/drivers/ata/pata_of_platform.c b/drivers/ata/pata_of_platform.c
index 5a1b82c08be9..480e043ce6b8 100644
--- a/drivers/ata/pata_of_platform.c
+++ b/drivers/ata/pata_of_platform.c
@@ -14,7 +14,7 @@
#include <linux/of_platform.h>
#include <linux/ata_platform.h>
-static int __devinit pata_of_platform_probe(struct of_device *ofdev,
+static int __devinit pata_of_platform_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
int ret;
@@ -78,7 +78,7 @@ static int __devinit pata_of_platform_probe(struct of_device *ofdev,
reg_shift, pio_mask);
}
-static int __devexit pata_of_platform_remove(struct of_device *ofdev)
+static int __devexit pata_of_platform_remove(struct platform_device *ofdev)
{
return __pata_platform_remove(&ofdev->dev);
}
diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c
new file mode 100644
index 000000000000..1898c6ed4b4e
--- /dev/null
+++ b/drivers/ata/pata_pxa.c
@@ -0,0 +1,411 @@
+/*
+ * Generic PXA PATA driver
+ *
+ * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/ata.h>
+#include <linux/libata.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+
+#include <scsi/scsi_host.h>
+
+#include <mach/pxa2xx-regs.h>
+#include <mach/pata_pxa.h>
+#include <mach/dma.h>
+
+#define DRV_NAME "pata_pxa"
+#define DRV_VERSION "0.1"
+
+struct pata_pxa_data {
+ uint32_t dma_channel;
+ struct pxa_dma_desc *dma_desc;
+ dma_addr_t dma_desc_addr;
+ uint32_t dma_desc_id;
+
+ /* DMA IO physical address */
+ uint32_t dma_io_addr;
+ /* PXA DREQ<0:2> pin selector */
+ uint32_t dma_dreq;
+ /* DMA DCSR register value */
+ uint32_t dma_dcsr;
+
+ struct completion dma_done;
+};
+
+/*
+ * Setup the DMA descriptors. The size is transfer capped at 4k per descriptor,
+ * if the transfer is longer, it is split into multiple chained descriptors.
+ */
+static void pxa_load_dmac(struct scatterlist *sg, struct ata_queued_cmd *qc)
+{
+ struct pata_pxa_data *pd = qc->ap->private_data;
+
+ uint32_t cpu_len, seg_len;
+ dma_addr_t cpu_addr;
+
+ cpu_addr = sg_dma_address(sg);
+ cpu_len = sg_dma_len(sg);
+
+ do {
+ seg_len = (cpu_len > 0x1000) ? 0x1000 : cpu_len;
+
+ pd->dma_desc[pd->dma_desc_id].ddadr = pd->dma_desc_addr +
+ ((pd->dma_desc_id + 1) * sizeof(struct pxa_dma_desc));
+
+ pd->dma_desc[pd->dma_desc_id].dcmd = DCMD_BURST32 |
+ DCMD_WIDTH2 | (DCMD_LENGTH & seg_len);
+
+ if (qc->tf.flags & ATA_TFLAG_WRITE) {
+ pd->dma_desc[pd->dma_desc_id].dsadr = cpu_addr;
+ pd->dma_desc[pd->dma_desc_id].dtadr = pd->dma_io_addr;
+ pd->dma_desc[pd->dma_desc_id].dcmd |= DCMD_INCSRCADDR |
+ DCMD_FLOWTRG;
+ } else {
+ pd->dma_desc[pd->dma_desc_id].dsadr = pd->dma_io_addr;
+ pd->dma_desc[pd->dma_desc_id].dtadr = cpu_addr;
+ pd->dma_desc[pd->dma_desc_id].dcmd |= DCMD_INCTRGADDR |
+ DCMD_FLOWSRC;
+ }
+
+ cpu_len -= seg_len;
+ cpu_addr += seg_len;
+ pd->dma_desc_id++;
+
+ } while (cpu_len);
+
+ /* Should not happen */
+ if (seg_len & 0x1f)
+ DALGN |= (1 << pd->dma_dreq);
+}
+
+/*
+ * Prepare taskfile for submission.
+ */
+static void pxa_qc_prep(struct ata_queued_cmd *qc)
+{
+ struct pata_pxa_data *pd = qc->ap->private_data;
+ int si = 0;
+ struct scatterlist *sg;
+
+ if (!(qc->flags & ATA_QCFLAG_DMAMAP))
+ return;
+
+ pd->dma_desc_id = 0;
+
+ DCSR(pd->dma_channel) = 0;
+ DALGN &= ~(1 << pd->dma_dreq);
+
+ for_each_sg(qc->sg, sg, qc->n_elem, si)
+ pxa_load_dmac(sg, qc);
+
+ pd->dma_desc[pd->dma_desc_id - 1].ddadr = DDADR_STOP;
+
+ /* Fire IRQ only at the end of last block */
+ pd->dma_desc[pd->dma_desc_id - 1].dcmd |= DCMD_ENDIRQEN;
+
+ DDADR(pd->dma_channel) = pd->dma_desc_addr;
+ DRCMR(pd->dma_dreq) = DRCMR_MAPVLD | pd->dma_channel;
+
+}
+
+/*
+ * Configure the DMA controller, load the DMA descriptors, but don't start the
+ * DMA controller yet. Only issue the ATA command.
+ */
+static void pxa_bmdma_setup(struct ata_queued_cmd *qc)
+{
+ qc->ap->ops->sff_exec_command(qc->ap, &qc->tf);
+}
+
+/*
+ * Execute the DMA transfer.
+ */
+static void pxa_bmdma_start(struct ata_queued_cmd *qc)
+{
+ struct pata_pxa_data *pd = qc->ap->private_data;
+ init_completion(&pd->dma_done);
+ DCSR(pd->dma_channel) = DCSR_RUN;
+}
+
+/*
+ * Wait until the DMA transfer completes, then stop the DMA controller.
+ */
+static void pxa_bmdma_stop(struct ata_queued_cmd *qc)
+{
+ struct pata_pxa_data *pd = qc->ap->private_data;
+
+ if ((DCSR(pd->dma_channel) & DCSR_RUN) &&
+ wait_for_completion_timeout(&pd->dma_done, HZ))
+ dev_err(qc->ap->dev, "Timeout waiting for DMA completion!");
+
+ DCSR(pd->dma_channel) = 0;
+}
+
+/*
+ * Read DMA status. The bmdma_stop() will take care of properly finishing the
+ * DMA transfer so we always have DMA-complete interrupt here.
+ */
+static unsigned char pxa_bmdma_status(struct ata_port *ap)
+{
+ struct pata_pxa_data *pd = ap->private_data;
+ unsigned char ret = ATA_DMA_INTR;
+
+ if (pd->dma_dcsr & DCSR_BUSERR)
+ ret |= ATA_DMA_ERR;
+
+ return ret;
+}
+
+/*
+ * No IRQ register present so we do nothing.
+ */
+static void pxa_irq_clear(struct ata_port *ap)
+{
+}
+
+/*
+ * Check for ATAPI DMA. ATAPI DMA is unsupported by this driver. It's still
+ * unclear why ATAPI has DMA issues.
+ */
+static int pxa_check_atapi_dma(struct ata_queued_cmd *qc)
+{
+ return -EOPNOTSUPP;
+}
+
+static struct scsi_host_template pxa_ata_sht = {
+ ATA_BMDMA_SHT(DRV_NAME),
+};
+
+static struct ata_port_operations pxa_ata_port_ops = {
+ .inherits = &ata_bmdma_port_ops,
+ .cable_detect = ata_cable_40wire,
+
+ .bmdma_setup = pxa_bmdma_setup,
+ .bmdma_start = pxa_bmdma_start,
+ .bmdma_stop = pxa_bmdma_stop,
+ .bmdma_status = pxa_bmdma_status,
+
+ .check_atapi_dma = pxa_check_atapi_dma,
+
+ .sff_irq_clear = pxa_irq_clear,
+
+ .qc_prep = pxa_qc_prep,
+};
+
+/*
+ * DMA interrupt handler.
+ */
+static void pxa_ata_dma_irq(int dma, void *port)
+{
+ struct ata_port *ap = port;
+ struct pata_pxa_data *pd = ap->private_data;
+
+ pd->dma_dcsr = DCSR(dma);
+ DCSR(dma) = pd->dma_dcsr;
+
+ if (pd->dma_dcsr & DCSR_STOPSTATE)
+ complete(&pd->dma_done);
+}
+
+static int __devinit pxa_ata_probe(struct platform_device *pdev)
+{
+ struct ata_host *host;
+ struct ata_port *ap;
+ struct pata_pxa_data *data;
+ struct resource *cmd_res;
+ struct resource *ctl_res;
+ struct resource *dma_res;
+ struct resource *irq_res;
+ struct pata_pxa_pdata *pdata = pdev->dev.platform_data;
+ int ret = 0;
+
+ /*
+ * Resource validation, three resources are needed:
+ * - CMD port base address
+ * - CTL port base address
+ * - DMA port base address
+ * - IRQ pin
+ */
+ if (pdev->num_resources != 4) {
+ dev_err(&pdev->dev, "invalid number of resources\n");
+ return -EINVAL;
+ }
+
+ /*
+ * CMD port base address
+ */
+ cmd_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(cmd_res == NULL))
+ return -EINVAL;
+
+ /*
+ * CTL port base address
+ */
+ ctl_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (unlikely(ctl_res == NULL))
+ return -EINVAL;
+
+ /*
+ * DMA port base address
+ */
+ dma_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (unlikely(dma_res == NULL))
+ return -EINVAL;
+
+ /*
+ * IRQ pin
+ */
+ irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (unlikely(irq_res == NULL))
+ return -EINVAL;
+
+ /*
+ * Allocate the host
+ */
+ host = ata_host_alloc(&pdev->dev, 1);
+ if (!host)
+ return -ENOMEM;
+
+ ap = host->ports[0];
+ ap->ops = &pxa_ata_port_ops;
+ ap->pio_mask = ATA_PIO4;
+ ap->mwdma_mask = ATA_MWDMA2;
+ ap->flags = ATA_FLAG_MMIO;
+
+ ap->ioaddr.cmd_addr = devm_ioremap(&pdev->dev, cmd_res->start,
+ resource_size(cmd_res));
+ ap->ioaddr.ctl_addr = devm_ioremap(&pdev->dev, ctl_res->start,
+ resource_size(ctl_res));
+ ap->ioaddr.bmdma_addr = devm_ioremap(&pdev->dev, dma_res->start,
+ resource_size(dma_res));
+
+ /*
+ * Adjust register offsets
+ */
+ ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr;
+ ap->ioaddr.data_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_DATA << pdata->reg_shift);
+ ap->ioaddr.error_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_ERR << pdata->reg_shift);
+ ap->ioaddr.feature_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_FEATURE << pdata->reg_shift);
+ ap->ioaddr.nsect_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_NSECT << pdata->reg_shift);
+ ap->ioaddr.lbal_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_LBAL << pdata->reg_shift);
+ ap->ioaddr.lbam_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_LBAM << pdata->reg_shift);
+ ap->ioaddr.lbah_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_LBAH << pdata->reg_shift);
+ ap->ioaddr.device_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_DEVICE << pdata->reg_shift);
+ ap->ioaddr.status_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_STATUS << pdata->reg_shift);
+ ap->ioaddr.command_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_CMD << pdata->reg_shift);
+
+ /*
+ * Allocate and load driver's internal data structure
+ */
+ data = devm_kzalloc(&pdev->dev, sizeof(struct pata_pxa_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ap->private_data = data;
+ data->dma_dreq = pdata->dma_dreq;
+ data->dma_io_addr = dma_res->start;
+
+ /*
+ * Allocate space for the DMA descriptors
+ */
+ data->dma_desc = dmam_alloc_coherent(&pdev->dev, PAGE_SIZE,
+ &data->dma_desc_addr, GFP_KERNEL);
+ if (!data->dma_desc)
+ return -EINVAL;
+
+ /*
+ * Request the DMA channel
+ */
+ data->dma_channel = pxa_request_dma(DRV_NAME, DMA_PRIO_LOW,
+ pxa_ata_dma_irq, ap);
+ if (data->dma_channel < 0)
+ return -EBUSY;
+
+ /*
+ * Stop and clear the DMA channel
+ */
+ DCSR(data->dma_channel) = 0;
+
+ /*
+ * Activate the ATA host
+ */
+ ret = ata_host_activate(host, irq_res->start, ata_sff_interrupt,
+ pdata->irq_flags, &pxa_ata_sht);
+ if (ret)
+ pxa_free_dma(data->dma_channel);
+
+ return ret;
+}
+
+static int __devexit pxa_ata_remove(struct platform_device *pdev)
+{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct pata_pxa_data *data = host->ports[0]->private_data;
+
+ pxa_free_dma(data->dma_channel);
+
+ ata_host_detach(host);
+
+ return 0;
+}
+
+static struct platform_driver pxa_ata_driver = {
+ .probe = pxa_ata_probe,
+ .remove = __devexit_p(pxa_ata_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pxa_ata_init(void)
+{
+ return platform_driver_register(&pxa_ata_driver);
+}
+
+static void __exit pxa_ata_exit(void)
+{
+ platform_driver_unregister(&pxa_ata_driver);
+}
+
+module_init(pxa_ata_init);
+module_exit(pxa_ata_exit);
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("DMA-capable driver for PATA on PXA CPU");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c
index 18c986dbb7f1..7325f77480dc 100644
--- a/drivers/ata/sata_fsl.c
+++ b/drivers/ata/sata_fsl.c
@@ -1296,7 +1296,7 @@ static const struct ata_port_info sata_fsl_port_info[] = {
},
};
-static int sata_fsl_probe(struct of_device *ofdev,
+static int sata_fsl_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
int retval = -ENXIO;
@@ -1370,7 +1370,7 @@ error_exit_with_cleanup:
return retval;
}
-static int sata_fsl_remove(struct of_device *ofdev)
+static int sata_fsl_remove(struct platform_device *ofdev)
{
struct ata_host *host = dev_get_drvdata(&ofdev->dev);
struct sata_fsl_host_priv *host_priv = host->private_data;
@@ -1387,13 +1387,13 @@ static int sata_fsl_remove(struct of_device *ofdev)
}
#ifdef CONFIG_PM
-static int sata_fsl_suspend(struct of_device *op, pm_message_t state)
+static int sata_fsl_suspend(struct platform_device *op, pm_message_t state)
{
struct ata_host *host = dev_get_drvdata(&op->dev);
return ata_host_suspend(host, state);
}
-static int sata_fsl_resume(struct of_device *op)
+static int sata_fsl_resume(struct platform_device *op)
{
struct ata_host *host = dev_get_drvdata(&op->dev);
struct sata_fsl_host_priv *host_priv = host->private_data;
diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c
index b7385e077717..c8fc69c85a06 100644
--- a/drivers/atm/fore200e.c
+++ b/drivers/atm/fore200e.c
@@ -674,7 +674,7 @@ static void fore200e_sba_write(u32 val, volatile u32 __iomem *addr)
static u32 fore200e_sba_dma_map(struct fore200e *fore200e, void* virt_addr, int size, int direction)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
u32 dma_addr;
dma_addr = dma_map_single(&op->dev, virt_addr, size, direction);
@@ -687,7 +687,7 @@ static u32 fore200e_sba_dma_map(struct fore200e *fore200e, void* virt_addr, int
static void fore200e_sba_dma_unmap(struct fore200e *fore200e, u32 dma_addr, int size, int direction)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
DPRINTK(3, "SBUS DVMA unmapping: dma_addr = 0x%08x, size = %d, direction = %d,\n",
dma_addr, size, direction);
@@ -697,7 +697,7 @@ static void fore200e_sba_dma_unmap(struct fore200e *fore200e, u32 dma_addr, int
static void fore200e_sba_dma_sync_for_cpu(struct fore200e *fore200e, u32 dma_addr, int size, int direction)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction);
@@ -706,7 +706,7 @@ static void fore200e_sba_dma_sync_for_cpu(struct fore200e *fore200e, u32 dma_add
static void fore200e_sba_dma_sync_for_device(struct fore200e *fore200e, u32 dma_addr, int size, int direction)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction);
@@ -719,7 +719,7 @@ static void fore200e_sba_dma_sync_for_device(struct fore200e *fore200e, u32 dma_
static int fore200e_sba_dma_chunk_alloc(struct fore200e *fore200e, struct chunk *chunk,
int size, int nbr, int alignment)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
chunk->alloc_size = chunk->align_size = size * nbr;
@@ -738,7 +738,7 @@ static int fore200e_sba_dma_chunk_alloc(struct fore200e *fore200e, struct chunk
/* free a DVMA consistent chunk of memory */
static void fore200e_sba_dma_chunk_free(struct fore200e *fore200e, struct chunk *chunk)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
dma_free_coherent(&op->dev, chunk->alloc_size,
chunk->alloc_addr, chunk->dma_addr);
@@ -770,7 +770,7 @@ static void fore200e_sba_reset(struct fore200e *fore200e)
static int __init fore200e_sba_map(struct fore200e *fore200e)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
unsigned int bursts;
/* gain access to the SBA specific registers */
@@ -800,7 +800,7 @@ static int __init fore200e_sba_map(struct fore200e *fore200e)
static void fore200e_sba_unmap(struct fore200e *fore200e)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
of_iounmap(&op->resource[0], fore200e->regs.sba.hcr, SBA200E_HCR_LENGTH);
of_iounmap(&op->resource[1], fore200e->regs.sba.bsr, SBA200E_BSR_LENGTH);
@@ -816,7 +816,7 @@ static int __init fore200e_sba_configure(struct fore200e *fore200e)
static int __init fore200e_sba_prom_read(struct fore200e *fore200e, struct prom_data *prom)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
const u8 *prop;
int len;
@@ -840,7 +840,7 @@ static int __init fore200e_sba_prom_read(struct fore200e *fore200e, struct prom_
static int fore200e_sba_proc_read(struct fore200e *fore200e, char *page)
{
- struct of_device *op = fore200e->bus_dev;
+ struct platform_device *op = fore200e->bus_dev;
const struct linux_prom_registers *regs;
regs = of_get_property(op->dev.of_node, "reg", NULL);
@@ -2513,7 +2513,7 @@ fore200e_load_and_start_fw(struct fore200e* fore200e)
device = &((struct pci_dev *) fore200e->bus_dev)->dev;
#ifdef CONFIG_SBUS
else if (strcmp(fore200e->bus->model_name, "SBA-200E") == 0)
- device = &((struct of_device *) fore200e->bus_dev)->dev;
+ device = &((struct platform_device *) fore200e->bus_dev)->dev;
#endif
else
return err;
@@ -2643,7 +2643,7 @@ fore200e_init(struct fore200e* fore200e)
}
#ifdef CONFIG_SBUS
-static int __devinit fore200e_sba_probe(struct of_device *op,
+static int __devinit fore200e_sba_probe(struct platform_device *op,
const struct of_device_id *match)
{
const struct fore200e_bus *bus = match->data;
@@ -2675,7 +2675,7 @@ static int __devinit fore200e_sba_probe(struct of_device *op,
return 0;
}
-static int __devexit fore200e_sba_remove(struct of_device *op)
+static int __devexit fore200e_sba_remove(struct platform_device *op)
{
struct fore200e *fore200e = dev_get_drvdata(&op->dev);
diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c
index b71888b909a0..2982b3ee9465 100644
--- a/drivers/block/xsysace.c
+++ b/drivers/block/xsysace.c
@@ -1194,7 +1194,7 @@ static struct platform_driver ace_platform_driver = {
#if defined(CONFIG_OF)
static int __devinit
-ace_of_probe(struct of_device *op, const struct of_device_id *match)
+ace_of_probe(struct platform_device *op, const struct of_device_id *match)
{
struct resource res;
resource_size_t physaddr;
@@ -1226,7 +1226,7 @@ ace_of_probe(struct of_device *op, const struct of_device_id *match)
return ace_alloc(&op->dev, id ? *id : 0, physaddr, irq, bus_width);
}
-static int __devexit ace_of_remove(struct of_device *op)
+static int __devexit ace_of_remove(struct platform_device *op)
{
ace_free(&op->dev);
return 0;
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 7cfcc629a7fd..3d44ec724c17 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -1002,7 +1002,7 @@ config SCx200_GPIO
config PC8736x_GPIO
tristate "NatSemi PC8736x GPIO Support"
- depends on X86
+ depends on X86_32
default SCx200_GPIO # mostly N
select NSC_GPIO # needed for support routines
help
diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c
index 7a4f080f8356..1acdb2509511 100644
--- a/drivers/char/hw_random/n2-drv.c
+++ b/drivers/char/hw_random/n2-drv.c
@@ -619,7 +619,7 @@ static void __devinit n2rng_driver_version(void)
pr_info("%s", version);
}
-static int __devinit n2rng_probe(struct of_device *op,
+static int __devinit n2rng_probe(struct platform_device *op,
const struct of_device_id *match)
{
int victoria_falls = (match->data != NULL);
@@ -714,7 +714,7 @@ out:
return err;
}
-static int __devexit n2rng_remove(struct of_device *op)
+static int __devexit n2rng_remove(struct platform_device *op)
{
struct n2rng *np = dev_get_drvdata(&op->dev);
diff --git a/drivers/char/hw_random/n2rng.h b/drivers/char/hw_random/n2rng.h
index a2b81e7bfc18..4bea07f30978 100644
--- a/drivers/char/hw_random/n2rng.h
+++ b/drivers/char/hw_random/n2rng.h
@@ -65,7 +65,7 @@ struct n2rng_unit {
};
struct n2rng {
- struct of_device *op;
+ struct platform_device *op;
unsigned long flags;
#define N2RNG_FLAG_VF 0x00000001 /* Victoria Falls RNG, else N2 */
diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c
index 261ba8f22b8b..a31c830ca8cd 100644
--- a/drivers/char/hw_random/pasemi-rng.c
+++ b/drivers/char/hw_random/pasemi-rng.c
@@ -94,7 +94,7 @@ static struct hwrng pasemi_rng = {
.data_read = pasemi_rng_data_read,
};
-static int __devinit rng_probe(struct of_device *ofdev,
+static int __devinit rng_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
void __iomem *rng_regs;
@@ -123,7 +123,7 @@ static int __devinit rng_probe(struct of_device *ofdev,
return err;
}
-static int __devexit rng_remove(struct of_device *dev)
+static int __devexit rng_remove(struct platform_device *dev)
{
void __iomem *rng_regs = (void __iomem *)pasemi_rng.priv;
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index ff68e7c34ce7..3822b4f49c84 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -1804,9 +1804,12 @@ static int hotmod_handler(const char *val, struct kernel_param *kp)
info->irq_setup = std_irq_setup;
info->slave_addr = ipmb;
- if (!add_smi(info))
+ if (!add_smi(info)) {
if (try_smi_init(info))
cleanup_one_si(info);
+ } else {
+ kfree(info);
+ }
} else {
/* remove */
struct smi_info *e, *tmp_e;
@@ -1890,9 +1893,12 @@ static __devinit void hardcode_find_bmc(void)
info->irq_setup = std_irq_setup;
info->slave_addr = slave_addrs[i];
- if (!add_smi(info))
+ if (!add_smi(info)) {
if (try_smi_init(info))
cleanup_one_si(info);
+ } else {
+ kfree(info);
+ }
}
}
@@ -1965,8 +1971,8 @@ static int acpi_gpe_irq_setup(struct smi_info *info)
/*
* Defined at
- * http://h21007.www2.hp.com/dspp/files/unprotected/devresource/
- * Docs/TechPapers/IA64/hpspmi.pdf
+ * http://h21007.www2.hp.com/portal/download/files
+ * /unprot/hpspmi.pdf
*/
struct SPMITable {
s8 Signature[4];
@@ -2013,18 +2019,12 @@ struct SPMITable {
static __devinit int try_init_spmi(struct SPMITable *spmi)
{
struct smi_info *info;
- u8 addr_space;
if (spmi->IPMIlegacy != 1) {
printk(KERN_INFO PFX "Bad SPMI legacy %d\n", spmi->IPMIlegacy);
return -ENODEV;
}
- if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
- addr_space = IPMI_MEM_ADDR_SPACE;
- else
- addr_space = IPMI_IO_ADDR_SPACE;
-
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
printk(KERN_ERR PFX "Could not allocate SI data (3)\n");
@@ -2088,7 +2088,13 @@ static __devinit int try_init_spmi(struct SPMITable *spmi)
}
info->io.addr_data = spmi->addr.address;
- add_smi(info);
+ pr_info("ipmi_si: SPMI: %s %#lx regsize %d spacing %d irq %d\n",
+ (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
+ info->io.addr_data, info->io.regsize, info->io.regspacing,
+ info->irq);
+
+ if (add_smi(info))
+ kfree(info);
return 0;
}
@@ -2204,7 +2210,10 @@ static int __devinit ipmi_pnp_probe(struct pnp_dev *dev,
res, info->io.regsize, info->io.regspacing,
info->irq);
- return add_smi(info);
+ if (add_smi(info))
+ goto err_free;
+
+ return 0;
err_free:
kfree(info);
@@ -2362,7 +2371,13 @@ static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data)
if (info->irq)
info->irq_setup = std_irq_setup;
- add_smi(info);
+ pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n",
+ (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
+ info->io.addr_data, info->io.regsize, info->io.regspacing,
+ info->irq);
+
+ if (add_smi(info))
+ kfree(info);
}
static void __devinit dmi_find_bmc(void)
@@ -2468,7 +2483,10 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
&pdev->resource[0], info->io.regsize, info->io.regspacing,
info->irq);
- return add_smi(info);
+ if (add_smi(info))
+ kfree(info);
+
+ return 0;
}
static void __devexit ipmi_pci_remove(struct pci_dev *pdev)
@@ -2510,7 +2528,7 @@ static struct pci_driver ipmi_pci_driver = {
#ifdef CONFIG_PPC_OF
-static int __devinit ipmi_of_probe(struct of_device *dev,
+static int __devinit ipmi_of_probe(struct platform_device *dev,
const struct of_device_id *match)
{
struct smi_info *info;
@@ -2581,10 +2599,15 @@ static int __devinit ipmi_of_probe(struct of_device *dev,
dev_set_drvdata(&dev->dev, info);
- return add_smi(info);
+ if (add_smi(info)) {
+ kfree(info);
+ return -EBUSY;
+ }
+
+ return 0;
}
-static int __devexit ipmi_of_remove(struct of_device *dev)
+static int __devexit ipmi_of_remove(struct platform_device *dev)
{
cleanup_one_si(dev_get_drvdata(&dev->dev));
return 0;
@@ -3014,6 +3037,8 @@ static __devinit void default_find_bmc(void)
info->io.addr_data);
} else
cleanup_one_si(info);
+ } else {
+ kfree(info);
}
}
}
@@ -3041,7 +3066,7 @@ static int add_smi(struct smi_info *new_smi)
si_to_str[new_smi->si_type]);
mutex_lock(&smi_infos_lock);
if (!is_new_interface(new_smi)) {
- printk(KERN_CONT PFX "duplicate interface\n");
+ printk(KERN_CONT " duplicate interface\n");
rv = -EBUSY;
goto out_err;
}
diff --git a/drivers/char/n_gsm.c b/drivers/char/n_gsm.c
index 099105e0894e..04ef3ef0a422 100644
--- a/drivers/char/n_gsm.c
+++ b/drivers/char/n_gsm.c
@@ -919,7 +919,7 @@ static void gsm_dlci_data_sweep(struct gsm_mux *gsm)
else
len = gsm_dlci_data_output_framed(gsm, dlci);
if (len < 0)
- return;
+ break;
/* DLCI empty - try the next */
if (len == 0)
i++;
diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c
index 18af923093c3..817169cbb245 100644
--- a/drivers/char/nozomi.c
+++ b/drivers/char/nozomi.c
@@ -1743,8 +1743,7 @@ static int ntty_write_room(struct tty_struct *tty)
if (dc) {
mutex_lock(&port->tty_sem);
if (port->port.count)
- room = port->fifo_ul.size -
- kfifo_len(&port->fifo_ul);
+ room = kfifo_avail(&port->fifo_ul);
mutex_unlock(&port->tty_sem);
}
return room;
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index 95acb8c880f4..dfa8b3062fda 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -961,7 +961,7 @@ static int __init rtc_init(void)
#endif
#ifdef CONFIG_SPARC32
struct device_node *ebus_dp;
- struct of_device *op;
+ struct platform_device *op;
#else
void *r;
#ifdef RTC_IRQ
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
index ed8a9cec2a05..0ed763cd2e77 100644
--- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
@@ -761,7 +761,7 @@ static struct platform_driver hwicap_platform_driver = {
#if defined(CONFIG_OF)
static int __devinit
-hwicap_of_probe(struct of_device *op, const struct of_device_id *match)
+hwicap_of_probe(struct platform_device *op, const struct of_device_id *match)
{
struct resource res;
const unsigned int *id;
@@ -798,7 +798,7 @@ hwicap_of_probe(struct of_device *op, const struct of_device_id *match)
regs);
}
-static int __devexit hwicap_of_remove(struct of_device *op)
+static int __devexit hwicap_of_remove(struct platform_device *op)
{
return hwicap_remove(&op->dev);
}
diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c
index 983530ba04a7..2b1baee525bc 100644
--- a/drivers/crypto/amcc/crypto4xx_core.c
+++ b/drivers/crypto/amcc/crypto4xx_core.c
@@ -1150,7 +1150,7 @@ struct crypto4xx_alg_common crypto4xx_alg[] = {
/**
* Module Initialization Routine
*/
-static int __init crypto4xx_probe(struct of_device *ofdev,
+static int __init crypto4xx_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
int rc;
@@ -1258,7 +1258,7 @@ err_alloc_dev:
return rc;
}
-static int __exit crypto4xx_remove(struct of_device *ofdev)
+static int __exit crypto4xx_remove(struct platform_device *ofdev)
{
struct device *dev = &ofdev->dev;
struct crypto4xx_core_device *core_dev = dev_get_drvdata(dev);
diff --git a/drivers/crypto/amcc/crypto4xx_core.h b/drivers/crypto/amcc/crypto4xx_core.h
index da9cbe3b9fc3..bac0bdeb4b5f 100644
--- a/drivers/crypto/amcc/crypto4xx_core.h
+++ b/drivers/crypto/amcc/crypto4xx_core.h
@@ -104,7 +104,7 @@ struct crypto4xx_device {
struct crypto4xx_core_device {
struct device *device;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
struct crypto4xx_device *dev;
u32 int_status;
u32 irq;
diff --git a/drivers/crypto/ixp4xx_crypto.c b/drivers/crypto/ixp4xx_crypto.c
index f17ddf37a1ed..0d662213c066 100644
--- a/drivers/crypto/ixp4xx_crypto.c
+++ b/drivers/crypto/ixp4xx_crypto.c
@@ -97,8 +97,13 @@
struct buffer_desc {
u32 phys_next;
+#ifdef __ARMEB__
u16 buf_len;
u16 pkt_len;
+#else
+ u16 pkt_len;
+ u16 buf_len;
+#endif
u32 phys_addr;
u32 __reserved[4];
struct buffer_desc *next;
@@ -106,17 +111,30 @@ struct buffer_desc {
};
struct crypt_ctl {
+#ifdef __ARMEB__
u8 mode; /* NPE_OP_* operation mode */
u8 init_len;
u16 reserved;
+#else
+ u16 reserved;
+ u8 init_len;
+ u8 mode; /* NPE_OP_* operation mode */
+#endif
u8 iv[MAX_IVLEN]; /* IV for CBC mode or CTR IV for CTR mode */
u32 icv_rev_aes; /* icv or rev aes */
u32 src_buf;
u32 dst_buf;
+#ifdef __ARMEB__
u16 auth_offs; /* Authentication start offset */
u16 auth_len; /* Authentication data length */
u16 crypt_offs; /* Cryption start offset */
u16 crypt_len; /* Cryption data length */
+#else
+ u16 auth_len; /* Authentication data length */
+ u16 auth_offs; /* Authentication start offset */
+ u16 crypt_len; /* Cryption data length */
+ u16 crypt_offs; /* Cryption start offset */
+#endif
u32 aadAddr; /* Additional Auth Data Addr for CCM mode */
u32 crypto_ctx; /* NPE Crypto Param structure address */
@@ -652,6 +670,9 @@ static int setup_auth(struct crypto_tfm *tfm, int encrypt, unsigned authsize,
/* write cfg word to cryptinfo */
cfgword = algo->cfgword | ( authsize << 6); /* (authsize/4) << 8 */
+#ifndef __ARMEB__
+ cfgword ^= 0xAA000000; /* change the "byte swap" flags */
+#endif
*(u32*)cinfo = cpu_to_be32(cfgword);
cinfo += sizeof(cfgword);
diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c
index 26af2dd5d831..88ee01510ec0 100644
--- a/drivers/crypto/n2_core.c
+++ b/drivers/crypto/n2_core.c
@@ -1552,7 +1552,7 @@ static void __exit n2_unregister_algs(void)
/* To map CWQ queues to interrupt sources, the hypervisor API provides
* a devino. This isn't very useful to us because all of the
- * interrupts listed in the of_device node have been translated to
+ * interrupts listed in the device_node have been translated to
* Linux virtual IRQ cookie numbers.
*
* So we have to back-translate, going through the 'intr' and 'ino'
@@ -1560,7 +1560,7 @@ static void __exit n2_unregister_algs(void)
* 'interrupts' property entries, in order to to figure out which
* devino goes to which already-translated IRQ.
*/
-static int find_devino_index(struct of_device *dev, struct spu_mdesc_info *ip,
+static int find_devino_index(struct platform_device *dev, struct spu_mdesc_info *ip,
unsigned long dev_ino)
{
const unsigned int *dev_intrs;
@@ -1580,7 +1580,7 @@ static int find_devino_index(struct of_device *dev, struct spu_mdesc_info *ip,
if (!dev_intrs)
return -ENODEV;
- for (i = 0; i < dev->num_irqs; i++) {
+ for (i = 0; i < dev->archdata.num_irqs; i++) {
if (dev_intrs[i] == intr)
return i;
}
@@ -1588,7 +1588,7 @@ static int find_devino_index(struct of_device *dev, struct spu_mdesc_info *ip,
return -ENODEV;
}
-static int spu_map_ino(struct of_device *dev, struct spu_mdesc_info *ip,
+static int spu_map_ino(struct platform_device *dev, struct spu_mdesc_info *ip,
const char *irq_name, struct spu_queue *p,
irq_handler_t handler)
{
@@ -1603,7 +1603,7 @@ static int spu_map_ino(struct of_device *dev, struct spu_mdesc_info *ip,
if (index < 0)
return index;
- p->irq = dev->irqs[index];
+ p->irq = dev->archdata.irqs[index];
sprintf(p->irq_name, "%s-%d", irq_name, index);
@@ -1736,7 +1736,7 @@ static void spu_list_destroy(struct list_head *list)
* gathering cpu membership information.
*/
static int spu_mdesc_walk_arcs(struct mdesc_handle *mdesc,
- struct of_device *dev,
+ struct platform_device *dev,
u64 node, struct spu_queue *p,
struct spu_queue **table)
{
@@ -1763,7 +1763,7 @@ static int spu_mdesc_walk_arcs(struct mdesc_handle *mdesc,
/* Process an 'exec-unit' MDESC node of type 'cwq'. */
static int handle_exec_unit(struct spu_mdesc_info *ip, struct list_head *list,
- struct of_device *dev, struct mdesc_handle *mdesc,
+ struct platform_device *dev, struct mdesc_handle *mdesc,
u64 node, const char *iname, unsigned long q_type,
irq_handler_t handler, struct spu_queue **table)
{
@@ -1794,7 +1794,7 @@ static int handle_exec_unit(struct spu_mdesc_info *ip, struct list_head *list,
return spu_map_ino(dev, ip, iname, p, handler);
}
-static int spu_mdesc_scan(struct mdesc_handle *mdesc, struct of_device *dev,
+static int spu_mdesc_scan(struct mdesc_handle *mdesc, struct platform_device *dev,
struct spu_mdesc_info *ip, struct list_head *list,
const char *exec_name, unsigned long q_type,
irq_handler_t handler, struct spu_queue **table)
@@ -1855,7 +1855,7 @@ static int __devinit get_irq_props(struct mdesc_handle *mdesc, u64 node,
}
static int __devinit grab_mdesc_irq_props(struct mdesc_handle *mdesc,
- struct of_device *dev,
+ struct platform_device *dev,
struct spu_mdesc_info *ip,
const char *node_name)
{
@@ -2004,7 +2004,7 @@ static void __devinit n2_spu_driver_version(void)
pr_info("%s", version);
}
-static int __devinit n2_crypto_probe(struct of_device *dev,
+static int __devinit n2_crypto_probe(struct platform_device *dev,
const struct of_device_id *match)
{
struct mdesc_handle *mdesc;
@@ -2081,7 +2081,7 @@ out_free_n2cp:
return err;
}
-static int __devexit n2_crypto_remove(struct of_device *dev)
+static int __devexit n2_crypto_remove(struct platform_device *dev)
{
struct n2_crypto *np = dev_get_drvdata(&dev->dev);
@@ -2116,7 +2116,7 @@ static void free_ncp(struct n2_mau *mp)
kfree(mp);
}
-static int __devinit n2_mau_probe(struct of_device *dev,
+static int __devinit n2_mau_probe(struct platform_device *dev,
const struct of_device_id *match)
{
struct mdesc_handle *mdesc;
@@ -2184,7 +2184,7 @@ out_free_ncp:
return err;
}
-static int __devexit n2_mau_remove(struct of_device *dev)
+static int __devexit n2_mau_remove(struct platform_device *dev)
{
struct n2_mau *mp = dev_get_drvdata(&dev->dev);
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index 97f4af1d8a64..4bcd825b5739 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -118,7 +118,7 @@ struct talitos_channel {
struct talitos_private {
struct device *dev;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
void __iomem *reg;
int irq;
@@ -2308,7 +2308,7 @@ static int hw_supports(struct device *dev, __be32 desc_hdr_template)
return ret;
}
-static int talitos_remove(struct of_device *ofdev)
+static int talitos_remove(struct platform_device *ofdev)
{
struct device *dev = &ofdev->dev;
struct talitos_private *priv = dev_get_drvdata(dev);
@@ -2401,7 +2401,7 @@ static struct talitos_crypto_alg *talitos_alg_alloc(struct device *dev,
return t_alg;
}
-static int talitos_probe(struct of_device *ofdev,
+static int talitos_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device *dev = &ofdev->dev;
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index fed57634b6c1..9520cf02edc8 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -141,7 +141,7 @@ config TXX9_DMAC
config SH_DMAE
tristate "Renesas SuperH DMAC support"
- depends on SUPERH && SH_DMA
+ depends on (SUPERH && SH_DMA) || (ARM && ARCH_SHMOBILE)
depends on !SH_DMA_API
select DMA_ENGINE
help
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index f0fd6db6063c..cea08bed9cf9 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -1297,7 +1297,7 @@ static void fsl_dma_chan_remove(struct fsldma_chan *chan)
kfree(chan);
}
-static int __devinit fsldma_of_probe(struct of_device *op,
+static int __devinit fsldma_of_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct fsldma_device *fdev;
@@ -1382,7 +1382,7 @@ out_return:
return err;
}
-static int fsldma_of_remove(struct of_device *op)
+static int fsldma_of_remove(struct platform_device *op)
{
struct fsldma_device *fdev;
unsigned int i;
diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c
index 14a8c0f1698e..4e9cbf300594 100644
--- a/drivers/dma/mpc512x_dma.c
+++ b/drivers/dma/mpc512x_dma.c
@@ -627,7 +627,7 @@ mpc_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
return &mdesc->desc;
}
-static int __devinit mpc_dma_probe(struct of_device *op,
+static int __devinit mpc_dma_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dn = op->dev.of_node;
@@ -753,7 +753,7 @@ static int __devinit mpc_dma_probe(struct of_device *op,
return retval;
}
-static int __devexit mpc_dma_remove(struct of_device *op)
+static int __devexit mpc_dma_remove(struct platform_device *op)
{
struct device *dev = &op->dev;
struct mpc_dma *mdma = dev_get_drvdata(dev);
diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c
index 7c3747902a37..0d58a4a4487f 100644
--- a/drivers/dma/ppc4xx/adma.c
+++ b/drivers/dma/ppc4xx/adma.c
@@ -4257,11 +4257,11 @@ static int ppc440spe_adma_setup_irqs(struct ppc440spe_adma_device *adev,
struct ppc440spe_adma_chan *chan,
int *initcode)
{
- struct of_device *ofdev;
+ struct platform_device *ofdev;
struct device_node *np;
int ret;
- ofdev = container_of(adev->dev, struct of_device, dev);
+ ofdev = container_of(adev->dev, struct platform_device, dev);
np = ofdev->dev.of_node;
if (adev->id != PPC440SPE_XOR_ID) {
adev->err_irq = irq_of_parse_and_map(np, 1);
@@ -4393,7 +4393,7 @@ static void ppc440spe_adma_release_irqs(struct ppc440spe_adma_device *adev,
/**
* ppc440spe_adma_probe - probe the asynch device
*/
-static int __devinit ppc440spe_adma_probe(struct of_device *ofdev,
+static int __devinit ppc440spe_adma_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -4625,7 +4625,7 @@ out:
/**
* ppc440spe_adma_remove - remove the asynch device
*/
-static int __devexit ppc440spe_adma_remove(struct of_device *ofdev)
+static int __devexit ppc440spe_adma_remove(struct platform_device *ofdev)
{
struct ppc440spe_adma_device *adev = dev_get_drvdata(&ofdev->dev);
struct device_node *np = ofdev->dev.of_node;
diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c
index a2a519fd2a24..fb64cf36ba61 100644
--- a/drivers/dma/shdma.c
+++ b/drivers/dma/shdma.c
@@ -816,7 +816,7 @@ static irqreturn_t sh_dmae_interrupt(int irq, void *data)
return ret;
}
-#if defined(CONFIG_CPU_SH4)
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
static irqreturn_t sh_dmae_err(int irq, void *data)
{
struct sh_dmae_device *shdev = (struct sh_dmae_device *)data;
@@ -1057,7 +1057,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
/* Default transfer size of 32 bytes requires 32-byte alignment */
shdev->common.copy_align = LOG2_DEFAULT_XFER_SIZE;
-#if defined(CONFIG_CPU_SH4)
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
chanirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
if (!chanirq_res)
@@ -1082,7 +1082,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
#else
chanirq_res = errirq_res;
-#endif /* CONFIG_CPU_SH4 */
+#endif /* CONFIG_CPU_SH4 || CONFIG_ARCH_SHMOBILE */
if (chanirq_res->start == chanirq_res->end &&
!platform_get_resource(pdev, IORESOURCE_IRQ, 1)) {
@@ -1129,7 +1129,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
chan_probe_err:
sh_dmae_chan_remove(shdev);
eirqres:
-#if defined(CONFIG_CPU_SH4)
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
free_irq(errirq, shdev);
eirq_err:
#endif
diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c
index 996c1bdb5a34..a5cefab8d65d 100644
--- a/drivers/edac/i5000_edac.c
+++ b/drivers/edac/i5000_edac.c
@@ -1482,7 +1482,7 @@ static int __devinit i5000_init_one(struct pci_dev *pdev,
/* wake up device */
rc = pci_enable_device(pdev);
- if (rc == -EIO)
+ if (rc)
return rc;
/* now probe and enable the device */
diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c
index 010c1d6526f5..38a9be9e1c7c 100644
--- a/drivers/edac/i5400_edac.c
+++ b/drivers/edac/i5400_edac.c
@@ -1348,7 +1348,7 @@ static int __devinit i5400_init_one(struct pci_dev *pdev,
/* wake up device */
rc = pci_enable_device(pdev);
- if (rc == -EIO)
+ if (rc)
return rc;
/* now probe and enable the device */
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c
index 1052340e6802..b123bb308a4a 100644
--- a/drivers/edac/mpc85xx_edac.c
+++ b/drivers/edac/mpc85xx_edac.c
@@ -43,7 +43,7 @@ static u32 orig_pci_err_en;
#endif
static u32 orig_l2_err_disable;
-#ifdef CONFIG_MPC85xx
+#ifdef CONFIG_FSL_SOC_BOOKE
static u32 orig_hid1[2];
#endif
@@ -200,7 +200,7 @@ static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int __devinit mpc85xx_pci_err_probe(struct of_device *op,
+static int __devinit mpc85xx_pci_err_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct edac_pci_ctl_info *pci;
@@ -305,7 +305,7 @@ err:
return res;
}
-static int mpc85xx_pci_err_remove(struct of_device *op)
+static int mpc85xx_pci_err_remove(struct platform_device *op)
{
struct edac_pci_ctl_info *pci = dev_get_drvdata(&op->dev);
struct mpc85xx_pci_pdata *pdata = pci->pvt_info;
@@ -503,7 +503,7 @@ static irqreturn_t mpc85xx_l2_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int __devinit mpc85xx_l2_err_probe(struct of_device *op,
+static int __devinit mpc85xx_l2_err_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct edac_device_ctl_info *edac_dev;
@@ -613,7 +613,7 @@ err:
return res;
}
-static int mpc85xx_l2_err_remove(struct of_device *op)
+static int mpc85xx_l2_err_remove(struct platform_device *op)
{
struct edac_device_ctl_info *edac_dev = dev_get_drvdata(&op->dev);
struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info;
@@ -647,7 +647,10 @@ static struct of_device_id mpc85xx_l2_err_of_match[] = {
{ .compatible = "fsl,mpc8555-l2-cache-controller", },
{ .compatible = "fsl,mpc8560-l2-cache-controller", },
{ .compatible = "fsl,mpc8568-l2-cache-controller", },
+ { .compatible = "fsl,mpc8569-l2-cache-controller", },
{ .compatible = "fsl,mpc8572-l2-cache-controller", },
+ { .compatible = "fsl,p1020-l2-cache-controller", },
+ { .compatible = "fsl,p1021-l2-cache-controller", },
{ .compatible = "fsl,p2020-l2-cache-controller", },
{},
};
@@ -953,7 +956,7 @@ static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci)
}
}
-static int __devinit mpc85xx_mc_err_probe(struct of_device *op,
+static int __devinit mpc85xx_mc_err_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct mem_ctl_info *mci;
@@ -1085,7 +1088,7 @@ err:
return res;
}
-static int mpc85xx_mc_err_remove(struct of_device *op)
+static int mpc85xx_mc_err_remove(struct platform_device *op)
{
struct mem_ctl_info *mci = dev_get_drvdata(&op->dev);
struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
@@ -1125,7 +1128,10 @@ static struct of_device_id mpc85xx_mc_err_of_match[] = {
{ .compatible = "fsl,mpc8569-memory-controller", },
{ .compatible = "fsl,mpc8572-memory-controller", },
{ .compatible = "fsl,mpc8349-memory-controller", },
+ { .compatible = "fsl,p1020-memory-controller", },
+ { .compatible = "fsl,p1021-memory-controller", },
{ .compatible = "fsl,p2020-memory-controller", },
+ { .compatible = "fsl,p4080-memory-controller", },
{},
};
MODULE_DEVICE_TABLE(of, mpc85xx_mc_err_of_match);
@@ -1140,7 +1146,7 @@ static struct of_platform_driver mpc85xx_mc_err_driver = {
},
};
-#ifdef CONFIG_MPC85xx
+#ifdef CONFIG_FSL_SOC_BOOKE
static void __init mpc85xx_mc_clear_rfxe(void *data)
{
orig_hid1[smp_processor_id()] = mfspr(SPRN_HID1);
@@ -1179,7 +1185,7 @@ static int __init mpc85xx_mc_init(void)
printk(KERN_WARNING EDAC_MOD_STR "PCI fails to register\n");
#endif
-#ifdef CONFIG_MPC85xx
+#ifdef CONFIG_FSL_SOC_BOOKE
/*
* need to clear HID1[RFXE] to disable machine check int
* so we can catch it
@@ -1193,7 +1199,7 @@ static int __init mpc85xx_mc_init(void)
module_init(mpc85xx_mc_init);
-#ifdef CONFIG_MPC85xx
+#ifdef CONFIG_FSL_SOC_BOOKE
static void __exit mpc85xx_mc_restore_hid1(void *data)
{
mtspr(SPRN_HID1, orig_hid1[smp_processor_id()]);
@@ -1202,7 +1208,7 @@ static void __exit mpc85xx_mc_restore_hid1(void *data)
static void __exit mpc85xx_mc_exit(void)
{
-#ifdef CONFIG_MPC85xx
+#ifdef CONFIG_FSL_SOC_BOOKE
on_each_cpu(mpc85xx_mc_restore_hid1, NULL, 0);
#endif
#ifdef CONFIG_PCI
diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c
index e78839e89a06..070cea41b661 100644
--- a/drivers/edac/ppc4xx_edac.c
+++ b/drivers/edac/ppc4xx_edac.c
@@ -184,9 +184,9 @@ struct ppc4xx_ecc_status {
/* Function Prototypes */
-static int ppc4xx_edac_probe(struct of_device *device,
+static int ppc4xx_edac_probe(struct platform_device *device,
const struct of_device_id *device_id);
-static int ppc4xx_edac_remove(struct of_device *device);
+static int ppc4xx_edac_remove(struct platform_device *device);
/* Global Variables */
@@ -1014,7 +1014,7 @@ ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1)
*/
static int __devinit
ppc4xx_edac_mc_init(struct mem_ctl_info *mci,
- struct of_device *op,
+ struct platform_device *op,
const struct of_device_id *match,
const dcr_host_t *dcr_host,
u32 mcopt1)
@@ -1108,7 +1108,7 @@ ppc4xx_edac_mc_init(struct mem_ctl_info *mci,
* mapped and assigned.
*/
static int __devinit
-ppc4xx_edac_register_irq(struct of_device *op, struct mem_ctl_info *mci)
+ppc4xx_edac_register_irq(struct platform_device *op, struct mem_ctl_info *mci)
{
int status = 0;
int ded_irq, sec_irq;
@@ -1238,7 +1238,7 @@ ppc4xx_edac_map_dcrs(const struct device_node *np, dcr_host_t *dcr_host)
* driver; otherwise, < 0 on error.
*/
static int __devinit
-ppc4xx_edac_probe(struct of_device *op, const struct of_device_id *match)
+ppc4xx_edac_probe(struct platform_device *op, const struct of_device_id *match)
{
int status = 0;
u32 mcopt1, memcheck;
@@ -1359,7 +1359,7 @@ ppc4xx_edac_probe(struct of_device *op, const struct of_device_id *match)
* Unconditionally returns 0.
*/
static int
-ppc4xx_edac_remove(struct of_device *op)
+ppc4xx_edac_remove(struct platform_device *op)
{
struct mem_ctl_info *mci = dev_get_drvdata(&op->dev);
struct ppc4xx_edac_pdata *pdata = mci->pvt_info;
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 7face915b963..510aa2054544 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -195,6 +195,24 @@ config GPIO_PCF857X
This driver provides an in-kernel interface to those GPIOs using
platform-neutral GPIO calls.
+config GPIO_SX150X
+ bool "Semtech SX150x I2C GPIO expander"
+ depends on I2C=y
+ default n
+ help
+ Say yes here to provide support for Semtech SX150-series I2C
+ GPIO expanders. Compatible models include:
+
+ 8 bits: sx1508q
+ 16 bits: sx1509q
+
+config GPIO_STMPE
+ bool "STMPE GPIOs"
+ depends on MFD_STMPE
+ help
+ This enables support for the GPIOs found on the STMPE I/O
+ Expanders.
+
config GPIO_TC35892
bool "TC35892 GPIOs"
depends on MFD_TC35892
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index e53dcff49b4f..fc6019d93720 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o
obj-$(CONFIG_GPIO_PCA953X) += pca953x.o
obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o
obj-$(CONFIG_GPIO_PL061) += pl061.o
+obj-$(CONFIG_GPIO_STMPE) += stmpe-gpio.o
obj-$(CONFIG_GPIO_TC35892) += tc35892-gpio.o
obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o
obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o
@@ -35,3 +36,4 @@ obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o
obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o
obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o
+obj-$(CONFIG_GPIO_SX150X) += sx150x.o
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 6a6bd569e1f8..21da9c19a0cb 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -57,9 +57,9 @@ struct gpio_desc {
#define FLAG_TRIG_RISE 6 /* trigger on rising edge */
#define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */
-#define PDESC_ID_SHIFT 16 /* add new flags before this one */
+#define ID_SHIFT 16 /* add new flags before this one */
-#define GPIO_FLAGS_MASK ((1 << PDESC_ID_SHIFT) - 1)
+#define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1)
#define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
#ifdef CONFIG_DEBUG_FS
@@ -69,12 +69,7 @@ struct gpio_desc {
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
#ifdef CONFIG_GPIO_SYSFS
-struct poll_desc {
- struct work_struct work;
- struct sysfs_dirent *value_sd;
-};
-
-static struct idr pdesc_idr;
+static DEFINE_IDR(dirent_idr);
#endif
static inline void desc_set_label(struct gpio_desc *d, const char *label)
@@ -325,24 +320,16 @@ static const DEVICE_ATTR(value, 0644,
static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
{
- struct work_struct *work = priv;
+ struct sysfs_dirent *value_sd = priv;
- schedule_work(work);
+ sysfs_notify_dirent(value_sd);
return IRQ_HANDLED;
}
-static void gpio_notify_sysfs(struct work_struct *work)
-{
- struct poll_desc *pdesc;
-
- pdesc = container_of(work, struct poll_desc, work);
- sysfs_notify_dirent(pdesc->value_sd);
-}
-
static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
unsigned long gpio_flags)
{
- struct poll_desc *pdesc;
+ struct sysfs_dirent *value_sd;
unsigned long irq_flags;
int ret, irq, id;
@@ -353,18 +340,16 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
if (irq < 0)
return -EIO;
- id = desc->flags >> PDESC_ID_SHIFT;
- pdesc = idr_find(&pdesc_idr, id);
- if (pdesc) {
- free_irq(irq, &pdesc->work);
- cancel_work_sync(&pdesc->work);
- }
+ id = desc->flags >> ID_SHIFT;
+ value_sd = idr_find(&dirent_idr, id);
+ if (value_sd)
+ free_irq(irq, value_sd);
desc->flags &= ~GPIO_TRIGGER_MASK;
if (!gpio_flags) {
ret = 0;
- goto free_sd;
+ goto free_id;
}
irq_flags = IRQF_SHARED;
@@ -375,55 +360,46 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
- if (!pdesc) {
- pdesc = kmalloc(sizeof(*pdesc), GFP_KERNEL);
- if (!pdesc) {
- ret = -ENOMEM;
+ if (!value_sd) {
+ value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
+ if (!value_sd) {
+ ret = -ENODEV;
goto err_out;
}
do {
ret = -ENOMEM;
- if (idr_pre_get(&pdesc_idr, GFP_KERNEL))
- ret = idr_get_new_above(&pdesc_idr,
- pdesc, 1, &id);
+ if (idr_pre_get(&dirent_idr, GFP_KERNEL))
+ ret = idr_get_new_above(&dirent_idr, value_sd,
+ 1, &id);
} while (ret == -EAGAIN);
if (ret)
- goto free_mem;
+ goto free_sd;
desc->flags &= GPIO_FLAGS_MASK;
- desc->flags |= (unsigned long)id << PDESC_ID_SHIFT;
+ desc->flags |= (unsigned long)id << ID_SHIFT;
- if (desc->flags >> PDESC_ID_SHIFT != id) {
+ if (desc->flags >> ID_SHIFT != id) {
ret = -ERANGE;
goto free_id;
}
-
- pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
- if (!pdesc->value_sd) {
- ret = -ENODEV;
- goto free_id;
- }
- INIT_WORK(&pdesc->work, gpio_notify_sysfs);
}
- ret = request_irq(irq, gpio_sysfs_irq, irq_flags,
- "gpiolib", &pdesc->work);
- if (ret)
- goto free_sd;
+ ret = request_any_context_irq(irq, gpio_sysfs_irq, irq_flags,
+ "gpiolib", value_sd);
+ if (ret < 0)
+ goto free_id;
desc->flags |= gpio_flags;
return 0;
-free_sd:
- if (pdesc)
- sysfs_put(pdesc->value_sd);
free_id:
- idr_remove(&pdesc_idr, id);
+ idr_remove(&dirent_idr, id);
desc->flags &= GPIO_FLAGS_MASK;
-free_mem:
- kfree(pdesc);
+free_sd:
+ if (value_sd)
+ sysfs_put(value_sd);
err_out:
return ret;
}
@@ -994,8 +970,6 @@ static int __init gpiolib_sysfs_init(void)
unsigned long flags;
unsigned gpio;
- idr_init(&pdesc_idr);
-
status = class_register(&gpio_class);
if (status < 0)
return status;
@@ -1272,7 +1246,7 @@ void gpio_free(unsigned gpio)
if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) {
if (chip->free) {
spin_unlock_irqrestore(&gpio_lock, flags);
- might_sleep_if(extra_checks && chip->can_sleep);
+ might_sleep_if(chip->can_sleep);
chip->free(chip, gpio - chip->base);
spin_lock_irqsave(&gpio_lock, flags);
}
@@ -1410,7 +1384,7 @@ int gpio_direction_input(unsigned gpio)
spin_unlock_irqrestore(&gpio_lock, flags);
- might_sleep_if(extra_checks && chip->can_sleep);
+ might_sleep_if(chip->can_sleep);
if (status) {
status = chip->request(chip, gpio);
@@ -1463,7 +1437,7 @@ int gpio_direction_output(unsigned gpio, int value)
spin_unlock_irqrestore(&gpio_lock, flags);
- might_sleep_if(extra_checks && chip->can_sleep);
+ might_sleep_if(chip->can_sleep);
if (status) {
status = chip->request(chip, gpio);
@@ -1521,7 +1495,7 @@ int gpio_set_debounce(unsigned gpio, unsigned debounce)
spin_unlock_irqrestore(&gpio_lock, flags);
- might_sleep_if(extra_checks && chip->can_sleep);
+ might_sleep_if(chip->can_sleep);
return chip->set_debounce(chip, gpio, debounce);
@@ -1571,7 +1545,7 @@ int __gpio_get_value(unsigned gpio)
struct gpio_chip *chip;
chip = gpio_to_chip(gpio);
- WARN_ON(extra_checks && chip->can_sleep);
+ WARN_ON(chip->can_sleep);
return chip->get ? chip->get(chip, gpio - chip->base) : 0;
}
EXPORT_SYMBOL_GPL(__gpio_get_value);
@@ -1590,7 +1564,7 @@ void __gpio_set_value(unsigned gpio, int value)
struct gpio_chip *chip;
chip = gpio_to_chip(gpio);
- WARN_ON(extra_checks && chip->can_sleep);
+ WARN_ON(chip->can_sleep);
chip->set(chip, gpio - chip->base, value);
}
EXPORT_SYMBOL_GPL(__gpio_set_value);
diff --git a/drivers/gpio/max730x.c b/drivers/gpio/max730x.c
index 7696a5625d58..94ce773f95f8 100644
--- a/drivers/gpio/max730x.c
+++ b/drivers/gpio/max730x.c
@@ -54,7 +54,7 @@ static int max7301_direction_input(struct gpio_chip *chip, unsigned offset)
{
struct max7301 *ts = container_of(chip, struct max7301, chip);
u8 *config;
- u8 offset_bits;
+ u8 offset_bits, pin_config;
int ret;
/* First 4 pins are unused in the controller */
@@ -63,12 +63,15 @@ static int max7301_direction_input(struct gpio_chip *chip, unsigned offset)
config = &ts->port_config[offset >> 2];
+ if (ts->input_pullup_active & BIT(offset))
+ pin_config = PIN_CONFIG_IN_PULLUP;
+ else
+ pin_config = PIN_CONFIG_IN_WO_PULLUP;
+
mutex_lock(&ts->lock);
- /* Standard GPIO API doesn't support pull-ups, has to be extended.
- * Hard-coding no pollup for now. */
*config = (*config & ~(PIN_CONFIG_MASK << offset_bits))
- | (PIN_CONFIG_IN_WO_PULLUP << offset_bits);
+ | (pin_config << offset_bits);
ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config);
@@ -177,6 +180,7 @@ int __devinit __max730x_probe(struct max7301 *ts)
/* Power up the chip and disable IRQ output */
ts->write(dev, 0x04, 0x01);
+ ts->input_pullup_active = pdata->input_pullup_active;
ts->chip.label = dev->driver->name;
ts->chip.direction_input = max7301_direction_input;
@@ -191,13 +195,17 @@ int __devinit __max730x_probe(struct max7301 *ts)
ts->chip.owner = THIS_MODULE;
/*
- * tristate all pins in hardware and cache the
+ * initialize pullups according to platform data and cache the
* register values for later use.
*/
for (i = 1; i < 8; i++) {
int j;
- /* 0xAA means input with internal pullup disabled */
- ts->write(dev, 0x08 + i, 0xAA);
+ /*
+ * initialize port_config with "0xAA", which means
+ * input with internal pullup disabled. This is needed
+ * to avoid writing zeros (in the inner for loop),
+ * which is not allowed according to the datasheet.
+ */
ts->port_config[i] = 0xAA;
for (j = 0; j < 4; j++) {
int offset = (i - 1) * 4 + j;
diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c
index 29f19ce3e80f..879b473aab5a 100644
--- a/drivers/gpio/pcf857x.c
+++ b/drivers/gpio/pcf857x.c
@@ -190,7 +190,6 @@ static int pcf857x_probe(struct i2c_client *client,
pdata = client->dev.platform_data;
if (!pdata) {
dev_dbg(&client->dev, "no platform data\n");
- return -EINVAL;
}
/* Allocate, initialize, and register this gpio_chip. */
@@ -200,7 +199,7 @@ static int pcf857x_probe(struct i2c_client *client,
mutex_init(&gpio->lock);
- gpio->chip.base = pdata->gpio_base;
+ gpio->chip.base = pdata ? pdata->gpio_base : -1;
gpio->chip.can_sleep = 1;
gpio->chip.dev = &client->dev;
gpio->chip.owner = THIS_MODULE;
@@ -278,7 +277,7 @@ static int pcf857x_probe(struct i2c_client *client,
* to zero, our software copy of the "latch" then matches the chip's
* all-ones reset state. Otherwise it flags pins to be driven low.
*/
- gpio->out = ~pdata->n_latch;
+ gpio->out = pdata ? ~pdata->n_latch : ~0;
status = gpiochip_add(&gpio->chip);
if (status < 0)
@@ -299,7 +298,7 @@ static int pcf857x_probe(struct i2c_client *client,
/* Let platform code set up the GPIOs and their users.
* Now is the first time anyone could use them.
*/
- if (pdata->setup) {
+ if (pdata && pdata->setup) {
status = pdata->setup(client,
gpio->chip.base, gpio->chip.ngpio,
pdata->context);
@@ -322,7 +321,7 @@ static int pcf857x_remove(struct i2c_client *client)
struct pcf857x *gpio = i2c_get_clientdata(client);
int status = 0;
- if (pdata->teardown) {
+ if (pdata && pdata->teardown) {
status = pdata->teardown(client,
gpio->chip.base, gpio->chip.ngpio,
pdata->context);
diff --git a/drivers/gpio/stmpe-gpio.c b/drivers/gpio/stmpe-gpio.c
new file mode 100644
index 000000000000..4e1f1b9d5e67
--- /dev/null
+++ b/drivers/gpio/stmpe-gpio.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/stmpe.h>
+
+/*
+ * These registers are modified under the irq bus lock and cached to avoid
+ * unnecessary writes in bus_sync_unlock.
+ */
+enum { REG_RE, REG_FE, REG_IE };
+
+#define CACHE_NR_REGS 3
+#define CACHE_NR_BANKS (STMPE_NR_GPIOS / 8)
+
+struct stmpe_gpio {
+ struct gpio_chip chip;
+ struct stmpe *stmpe;
+ struct device *dev;
+ struct mutex irq_lock;
+
+ int irq_base;
+
+ /* Caches of interrupt control registers for bus_lock */
+ u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS];
+ u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS];
+};
+
+static inline struct stmpe_gpio *to_stmpe_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct stmpe_gpio, chip);
+}
+
+static int stmpe_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 reg = stmpe->regs[STMPE_IDX_GPMR_LSB] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+ int ret;
+
+ ret = stmpe_reg_read(stmpe, reg);
+ if (ret < 0)
+ return ret;
+
+ return ret & mask;
+}
+
+static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ int which = val ? STMPE_IDX_GPSR_LSB : STMPE_IDX_GPCR_LSB;
+ u8 reg = stmpe->regs[which] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+
+ stmpe_reg_write(stmpe, reg, mask);
+}
+
+static int stmpe_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int val)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+
+ stmpe_gpio_set(chip, offset, val);
+
+ return stmpe_set_bits(stmpe, reg, mask, mask);
+}
+
+static int stmpe_gpio_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8);
+ u8 mask = 1 << (offset % 8);
+
+ return stmpe_set_bits(stmpe, reg, mask, 0);
+}
+
+static int stmpe_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+
+ return stmpe_gpio->irq_base + offset;
+}
+
+static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+
+ return stmpe_set_altfunc(stmpe, 1 << offset, STMPE_BLOCK_GPIO);
+}
+
+static struct gpio_chip template_chip = {
+ .label = "stmpe",
+ .owner = THIS_MODULE,
+ .direction_input = stmpe_gpio_direction_input,
+ .get = stmpe_gpio_get,
+ .direction_output = stmpe_gpio_direction_output,
+ .set = stmpe_gpio_set,
+ .to_irq = stmpe_gpio_to_irq,
+ .request = stmpe_gpio_request,
+ .can_sleep = 1,
+};
+
+static int stmpe_gpio_irq_set_type(unsigned int irq, unsigned int type)
+{
+ struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+ int offset = irq - stmpe_gpio->irq_base;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
+ return -EINVAL;
+
+ if (type == IRQ_TYPE_EDGE_RISING)
+ stmpe_gpio->regs[REG_RE][regoffset] |= mask;
+ else
+ stmpe_gpio->regs[REG_RE][regoffset] &= ~mask;
+
+ if (type == IRQ_TYPE_EDGE_FALLING)
+ stmpe_gpio->regs[REG_FE][regoffset] |= mask;
+ else
+ stmpe_gpio->regs[REG_FE][regoffset] &= ~mask;
+
+ return 0;
+}
+
+static void stmpe_gpio_irq_lock(unsigned int irq)
+{
+ struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+
+ mutex_lock(&stmpe_gpio->irq_lock);
+}
+
+static void stmpe_gpio_irq_sync_unlock(unsigned int irq)
+{
+ struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
+ static const u8 regmap[] = {
+ [REG_RE] = STMPE_IDX_GPRER_LSB,
+ [REG_FE] = STMPE_IDX_GPFER_LSB,
+ [REG_IE] = STMPE_IDX_IEGPIOR_LSB,
+ };
+ int i, j;
+
+ for (i = 0; i < CACHE_NR_REGS; i++) {
+ for (j = 0; j < num_banks; j++) {
+ u8 old = stmpe_gpio->oldregs[i][j];
+ u8 new = stmpe_gpio->regs[i][j];
+
+ if (new == old)
+ continue;
+
+ stmpe_gpio->oldregs[i][j] = new;
+ stmpe_reg_write(stmpe, stmpe->regs[regmap[i]] - j, new);
+ }
+ }
+
+ mutex_unlock(&stmpe_gpio->irq_lock);
+}
+
+static void stmpe_gpio_irq_mask(unsigned int irq)
+{
+ struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+ int offset = irq - stmpe_gpio->irq_base;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ stmpe_gpio->regs[REG_IE][regoffset] &= ~mask;
+}
+
+static void stmpe_gpio_irq_unmask(unsigned int irq)
+{
+ struct stmpe_gpio *stmpe_gpio = get_irq_chip_data(irq);
+ int offset = irq - stmpe_gpio->irq_base;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ stmpe_gpio->regs[REG_IE][regoffset] |= mask;
+}
+
+static struct irq_chip stmpe_gpio_irq_chip = {
+ .name = "stmpe-gpio",
+ .bus_lock = stmpe_gpio_irq_lock,
+ .bus_sync_unlock = stmpe_gpio_irq_sync_unlock,
+ .mask = stmpe_gpio_irq_mask,
+ .unmask = stmpe_gpio_irq_unmask,
+ .set_type = stmpe_gpio_irq_set_type,
+};
+
+static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
+{
+ struct stmpe_gpio *stmpe_gpio = dev;
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ u8 statmsbreg = stmpe->regs[STMPE_IDX_ISGPIOR_MSB];
+ int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8);
+ u8 status[num_banks];
+ int ret;
+ int i;
+
+ ret = stmpe_block_read(stmpe, statmsbreg, num_banks, status);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ for (i = 0; i < num_banks; i++) {
+ int bank = num_banks - i - 1;
+ unsigned int enabled = stmpe_gpio->regs[REG_IE][bank];
+ unsigned int stat = status[i];
+
+ stat &= enabled;
+ if (!stat)
+ continue;
+
+ while (stat) {
+ int bit = __ffs(stat);
+ int line = bank * 8 + bit;
+
+ handle_nested_irq(stmpe_gpio->irq_base + line);
+ stat &= ~(1 << bit);
+ }
+
+ stmpe_reg_write(stmpe, statmsbreg + i, status[i]);
+ stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_GPEDR_MSB] + i,
+ status[i]);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit stmpe_gpio_irq_init(struct stmpe_gpio *stmpe_gpio)
+{
+ int base = stmpe_gpio->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) {
+ set_irq_chip_data(irq, stmpe_gpio);
+ set_irq_chip_and_handler(irq, &stmpe_gpio_irq_chip,
+ handle_simple_irq);
+ set_irq_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ set_irq_noprobe(irq);
+#endif
+ }
+
+ return 0;
+}
+
+static void stmpe_gpio_irq_remove(struct stmpe_gpio *stmpe_gpio)
+{
+ int base = stmpe_gpio->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) {
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#endif
+ set_irq_chip_and_handler(irq, NULL, NULL);
+ set_irq_chip_data(irq, NULL);
+ }
+}
+
+static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
+{
+ struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+ struct stmpe_gpio_platform_data *pdata;
+ struct stmpe_gpio *stmpe_gpio;
+ int ret;
+ int irq;
+
+ pdata = stmpe->pdata->gpio;
+ if (!pdata)
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ stmpe_gpio = kzalloc(sizeof(struct stmpe_gpio), GFP_KERNEL);
+ if (!stmpe_gpio)
+ return -ENOMEM;
+
+ mutex_init(&stmpe_gpio->irq_lock);
+
+ stmpe_gpio->dev = &pdev->dev;
+ stmpe_gpio->stmpe = stmpe;
+
+ stmpe_gpio->chip = template_chip;
+ stmpe_gpio->chip.ngpio = stmpe->num_gpios;
+ stmpe_gpio->chip.dev = &pdev->dev;
+ stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1;
+
+ stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0);
+
+ ret = stmpe_enable(stmpe, STMPE_BLOCK_GPIO);
+ if (ret)
+ return ret;
+
+ ret = stmpe_gpio_irq_init(stmpe_gpio);
+ if (ret)
+ goto out_free;
+
+ ret = request_threaded_irq(irq, NULL, stmpe_gpio_irq, IRQF_ONESHOT,
+ "stmpe-gpio", stmpe_gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
+ goto out_removeirq;
+ }
+
+ ret = gpiochip_add(&stmpe_gpio->chip);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
+ goto out_freeirq;
+ }
+
+ if (pdata && pdata->setup)
+ pdata->setup(stmpe, stmpe_gpio->chip.base);
+
+ platform_set_drvdata(pdev, stmpe_gpio);
+
+ return 0;
+
+out_freeirq:
+ free_irq(irq, stmpe_gpio);
+out_removeirq:
+ stmpe_gpio_irq_remove(stmpe_gpio);
+out_free:
+ kfree(stmpe_gpio);
+ return ret;
+}
+
+static int __devexit stmpe_gpio_remove(struct platform_device *pdev)
+{
+ struct stmpe_gpio *stmpe_gpio = platform_get_drvdata(pdev);
+ struct stmpe *stmpe = stmpe_gpio->stmpe;
+ struct stmpe_gpio_platform_data *pdata = stmpe->pdata->gpio;
+ int irq = platform_get_irq(pdev, 0);
+ int ret;
+
+ if (pdata && pdata->remove)
+ pdata->remove(stmpe, stmpe_gpio->chip.base);
+
+ ret = gpiochip_remove(&stmpe_gpio->chip);
+ if (ret < 0) {
+ dev_err(stmpe_gpio->dev,
+ "unable to remove gpiochip: %d\n", ret);
+ return ret;
+ }
+
+ stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
+
+ free_irq(irq, stmpe_gpio);
+ stmpe_gpio_irq_remove(stmpe_gpio);
+ platform_set_drvdata(pdev, NULL);
+ kfree(stmpe_gpio);
+
+ return 0;
+}
+
+static struct platform_driver stmpe_gpio_driver = {
+ .driver.name = "stmpe-gpio",
+ .driver.owner = THIS_MODULE,
+ .probe = stmpe_gpio_probe,
+ .remove = __devexit_p(stmpe_gpio_remove),
+};
+
+static int __init stmpe_gpio_init(void)
+{
+ return platform_driver_register(&stmpe_gpio_driver);
+}
+subsys_initcall(stmpe_gpio_init);
+
+static void __exit stmpe_gpio_exit(void)
+{
+ platform_driver_unregister(&stmpe_gpio_driver);
+}
+module_exit(stmpe_gpio_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMPExxxx GPIO driver");
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
diff --git a/drivers/gpio/sx150x.c b/drivers/gpio/sx150x.c
new file mode 100644
index 000000000000..b42f42ca70c3
--- /dev/null
+++ b/drivers/gpio/sx150x.c
@@ -0,0 +1,645 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/i2c/sx150x.h>
+
+struct sx150x_device_data {
+ u8 reg_pullup;
+ u8 reg_pulldn;
+ u8 reg_drain;
+ u8 reg_polarity;
+ u8 reg_dir;
+ u8 reg_data;
+ u8 reg_irq_mask;
+ u8 reg_irq_src;
+ u8 reg_sense;
+ u8 reg_clock;
+ u8 reg_misc;
+ u8 reg_reset;
+ u8 ngpios;
+};
+
+struct sx150x_chip {
+ struct gpio_chip gpio_chip;
+ struct i2c_client *client;
+ const struct sx150x_device_data *dev_cfg;
+ int irq_summary;
+ int irq_base;
+ u32 irq_sense;
+ unsigned long irq_set_type_pending;
+ struct irq_chip irq_chip;
+ struct mutex lock;
+};
+
+static const struct sx150x_device_data sx150x_devices[] = {
+ [0] = { /* sx1508q */
+ .reg_pullup = 0x03,
+ .reg_pulldn = 0x04,
+ .reg_drain = 0x05,
+ .reg_polarity = 0x06,
+ .reg_dir = 0x07,
+ .reg_data = 0x08,
+ .reg_irq_mask = 0x09,
+ .reg_irq_src = 0x0c,
+ .reg_sense = 0x0b,
+ .reg_clock = 0x0f,
+ .reg_misc = 0x10,
+ .reg_reset = 0x7d,
+ .ngpios = 8
+ },
+ [1] = { /* sx1509q */
+ .reg_pullup = 0x07,
+ .reg_pulldn = 0x09,
+ .reg_drain = 0x0b,
+ .reg_polarity = 0x0d,
+ .reg_dir = 0x0f,
+ .reg_data = 0x11,
+ .reg_irq_mask = 0x13,
+ .reg_irq_src = 0x19,
+ .reg_sense = 0x17,
+ .reg_clock = 0x1e,
+ .reg_misc = 0x1f,
+ .reg_reset = 0x7d,
+ .ngpios = 16
+ },
+};
+
+static const struct i2c_device_id sx150x_id[] = {
+ {"sx1508q", 0},
+ {"sx1509q", 1},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, sx150x_id);
+
+static s32 sx150x_i2c_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ s32 err = i2c_smbus_write_byte_data(client, reg, val);
+
+ if (err < 0)
+ dev_warn(&client->dev,
+ "i2c write fail: can't write %02x to %02x: %d\n",
+ val, reg, err);
+ return err;
+}
+
+static s32 sx150x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+ s32 err = i2c_smbus_read_byte_data(client, reg);
+
+ if (err >= 0)
+ *val = err;
+ else
+ dev_warn(&client->dev,
+ "i2c read fail: can't read from %02x: %d\n",
+ reg, err);
+ return err;
+}
+
+static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset)
+{
+ return (chip->dev_cfg->ngpios == offset);
+}
+
+/*
+ * These utility functions solve the common problem of locating and setting
+ * configuration bits. Configuration bits are grouped into registers
+ * whose indexes increase downwards. For example, with eight-bit registers,
+ * sixteen gpios would have their config bits grouped in the following order:
+ * REGISTER N-1 [ f e d c b a 9 8 ]
+ * N [ 7 6 5 4 3 2 1 0 ]
+ *
+ * For multi-bit configurations, the pattern gets wider:
+ * REGISTER N-3 [ f f e e d d c c ]
+ * N-2 [ b b a a 9 9 8 8 ]
+ * N-1 [ 7 7 6 6 5 5 4 4 ]
+ * N [ 3 3 2 2 1 1 0 0 ]
+ *
+ * Given the address of the starting register 'N', the index of the gpio
+ * whose configuration we seek to change, and the width in bits of that
+ * configuration, these functions allow us to locate the correct
+ * register and mask the correct bits.
+ */
+static inline void sx150x_find_cfg(u8 offset, u8 width,
+ u8 *reg, u8 *mask, u8 *shift)
+{
+ *reg -= offset * width / 8;
+ *mask = (1 << width) - 1;
+ *shift = (offset * width) % 8;
+ *mask <<= *shift;
+}
+
+static s32 sx150x_write_cfg(struct sx150x_chip *chip,
+ u8 offset, u8 width, u8 reg, u8 val)
+{
+ u8 mask;
+ u8 data;
+ u8 shift;
+ s32 err;
+
+ sx150x_find_cfg(offset, width, &reg, &mask, &shift);
+ err = sx150x_i2c_read(chip->client, reg, &data);
+ if (err < 0)
+ return err;
+
+ data &= ~mask;
+ data |= (val << shift) & mask;
+ return sx150x_i2c_write(chip->client, reg, data);
+}
+
+static int sx150x_get_io(struct sx150x_chip *chip, unsigned offset)
+{
+ u8 reg = chip->dev_cfg->reg_data;
+ u8 mask;
+ u8 data;
+ u8 shift;
+ s32 err;
+
+ sx150x_find_cfg(offset, 1, &reg, &mask, &shift);
+ err = sx150x_i2c_read(chip->client, reg, &data);
+ if (err >= 0)
+ err = (data & mask) != 0 ? 1 : 0;
+
+ return err;
+}
+
+static void sx150x_set_oscio(struct sx150x_chip *chip, int val)
+{
+ sx150x_i2c_write(chip->client,
+ chip->dev_cfg->reg_clock,
+ (val ? 0x1f : 0x10));
+}
+
+static void sx150x_set_io(struct sx150x_chip *chip, unsigned offset, int val)
+{
+ sx150x_write_cfg(chip,
+ offset,
+ 1,
+ chip->dev_cfg->reg_data,
+ (val ? 1 : 0));
+}
+
+static int sx150x_io_input(struct sx150x_chip *chip, unsigned offset)
+{
+ return sx150x_write_cfg(chip,
+ offset,
+ 1,
+ chip->dev_cfg->reg_dir,
+ 1);
+}
+
+static int sx150x_io_output(struct sx150x_chip *chip, unsigned offset, int val)
+{
+ int err;
+
+ err = sx150x_write_cfg(chip,
+ offset,
+ 1,
+ chip->dev_cfg->reg_data,
+ (val ? 1 : 0));
+ if (err >= 0)
+ err = sx150x_write_cfg(chip,
+ offset,
+ 1,
+ chip->dev_cfg->reg_dir,
+ 0);
+ return err;
+}
+
+static int sx150x_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct sx150x_chip *chip;
+ int status = -EINVAL;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ if (!offset_is_oscio(chip, offset)) {
+ mutex_lock(&chip->lock);
+ status = sx150x_get_io(chip, offset);
+ mutex_unlock(&chip->lock);
+ }
+
+ return status;
+}
+
+static void sx150x_gpio_set(struct gpio_chip *gc, unsigned offset, int val)
+{
+ struct sx150x_chip *chip;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ mutex_lock(&chip->lock);
+ if (offset_is_oscio(chip, offset))
+ sx150x_set_oscio(chip, val);
+ else
+ sx150x_set_io(chip, offset, val);
+ mutex_unlock(&chip->lock);
+}
+
+static int sx150x_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+ struct sx150x_chip *chip;
+ int status = -EINVAL;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ if (!offset_is_oscio(chip, offset)) {
+ mutex_lock(&chip->lock);
+ status = sx150x_io_input(chip, offset);
+ mutex_unlock(&chip->lock);
+ }
+ return status;
+}
+
+static int sx150x_gpio_direction_output(struct gpio_chip *gc,
+ unsigned offset,
+ int val)
+{
+ struct sx150x_chip *chip;
+ int status = 0;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ if (!offset_is_oscio(chip, offset)) {
+ mutex_lock(&chip->lock);
+ status = sx150x_io_output(chip, offset, val);
+ mutex_unlock(&chip->lock);
+ }
+ return status;
+}
+
+static int sx150x_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct sx150x_chip *chip;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ if (offset >= chip->dev_cfg->ngpios)
+ return -EINVAL;
+
+ if (chip->irq_base < 0)
+ return -EINVAL;
+
+ return chip->irq_base + offset;
+}
+
+static void sx150x_irq_mask(unsigned int irq)
+{
+ struct irq_chip *ic = get_irq_chip(irq);
+ struct sx150x_chip *chip;
+ unsigned n;
+
+ chip = container_of(ic, struct sx150x_chip, irq_chip);
+ n = irq - chip->irq_base;
+
+ sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1);
+ sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0);
+}
+
+static void sx150x_irq_unmask(unsigned int irq)
+{
+ struct irq_chip *ic = get_irq_chip(irq);
+ struct sx150x_chip *chip;
+ unsigned n;
+
+ chip = container_of(ic, struct sx150x_chip, irq_chip);
+ n = irq - chip->irq_base;
+
+ sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0);
+ sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense,
+ chip->irq_sense >> (n * 2));
+}
+
+static int sx150x_irq_set_type(unsigned int irq, unsigned int flow_type)
+{
+ struct irq_chip *ic = get_irq_chip(irq);
+ struct sx150x_chip *chip;
+ unsigned n, val = 0;
+
+ if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
+ return -EINVAL;
+
+ chip = container_of(ic, struct sx150x_chip, irq_chip);
+ n = irq - chip->irq_base;
+
+ if (flow_type & IRQ_TYPE_EDGE_RISING)
+ val |= 0x1;
+ if (flow_type & IRQ_TYPE_EDGE_FALLING)
+ val |= 0x2;
+
+ chip->irq_sense &= ~(3UL << (n * 2));
+ chip->irq_sense |= val << (n * 2);
+ chip->irq_set_type_pending |= BIT(n);
+ return 0;
+}
+
+static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id)
+{
+ struct sx150x_chip *chip = (struct sx150x_chip *)dev_id;
+ unsigned nhandled = 0;
+ unsigned sub_irq;
+ unsigned n;
+ s32 err;
+ u8 val;
+ int i;
+
+ for (i = (chip->dev_cfg->ngpios / 8) - 1; i >= 0; --i) {
+ err = sx150x_i2c_read(chip->client,
+ chip->dev_cfg->reg_irq_src - i,
+ &val);
+ if (err < 0)
+ continue;
+
+ sx150x_i2c_write(chip->client,
+ chip->dev_cfg->reg_irq_src - i,
+ val);
+ for (n = 0; n < 8; ++n) {
+ if (val & (1 << n)) {
+ sub_irq = chip->irq_base + (i * 8) + n;
+ handle_nested_irq(sub_irq);
+ ++nhandled;
+ }
+ }
+ }
+
+ return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
+}
+
+static void sx150x_irq_bus_lock(unsigned int irq)
+{
+ struct irq_chip *ic = get_irq_chip(irq);
+ struct sx150x_chip *chip;
+
+ chip = container_of(ic, struct sx150x_chip, irq_chip);
+
+ mutex_lock(&chip->lock);
+}
+
+static void sx150x_irq_bus_sync_unlock(unsigned int irq)
+{
+ struct irq_chip *ic = get_irq_chip(irq);
+ struct sx150x_chip *chip;
+ unsigned n;
+
+ chip = container_of(ic, struct sx150x_chip, irq_chip);
+
+ while (chip->irq_set_type_pending) {
+ n = __ffs(chip->irq_set_type_pending);
+ chip->irq_set_type_pending &= ~BIT(n);
+ if (!(irq_to_desc(n + chip->irq_base)->status & IRQ_MASKED))
+ sx150x_write_cfg(chip, n, 2,
+ chip->dev_cfg->reg_sense,
+ chip->irq_sense >> (n * 2));
+ }
+
+ mutex_unlock(&chip->lock);
+}
+
+static void sx150x_init_chip(struct sx150x_chip *chip,
+ struct i2c_client *client,
+ kernel_ulong_t driver_data,
+ struct sx150x_platform_data *pdata)
+{
+ mutex_init(&chip->lock);
+
+ chip->client = client;
+ chip->dev_cfg = &sx150x_devices[driver_data];
+ chip->gpio_chip.label = client->name;
+ chip->gpio_chip.direction_input = sx150x_gpio_direction_input;
+ chip->gpio_chip.direction_output = sx150x_gpio_direction_output;
+ chip->gpio_chip.get = sx150x_gpio_get;
+ chip->gpio_chip.set = sx150x_gpio_set;
+ chip->gpio_chip.to_irq = sx150x_gpio_to_irq;
+ chip->gpio_chip.base = pdata->gpio_base;
+ chip->gpio_chip.can_sleep = 1;
+ chip->gpio_chip.ngpio = chip->dev_cfg->ngpios;
+ if (pdata->oscio_is_gpo)
+ ++chip->gpio_chip.ngpio;
+
+ chip->irq_chip.name = client->name;
+ chip->irq_chip.mask = sx150x_irq_mask;
+ chip->irq_chip.unmask = sx150x_irq_unmask;
+ chip->irq_chip.set_type = sx150x_irq_set_type;
+ chip->irq_chip.bus_lock = sx150x_irq_bus_lock;
+ chip->irq_chip.bus_sync_unlock = sx150x_irq_bus_sync_unlock;
+ chip->irq_summary = -1;
+ chip->irq_base = -1;
+ chip->irq_sense = 0;
+ chip->irq_set_type_pending = 0;
+}
+
+static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg)
+{
+ int err = 0;
+ unsigned n;
+
+ for (n = 0; err >= 0 && n < (chip->dev_cfg->ngpios / 8); ++n)
+ err = sx150x_i2c_write(chip->client, base - n, cfg >> (n * 8));
+ return err;
+}
+
+static int sx150x_init_hw(struct sx150x_chip *chip,
+ struct sx150x_platform_data *pdata)
+{
+ int err = 0;
+
+ err = i2c_smbus_write_word_data(chip->client,
+ chip->dev_cfg->reg_reset,
+ 0x3412);
+ if (err < 0)
+ return err;
+
+ err = sx150x_i2c_write(chip->client,
+ chip->dev_cfg->reg_misc,
+ 0x01);
+ if (err < 0)
+ return err;
+
+ err = sx150x_init_io(chip, chip->dev_cfg->reg_pullup,
+ pdata->io_pullup_ena);
+ if (err < 0)
+ return err;
+
+ err = sx150x_init_io(chip, chip->dev_cfg->reg_pulldn,
+ pdata->io_pulldn_ena);
+ if (err < 0)
+ return err;
+
+ err = sx150x_init_io(chip, chip->dev_cfg->reg_drain,
+ pdata->io_open_drain_ena);
+ if (err < 0)
+ return err;
+
+ err = sx150x_init_io(chip, chip->dev_cfg->reg_polarity,
+ pdata->io_polarity);
+ if (err < 0)
+ return err;
+
+ if (pdata->oscio_is_gpo)
+ sx150x_set_oscio(chip, 0);
+
+ return err;
+}
+
+static int sx150x_install_irq_chip(struct sx150x_chip *chip,
+ int irq_summary,
+ int irq_base)
+{
+ int err;
+ unsigned n;
+ unsigned irq;
+
+ chip->irq_summary = irq_summary;
+ chip->irq_base = irq_base;
+
+ for (n = 0; n < chip->dev_cfg->ngpios; ++n) {
+ irq = irq_base + n;
+ set_irq_chip_and_handler(irq, &chip->irq_chip, handle_edge_irq);
+ set_irq_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ set_irq_noprobe(irq);
+#endif
+ }
+
+ err = request_threaded_irq(irq_summary,
+ NULL,
+ sx150x_irq_thread_fn,
+ IRQF_SHARED | IRQF_TRIGGER_FALLING,
+ chip->irq_chip.name,
+ chip);
+ if (err < 0) {
+ chip->irq_summary = -1;
+ chip->irq_base = -1;
+ }
+
+ return err;
+}
+
+static void sx150x_remove_irq_chip(struct sx150x_chip *chip)
+{
+ unsigned n;
+ unsigned irq;
+
+ free_irq(chip->irq_summary, chip);
+
+ for (n = 0; n < chip->dev_cfg->ngpios; ++n) {
+ irq = chip->irq_base + n;
+ set_irq_handler(irq, NULL);
+ set_irq_chip(irq, NULL);
+ }
+}
+
+static int __devinit sx150x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ static const u32 i2c_funcs = I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WRITE_WORD_DATA;
+ struct sx150x_platform_data *pdata;
+ struct sx150x_chip *chip;
+ int rc;
+
+ pdata = client->dev.platform_data;
+ if (!pdata)
+ return -EINVAL;
+
+ if (!i2c_check_functionality(client->adapter, i2c_funcs))
+ return -ENOSYS;
+
+ chip = kzalloc(sizeof(struct sx150x_chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ sx150x_init_chip(chip, client, id->driver_data, pdata);
+ rc = sx150x_init_hw(chip, pdata);
+ if (rc < 0)
+ goto probe_fail_pre_gpiochip_add;
+
+ rc = gpiochip_add(&chip->gpio_chip);
+ if (rc < 0)
+ goto probe_fail_pre_gpiochip_add;
+
+ if (pdata->irq_summary >= 0) {
+ rc = sx150x_install_irq_chip(chip,
+ pdata->irq_summary,
+ pdata->irq_base);
+ if (rc < 0)
+ goto probe_fail_post_gpiochip_add;
+ }
+
+ i2c_set_clientdata(client, chip);
+
+ return 0;
+probe_fail_post_gpiochip_add:
+ WARN_ON(gpiochip_remove(&chip->gpio_chip) < 0);
+probe_fail_pre_gpiochip_add:
+ kfree(chip);
+ return rc;
+}
+
+static int __devexit sx150x_remove(struct i2c_client *client)
+{
+ struct sx150x_chip *chip;
+ int rc;
+
+ chip = i2c_get_clientdata(client);
+ rc = gpiochip_remove(&chip->gpio_chip);
+ if (rc < 0)
+ return rc;
+
+ if (chip->irq_summary >= 0)
+ sx150x_remove_irq_chip(chip);
+
+ kfree(chip);
+
+ return 0;
+}
+
+static struct i2c_driver sx150x_driver = {
+ .driver = {
+ .name = "sx150x",
+ .owner = THIS_MODULE
+ },
+ .probe = sx150x_probe,
+ .remove = __devexit_p(sx150x_remove),
+ .id_table = sx150x_id,
+};
+
+static int __init sx150x_init(void)
+{
+ return i2c_add_driver(&sx150x_driver);
+}
+subsys_initcall(sx150x_init);
+
+static void __exit sx150x_exit(void)
+{
+ return i2c_del_driver(&sx150x_driver);
+}
+module_exit(sx150x_exit);
+
+MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>");
+MODULE_DESCRIPTION("Driver for Semtech SX150X I2C GPIO Expanders");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:sx150x");
diff --git a/drivers/gpio/wm831x-gpio.c b/drivers/gpio/wm831x-gpio.c
index 1fa449a1a4cb..309644cf4d9b 100644
--- a/drivers/gpio/wm831x-gpio.c
+++ b/drivers/gpio/wm831x-gpio.c
@@ -108,6 +108,37 @@ static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
return wm831x->irq_base + WM831X_IRQ_GPIO_1 + offset;
}
+static int wm831x_gpio_set_debounce(struct gpio_chip *chip, unsigned offset,
+ unsigned debounce)
+{
+ struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
+ struct wm831x *wm831x = wm831x_gpio->wm831x;
+ int reg = WM831X_GPIO1_CONTROL + offset;
+ int ret, fn;
+
+ ret = wm831x_reg_read(wm831x, reg);
+ if (ret < 0)
+ return ret;
+
+ switch (ret & WM831X_GPN_FN_MASK) {
+ case 0:
+ case 1:
+ break;
+ default:
+ /* Not in GPIO mode */
+ return -EBUSY;
+ }
+
+ if (debounce >= 32 && debounce <= 64)
+ fn = 0;
+ else if (debounce >= 4000 && debounce <= 8000)
+ fn = 1;
+ else
+ return -EINVAL;
+
+ return wm831x_set_bits(wm831x, reg, WM831X_GPN_FN_MASK, fn);
+}
+
#ifdef CONFIG_DEBUG_FS
static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
@@ -208,6 +239,7 @@ static struct gpio_chip template_chip = {
.direction_output = wm831x_gpio_direction_out,
.set = wm831x_gpio_set,
.to_irq = wm831x_gpio_to_irq,
+ .set_debounce = wm831x_gpio_set_debounce,
.dbg_show = wm831x_gpio_dbg_show,
.can_sleep = 1,
};
diff --git a/drivers/gpu/drm/ati_pcigart.c b/drivers/gpu/drm/ati_pcigart.c
index 17be051b7aa3..1c3649242208 100644
--- a/drivers/gpu/drm/ati_pcigart.c
+++ b/drivers/gpu/drm/ati_pcigart.c
@@ -152,7 +152,7 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga
/* we need to support large memory configurations */
entry->busaddr[i] = pci_map_page(dev->pdev, entry->pagelist[i],
0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
- if (entry->busaddr[i] == 0) {
+ if (pci_dma_mapping_error(dev->pdev, entry->busaddr[i])) {
DRM_ERROR("unable to map PCIGART pages!\n");
drm_ati_pcigart_cleanup(dev, gart_info);
address = NULL;
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c
index a5c9ce93bbcb..3e257a50bf56 100644
--- a/drivers/gpu/drm/drm_bufs.c
+++ b/drivers/gpu/drm/drm_bufs.c
@@ -328,14 +328,13 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
return -EINVAL;
}
- list = kmalloc(sizeof(*list), GFP_KERNEL);
+ list = kzalloc(sizeof(*list), GFP_KERNEL);
if (!list) {
if (map->type == _DRM_REGISTERS)
iounmap(map->handle);
kfree(map);
return -EINVAL;
}
- memset(list, 0, sizeof(*list));
list->map = map;
mutex_lock(&dev->struct_mutex);
@@ -678,13 +677,12 @@ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request)
return -EINVAL;
}
- entry->buflist = kmalloc(count * sizeof(*entry->buflist), GFP_KERNEL);
+ entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL);
if (!entry->buflist) {
mutex_unlock(&dev->struct_mutex);
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- memset(entry->buflist, 0, count * sizeof(*entry->buflist));
entry->buf_size = size;
entry->page_order = page_order;
@@ -708,7 +706,7 @@ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request)
buf->file_priv = NULL;
buf->dev_priv_size = dev->driver->dev_priv_size;
- buf->dev_private = kmalloc(buf->dev_priv_size, GFP_KERNEL);
+ buf->dev_private = kzalloc(buf->dev_priv_size, GFP_KERNEL);
if (!buf->dev_private) {
/* Set count correctly so we free the proper amount. */
entry->buf_count = count;
@@ -717,7 +715,6 @@ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request)
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- memset(buf->dev_private, 0, buf->dev_priv_size);
DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address);
@@ -832,22 +829,20 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request)
return -EINVAL;
}
- entry->buflist = kmalloc(count * sizeof(*entry->buflist), GFP_KERNEL);
+ entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL);
if (!entry->buflist) {
mutex_unlock(&dev->struct_mutex);
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- memset(entry->buflist, 0, count * sizeof(*entry->buflist));
- entry->seglist = kmalloc(count * sizeof(*entry->seglist), GFP_KERNEL);
+ entry->seglist = kzalloc(count * sizeof(*entry->seglist), GFP_KERNEL);
if (!entry->seglist) {
kfree(entry->buflist);
mutex_unlock(&dev->struct_mutex);
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- memset(entry->seglist, 0, count * sizeof(*entry->seglist));
/* Keep the original pagelist until we know all the allocations
* have succeeded
@@ -911,8 +906,8 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request)
buf->file_priv = NULL;
buf->dev_priv_size = dev->driver->dev_priv_size;
- buf->dev_private = kmalloc(buf->dev_priv_size,
- GFP_KERNEL);
+ buf->dev_private = kzalloc(buf->dev_priv_size,
+ GFP_KERNEL);
if (!buf->dev_private) {
/* Set count correctly so we free the proper amount. */
entry->buf_count = count;
@@ -923,7 +918,6 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request)
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- memset(buf->dev_private, 0, buf->dev_priv_size);
DRM_DEBUG("buffer %d @ %p\n",
entry->buf_count, buf->address);
@@ -1048,14 +1042,13 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request
return -EINVAL;
}
- entry->buflist = kmalloc(count * sizeof(*entry->buflist),
+ entry->buflist = kzalloc(count * sizeof(*entry->buflist),
GFP_KERNEL);
if (!entry->buflist) {
mutex_unlock(&dev->struct_mutex);
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- memset(entry->buflist, 0, count * sizeof(*entry->buflist));
entry->buf_size = size;
entry->page_order = page_order;
@@ -1080,7 +1073,7 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request
buf->file_priv = NULL;
buf->dev_priv_size = dev->driver->dev_priv_size;
- buf->dev_private = kmalloc(buf->dev_priv_size, GFP_KERNEL);
+ buf->dev_private = kzalloc(buf->dev_priv_size, GFP_KERNEL);
if (!buf->dev_private) {
/* Set count correctly so we free the proper amount. */
entry->buf_count = count;
@@ -1090,8 +1083,6 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request
return -ENOMEM;
}
- memset(buf->dev_private, 0, buf->dev_priv_size);
-
DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address);
offset += alignment;
@@ -1209,14 +1200,13 @@ static int drm_addbufs_fb(struct drm_device * dev, struct drm_buf_desc * request
return -EINVAL;
}
- entry->buflist = kmalloc(count * sizeof(*entry->buflist),
+ entry->buflist = kzalloc(count * sizeof(*entry->buflist),
GFP_KERNEL);
if (!entry->buflist) {
mutex_unlock(&dev->struct_mutex);
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- memset(entry->buflist, 0, count * sizeof(*entry->buflist));
entry->buf_size = size;
entry->page_order = page_order;
@@ -1240,7 +1230,7 @@ static int drm_addbufs_fb(struct drm_device * dev, struct drm_buf_desc * request
buf->file_priv = NULL;
buf->dev_priv_size = dev->driver->dev_priv_size;
- buf->dev_private = kmalloc(buf->dev_priv_size, GFP_KERNEL);
+ buf->dev_private = kzalloc(buf->dev_priv_size, GFP_KERNEL);
if (!buf->dev_private) {
/* Set count correctly so we free the proper amount. */
entry->buf_count = count;
@@ -1249,7 +1239,6 @@ static int drm_addbufs_fb(struct drm_device * dev, struct drm_buf_desc * request
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- memset(buf->dev_private, 0, buf->dev_priv_size);
DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 4c68f76993d8..37e0b4fa482a 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -1682,9 +1682,9 @@ int drm_mode_addfb(struct drm_device *dev,
/* TODO setup destructor callback */
fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
- if (!fb) {
+ if (IS_ERR(fb)) {
DRM_ERROR("could not create framebuffer\n");
- ret = -EINVAL;
+ ret = PTR_ERR(fb);
goto out;
}
@@ -2541,7 +2541,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
goto out;
}
- crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size);
+ crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
out:
mutex_unlock(&dev->mode_config.mutex);
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index b9e4dbfa0533..7e31d4348340 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -817,12 +817,12 @@ int drm_helper_resume_force_mode(struct drm_device *dev)
if (encoder_funcs->dpms)
(*encoder_funcs->dpms) (encoder,
drm_helper_choose_encoder_dpms(encoder));
-
- crtc_funcs = crtc->helper_private;
- if (crtc_funcs->dpms)
- (*crtc_funcs->dpms) (crtc,
- drm_helper_choose_crtc_dpms(crtc));
}
+
+ crtc_funcs = crtc->helper_private;
+ if (crtc_funcs->dpms)
+ (*crtc_funcs->dpms) (crtc,
+ drm_helper_choose_crtc_dpms(crtc));
}
}
/* disable the unused connectors while restoring the modesetting */
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index dce5c4a97f8d..96e963108225 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -33,6 +33,11 @@
#include <linux/i2c-algo-bit.h>
#include "drmP.h"
#include "drm_edid.h"
+#include "drm_edid_modes.h"
+
+#define version_greater(edid, maj, min) \
+ (((edid)->version > (maj)) || \
+ ((edid)->version == (maj) && (edid)->revision > (min)))
#define EDID_EST_TIMINGS 16
#define EDID_STD_TIMINGS 8
@@ -62,6 +67,13 @@
/* use +hsync +vsync for detailed mode */
#define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6)
+struct detailed_mode_closure {
+ struct drm_connector *connector;
+ struct edid *edid;
+ bool preferred;
+ u32 quirks;
+ int modes;
+};
#define LEVEL_DMT 0
#define LEVEL_GTF 1
@@ -375,7 +387,6 @@ static u32 edid_get_quirks(struct edid *edid)
#define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay)
#define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh))
-
/**
* edid_fixup_preferred - set preferred modes based on quirk list
* @connector: has mode list to fix up
@@ -422,245 +433,6 @@ static void edid_fixup_preferred(struct drm_connector *connector,
preferred_mode->type |= DRM_MODE_TYPE_PREFERRED;
}
-/*
- * Add the Autogenerated from the DMT spec.
- * This table is copied from xfree86/modes/xf86EdidModes.c.
- * But the mode with Reduced blank feature is deleted.
- */
-static struct drm_display_mode drm_dmt_modes[] = {
- /* 640x350@85Hz */
- { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 31500, 640, 672,
- 736, 832, 0, 350, 382, 385, 445, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 640x400@85Hz */
- { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 31500, 640, 672,
- 736, 832, 0, 400, 401, 404, 445, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 720x400@85Hz */
- { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 756,
- 828, 936, 0, 400, 401, 404, 446, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 640x480@60Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
- 752, 800, 0, 480, 489, 492, 525, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 640x480@72Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
- 704, 832, 0, 480, 489, 492, 520, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 640x480@75Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
- 720, 840, 0, 480, 481, 484, 500, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 640x480@85Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 36000, 640, 696,
- 752, 832, 0, 480, 481, 484, 509, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 800x600@56Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
- 896, 1024, 0, 600, 601, 603, 625, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 800x600@60Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
- 968, 1056, 0, 600, 601, 605, 628, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 800x600@72Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
- 976, 1040, 0, 600, 637, 643, 666, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 800x600@75Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
- 896, 1056, 0, 600, 601, 604, 625, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 800x600@85Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 56250, 800, 832,
- 896, 1048, 0, 600, 601, 604, 631, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 848x480@60Hz */
- { DRM_MODE("848x480", DRM_MODE_TYPE_DRIVER, 33750, 848, 864,
- 976, 1088, 0, 480, 486, 494, 517, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1024x768@43Hz, interlace */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032,
- 1208, 1264, 0, 768, 768, 772, 817, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
- DRM_MODE_FLAG_INTERLACE) },
- /* 1024x768@60Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
- 1184, 1344, 0, 768, 771, 777, 806, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 1024x768@70Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
- 1184, 1328, 0, 768, 771, 777, 806, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 1024x768@75Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040,
- 1136, 1312, 0, 768, 769, 772, 800, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1024x768@85Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 94500, 1024, 1072,
- 1168, 1376, 0, 768, 769, 772, 808, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1152x864@75Hz */
- { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
- 1344, 1600, 0, 864, 865, 868, 900, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x768@60Hz */
- { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
- 1472, 1664, 0, 768, 771, 778, 798, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x768@75Hz */
- { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 102250, 1280, 1360,
- 1488, 1696, 0, 768, 771, 778, 805, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 1280x768@85Hz */
- { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 117500, 1280, 1360,
- 1496, 1712, 0, 768, 771, 778, 809, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x800@60Hz */
- { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
- 1480, 1680, 0, 800, 803, 809, 831, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
- /* 1280x800@75Hz */
- { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 106500, 1280, 1360,
- 1488, 1696, 0, 800, 803, 809, 838, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x800@85Hz */
- { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 122500, 1280, 1360,
- 1496, 1712, 0, 800, 803, 809, 843, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x960@60Hz */
- { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
- 1488, 1800, 0, 960, 961, 964, 1000, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x960@85Hz */
- { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1344,
- 1504, 1728, 0, 960, 961, 964, 1011, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x1024@60Hz */
- { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
- 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x1024@75Hz */
- { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
- 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1280x1024@85Hz */
- { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 157500, 1280, 1344,
- 1504, 1728, 0, 1024, 1025, 1028, 1072, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1360x768@60Hz */
- { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
- 1536, 1792, 0, 768, 771, 777, 795, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1440x1050@60Hz */
- { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
- 1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1440x1050@75Hz */
- { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 156000, 1400, 1504,
- 1648, 1896, 0, 1050, 1053, 1057, 1099, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1440x1050@85Hz */
- { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 179500, 1400, 1504,
- 1656, 1912, 0, 1050, 1053, 1057, 1105, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1440x900@60Hz */
- { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
- 1672, 1904, 0, 900, 903, 909, 934, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1440x900@75Hz */
- { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 136750, 1440, 1536,
- 1688, 1936, 0, 900, 903, 909, 942, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1440x900@85Hz */
- { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 157000, 1440, 1544,
- 1696, 1952, 0, 900, 903, 909, 948, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1600x1200@60Hz */
- { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
- 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1600x1200@65Hz */
- { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 175500, 1600, 1664,
- 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1600x1200@70Hz */
- { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 189000, 1600, 1664,
- 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1600x1200@75Hz */
- { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 202500, 1600, 1664,
- 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1600x1200@85Hz */
- { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 229500, 1600, 1664,
- 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1680x1050@60Hz */
- { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
- 1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1680x1050@75Hz */
- { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 187000, 1680, 1800,
- 1976, 2272, 0, 1050, 1053, 1059, 1099, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1680x1050@85Hz */
- { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 214750, 1680, 1808,
- 1984, 2288, 0, 1050, 1053, 1059, 1105, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1792x1344@60Hz */
- { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
- 2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1729x1344@75Hz */
- { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 261000, 1792, 1888,
- 2104, 2456, 0, 1344, 1345, 1348, 1417, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1853x1392@60Hz */
- { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
- 2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1856x1392@75Hz */
- { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 288000, 1856, 1984,
- 2208, 2560, 0, 1392, 1395, 1399, 1500, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1920x1200@60Hz */
- { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
- 2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1920x1200@75Hz */
- { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 245250, 1920, 2056,
- 2264, 2608, 0, 1200, 1203, 1209, 1255, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1920x1200@85Hz */
- { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 281250, 1920, 2064,
- 2272, 2624, 0, 1200, 1203, 1209, 1262, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1920x1440@60Hz */
- { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
- 2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 1920x1440@75Hz */
- { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2064,
- 2288, 2640, 0, 1440, 1441, 1444, 1500, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 2560x1600@60Hz */
- { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
- 3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 2560x1600@75HZ */
- { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 443250, 2560, 2768,
- 3048, 3536, 0, 1600, 1603, 1609, 1672, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
- /* 2560x1600@85HZ */
- { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 505250, 2560, 2768,
- 3048, 3536, 0, 1600, 1603, 1609, 1682, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
-};
-static const int drm_num_dmt_modes =
- sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
-
struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
int hsize, int vsize, int fresh)
{
@@ -685,6 +457,46 @@ EXPORT_SYMBOL(drm_mode_find_dmt);
typedef void detailed_cb(struct detailed_timing *timing, void *closure);
static void
+cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure)
+{
+ int i, n = 0;
+ u8 rev = ext[0x01], d = ext[0x02];
+ u8 *det_base = ext + d;
+
+ switch (rev) {
+ case 0:
+ /* can't happen */
+ return;
+ case 1:
+ /* have to infer how many blocks we have, check pixel clock */
+ for (i = 0; i < 6; i++)
+ if (det_base[18*i] || det_base[18*i+1])
+ n++;
+ break;
+ default:
+ /* explicit count */
+ n = min(ext[0x03] & 0x0f, 6);
+ break;
+ }
+
+ for (i = 0; i < n; i++)
+ cb((struct detailed_timing *)(det_base + 18 * i), closure);
+}
+
+static void
+vtb_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure)
+{
+ unsigned int i, n = min((int)ext[0x02], 6);
+ u8 *det_base = ext + 5;
+
+ if (ext[0x01] != 1)
+ return; /* unknown version */
+
+ for (i = 0; i < n; i++)
+ cb((struct detailed_timing *)(det_base + 18 * i), closure);
+}
+
+static void
drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure)
{
int i;
@@ -696,7 +508,19 @@ drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure)
for (i = 0; i < EDID_DETAILED_TIMINGS; i++)
cb(&(edid->detailed_timings[i]), closure);
- /* XXX extension block walk */
+ for (i = 1; i <= raw_edid[0x7e]; i++) {
+ u8 *ext = raw_edid + (i * EDID_LENGTH);
+ switch (*ext) {
+ case CEA_EXT:
+ cea_for_each_detailed_block(ext, cb, closure);
+ break;
+ case VTB_EXT:
+ vtb_for_each_detailed_block(ext, cb, closure);
+ break;
+ default:
+ break;
+ }
+ }
}
static void
@@ -1047,117 +871,6 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
return mode;
}
-/*
- * Detailed mode info for the EDID "established modes" data to use.
- */
-static struct drm_display_mode edid_est_modes[] = {
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
- 968, 1056, 0, 600, 601, 605, 628, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
- 896, 1024, 0, 600, 601, 603, 625, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
- 720, 840, 0, 480, 481, 484, 500, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
- 704, 832, 0, 480, 489, 491, 520, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704,
- 768, 864, 0, 480, 483, 486, 525, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */
- { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656,
- 752, 800, 0, 480, 490, 492, 525, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */
- { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738,
- 846, 900, 0, 400, 421, 423, 449, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */
- { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738,
- 846, 900, 0, 400, 412, 414, 449, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */
- { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
- 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040,
- 1136, 1312, 0, 768, 769, 772, 800, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
- 1184, 1328, 0, 768, 771, 777, 806, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
- 1184, 1344, 0, 768, 771, 777, 806, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */
- { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
- 1208, 1264, 0, 768, 768, 776, 817, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */
- { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864,
- 928, 1152, 0, 624, 625, 628, 667, 0,
- DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
- 896, 1056, 0, 600, 601, 604, 625, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */
- { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
- 976, 1040, 0, 600, 637, 643, 666, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */
- { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
- 1344, 1600, 0, 864, 865, 868, 900, 0,
- DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */
-};
-
-/**
- * add_established_modes - get est. modes from EDID and add them
- * @edid: EDID block to scan
- *
- * Each EDID block contains a bitmap of the supported "established modes" list
- * (defined above). Tease them out and add them to the global modes list.
- */
-static int add_established_modes(struct drm_connector *connector, struct edid *edid)
-{
- struct drm_device *dev = connector->dev;
- unsigned long est_bits = edid->established_timings.t1 |
- (edid->established_timings.t2 << 8) |
- ((edid->established_timings.mfg_rsvd & 0x80) << 9);
- int i, modes = 0;
-
- for (i = 0; i <= EDID_EST_TIMINGS; i++)
- if (est_bits & (1<<i)) {
- struct drm_display_mode *newmode;
- newmode = drm_mode_duplicate(dev, &edid_est_modes[i]);
- if (newmode) {
- drm_mode_probed_add(connector, newmode);
- modes++;
- }
- }
-
- return modes;
-}
-
-/**
- * add_standard_modes - get std. modes from EDID and add them
- * @edid: EDID block to scan
- *
- * Standard modes can be calculated using the CVT standard. Grab them from
- * @edid, calculate them, and add them to the list.
- */
-static int add_standard_modes(struct drm_connector *connector, struct edid *edid)
-{
- int i, modes = 0;
-
- for (i = 0; i < EDID_STD_TIMINGS; i++) {
- struct drm_display_mode *newmode;
-
- newmode = drm_mode_std(connector, edid,
- &edid->standard_timings[i],
- edid->revision);
- if (newmode) {
- drm_mode_probed_add(connector, newmode);
- modes++;
- }
- }
-
- return modes;
-}
-
static bool
mode_is_rb(struct drm_display_mode *mode)
{
@@ -1267,113 +980,33 @@ drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid,
return modes;
}
-static int drm_cvt_modes(struct drm_connector *connector,
- struct detailed_timing *timing)
+static void
+do_inferred_modes(struct detailed_timing *timing, void *c)
{
- int i, j, modes = 0;
- struct drm_display_mode *newmode;
- struct drm_device *dev = connector->dev;
- struct cvt_timing *cvt;
- const int rates[] = { 60, 85, 75, 60, 50 };
- const u8 empty[3] = { 0, 0, 0 };
-
- for (i = 0; i < 4; i++) {
- int uninitialized_var(width), height;
- cvt = &(timing->data.other_data.data.cvt[i]);
+ struct detailed_mode_closure *closure = c;
+ struct detailed_non_pixel *data = &timing->data.other_data;
+ int gtf = (closure->edid->features & DRM_EDID_FEATURE_DEFAULT_GTF);
- if (!memcmp(cvt->code, empty, 3))
- continue;
+ if (gtf && data->type == EDID_DETAIL_MONITOR_RANGE)
+ closure->modes += drm_gtf_modes_for_range(closure->connector,
+ closure->edid,
+ timing);
+}
- height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2;
- switch (cvt->code[1] & 0x0c) {
- case 0x00:
- width = height * 4 / 3;
- break;
- case 0x04:
- width = height * 16 / 9;
- break;
- case 0x08:
- width = height * 16 / 10;
- break;
- case 0x0c:
- width = height * 15 / 9;
- break;
- }
+static int
+add_inferred_modes(struct drm_connector *connector, struct edid *edid)
+{
+ struct detailed_mode_closure closure = {
+ connector, edid, 0, 0, 0
+ };
- for (j = 1; j < 5; j++) {
- if (cvt->code[2] & (1 << j)) {
- newmode = drm_cvt_mode(dev, width, height,
- rates[j], j == 0,
- false, false);
- if (newmode) {
- drm_mode_probed_add(connector, newmode);
- modes++;
- }
- }
- }
- }
+ if (version_greater(edid, 1, 0))
+ drm_for_each_detailed_block((u8 *)edid, do_inferred_modes,
+ &closure);
- return modes;
+ return closure.modes;
}
-static const struct {
- short w;
- short h;
- short r;
- short rb;
-} est3_modes[] = {
- /* byte 6 */
- { 640, 350, 85, 0 },
- { 640, 400, 85, 0 },
- { 720, 400, 85, 0 },
- { 640, 480, 85, 0 },
- { 848, 480, 60, 0 },
- { 800, 600, 85, 0 },
- { 1024, 768, 85, 0 },
- { 1152, 864, 75, 0 },
- /* byte 7 */
- { 1280, 768, 60, 1 },
- { 1280, 768, 60, 0 },
- { 1280, 768, 75, 0 },
- { 1280, 768, 85, 0 },
- { 1280, 960, 60, 0 },
- { 1280, 960, 85, 0 },
- { 1280, 1024, 60, 0 },
- { 1280, 1024, 85, 0 },
- /* byte 8 */
- { 1360, 768, 60, 0 },
- { 1440, 900, 60, 1 },
- { 1440, 900, 60, 0 },
- { 1440, 900, 75, 0 },
- { 1440, 900, 85, 0 },
- { 1400, 1050, 60, 1 },
- { 1400, 1050, 60, 0 },
- { 1400, 1050, 75, 0 },
- /* byte 9 */
- { 1400, 1050, 85, 0 },
- { 1680, 1050, 60, 1 },
- { 1680, 1050, 60, 0 },
- { 1680, 1050, 75, 0 },
- { 1680, 1050, 85, 0 },
- { 1600, 1200, 60, 0 },
- { 1600, 1200, 65, 0 },
- { 1600, 1200, 70, 0 },
- /* byte 10 */
- { 1600, 1200, 75, 0 },
- { 1600, 1200, 85, 0 },
- { 1792, 1344, 60, 0 },
- { 1792, 1344, 85, 0 },
- { 1856, 1392, 60, 0 },
- { 1856, 1392, 75, 0 },
- { 1920, 1200, 60, 1 },
- { 1920, 1200, 60, 0 },
- /* byte 11 */
- { 1920, 1200, 75, 0 },
- { 1920, 1200, 85, 0 },
- { 1920, 1440, 60, 0 },
- { 1920, 1440, 75, 0 },
-};
-
static int
drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing)
{
@@ -1403,37 +1036,63 @@ drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing)
return modes;
}
-static int add_detailed_modes(struct drm_connector *connector,
- struct detailed_timing *timing,
- struct edid *edid, u32 quirks, int preferred)
+static void
+do_established_modes(struct detailed_timing *timing, void *c)
{
- int i, modes = 0;
+ struct detailed_mode_closure *closure = c;
struct detailed_non_pixel *data = &timing->data.other_data;
- int gtf = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF);
- struct drm_display_mode *newmode;
- struct drm_device *dev = connector->dev;
- if (timing->pixel_clock) {
- newmode = drm_mode_detailed(dev, edid, timing, quirks);
- if (!newmode)
- return 0;
+ if (data->type == EDID_DETAIL_EST_TIMINGS)
+ closure->modes += drm_est3_modes(closure->connector, timing);
+}
- if (preferred)
- newmode->type |= DRM_MODE_TYPE_PREFERRED;
+/**
+ * add_established_modes - get est. modes from EDID and add them
+ * @edid: EDID block to scan
+ *
+ * Each EDID block contains a bitmap of the supported "established modes" list
+ * (defined above). Tease them out and add them to the global modes list.
+ */
+static int
+add_established_modes(struct drm_connector *connector, struct edid *edid)
+{
+ struct drm_device *dev = connector->dev;
+ unsigned long est_bits = edid->established_timings.t1 |
+ (edid->established_timings.t2 << 8) |
+ ((edid->established_timings.mfg_rsvd & 0x80) << 9);
+ int i, modes = 0;
+ struct detailed_mode_closure closure = {
+ connector, edid, 0, 0, 0
+ };
- drm_mode_probed_add(connector, newmode);
- return 1;
+ for (i = 0; i <= EDID_EST_TIMINGS; i++) {
+ if (est_bits & (1<<i)) {
+ struct drm_display_mode *newmode;
+ newmode = drm_mode_duplicate(dev, &edid_est_modes[i]);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
}
- /* other timing types */
- switch (data->type) {
- case EDID_DETAIL_MONITOR_RANGE:
- if (gtf)
- modes += drm_gtf_modes_for_range(connector, edid,
- timing);
- break;
- case EDID_DETAIL_STD_MODES:
- /* Six modes per detailed section */
+ if (version_greater(edid, 1, 0))
+ drm_for_each_detailed_block((u8 *)edid,
+ do_established_modes, &closure);
+
+ return modes + closure.modes;
+}
+
+static void
+do_standard_modes(struct detailed_timing *timing, void *c)
+{
+ struct detailed_mode_closure *closure = c;
+ struct detailed_non_pixel *data = &timing->data.other_data;
+ struct drm_connector *connector = closure->connector;
+ struct edid *edid = closure->edid;
+
+ if (data->type == EDID_DETAIL_STD_MODES) {
+ int i;
for (i = 0; i < 6; i++) {
struct std_timing *std;
struct drm_display_mode *newmode;
@@ -1443,108 +1102,169 @@ static int add_detailed_modes(struct drm_connector *connector,
edid->revision);
if (newmode) {
drm_mode_probed_add(connector, newmode);
- modes++;
+ closure->modes++;
}
}
- break;
- case EDID_DETAIL_CVT_3BYTE:
- modes += drm_cvt_modes(connector, timing);
- break;
- case EDID_DETAIL_EST_TIMINGS:
- modes += drm_est3_modes(connector, timing);
- break;
- default:
- break;
}
-
- return modes;
}
/**
- * add_detailed_info - get detailed mode info from EDID data
- * @connector: attached connector
+ * add_standard_modes - get std. modes from EDID and add them
* @edid: EDID block to scan
- * @quirks: quirks to apply
*
- * Some of the detailed timing sections may contain mode information. Grab
- * it and add it to the list.
+ * Standard modes can be calculated using the appropriate standard (DMT,
+ * GTF or CVT. Grab them from @edid and add them to the list.
*/
-static int add_detailed_info(struct drm_connector *connector,
- struct edid *edid, u32 quirks)
+static int
+add_standard_modes(struct drm_connector *connector, struct edid *edid)
{
int i, modes = 0;
+ struct detailed_mode_closure closure = {
+ connector, edid, 0, 0, 0
+ };
+
+ for (i = 0; i < EDID_STD_TIMINGS; i++) {
+ struct drm_display_mode *newmode;
+
+ newmode = drm_mode_std(connector, edid,
+ &edid->standard_timings[i],
+ edid->revision);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
+
+ if (version_greater(edid, 1, 0))
+ drm_for_each_detailed_block((u8 *)edid, do_standard_modes,
+ &closure);
+
+ /* XXX should also look for standard codes in VTB blocks */
+
+ return modes + closure.modes;
+}
- for (i = 0; i < EDID_DETAILED_TIMINGS; i++) {
- struct detailed_timing *timing = &edid->detailed_timings[i];
- int preferred = (i == 0);
+static int drm_cvt_modes(struct drm_connector *connector,
+ struct detailed_timing *timing)
+{
+ int i, j, modes = 0;
+ struct drm_display_mode *newmode;
+ struct drm_device *dev = connector->dev;
+ struct cvt_timing *cvt;
+ const int rates[] = { 60, 85, 75, 60, 50 };
+ const u8 empty[3] = { 0, 0, 0 };
- if (preferred && edid->version == 1 && edid->revision < 4)
- preferred = (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
+ for (i = 0; i < 4; i++) {
+ int uninitialized_var(width), height;
+ cvt = &(timing->data.other_data.data.cvt[i]);
- /* In 1.0, only timings are allowed */
- if (!timing->pixel_clock && edid->version == 1 &&
- edid->revision == 0)
+ if (!memcmp(cvt->code, empty, 3))
continue;
- modes += add_detailed_modes(connector, timing, edid, quirks,
- preferred);
+ height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2;
+ switch (cvt->code[1] & 0x0c) {
+ case 0x00:
+ width = height * 4 / 3;
+ break;
+ case 0x04:
+ width = height * 16 / 9;
+ break;
+ case 0x08:
+ width = height * 16 / 10;
+ break;
+ case 0x0c:
+ width = height * 15 / 9;
+ break;
+ }
+
+ for (j = 1; j < 5; j++) {
+ if (cvt->code[2] & (1 << j)) {
+ newmode = drm_cvt_mode(dev, width, height,
+ rates[j], j == 0,
+ false, false);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
+ }
}
return modes;
}
-/**
- * add_detailed_mode_eedid - get detailed mode info from addtional timing
- * EDID block
- * @connector: attached connector
- * @edid: EDID block to scan(It is only to get addtional timing EDID block)
- * @quirks: quirks to apply
- *
- * Some of the detailed timing sections may contain mode information. Grab
- * it and add it to the list.
- */
-static int add_detailed_info_eedid(struct drm_connector *connector,
- struct edid *edid, u32 quirks)
+static void
+do_cvt_mode(struct detailed_timing *timing, void *c)
{
- int i, modes = 0;
- char *edid_ext = NULL;
- struct detailed_timing *timing;
- int start_offset, end_offset;
+ struct detailed_mode_closure *closure = c;
+ struct detailed_non_pixel *data = &timing->data.other_data;
- if (edid->version == 1 && edid->revision < 3)
- return 0;
- if (!edid->extensions)
- return 0;
+ if (data->type == EDID_DETAIL_CVT_3BYTE)
+ closure->modes += drm_cvt_modes(closure->connector, timing);
+}
- /* Find CEA extension */
- for (i = 0; i < edid->extensions; i++) {
- edid_ext = (char *)edid + EDID_LENGTH * (i + 1);
- if (edid_ext[0] == 0x02)
- break;
- }
+static int
+add_cvt_modes(struct drm_connector *connector, struct edid *edid)
+{
+ struct detailed_mode_closure closure = {
+ connector, edid, 0, 0, 0
+ };
- if (i == edid->extensions)
- return 0;
+ if (version_greater(edid, 1, 2))
+ drm_for_each_detailed_block((u8 *)edid, do_cvt_mode, &closure);
- /* Get the start offset of detailed timing block */
- start_offset = edid_ext[2];
- if (start_offset == 0) {
- /* If the start_offset is zero, it means that neither detailed
- * info nor data block exist. In such case it is also
- * unnecessary to parse the detailed timing info.
- */
- return 0;
- }
+ /* XXX should also look for CVT codes in VTB blocks */
- end_offset = EDID_LENGTH;
- end_offset -= sizeof(struct detailed_timing);
- for (i = start_offset; i < end_offset;
- i += sizeof(struct detailed_timing)) {
- timing = (struct detailed_timing *)(edid_ext + i);
- modes += add_detailed_modes(connector, timing, edid, quirks, 0);
+ return closure.modes;
+}
+
+static void
+do_detailed_mode(struct detailed_timing *timing, void *c)
+{
+ struct detailed_mode_closure *closure = c;
+ struct drm_display_mode *newmode;
+
+ if (timing->pixel_clock) {
+ newmode = drm_mode_detailed(closure->connector->dev,
+ closure->edid, timing,
+ closure->quirks);
+ if (!newmode)
+ return;
+
+ if (closure->preferred)
+ newmode->type |= DRM_MODE_TYPE_PREFERRED;
+
+ drm_mode_probed_add(closure->connector, newmode);
+ closure->modes++;
+ closure->preferred = 0;
}
+}
- return modes;
+/*
+ * add_detailed_modes - Add modes from detailed timings
+ * @connector: attached connector
+ * @edid: EDID block to scan
+ * @quirks: quirks to apply
+ */
+static int
+add_detailed_modes(struct drm_connector *connector, struct edid *edid,
+ u32 quirks)
+{
+ struct detailed_mode_closure closure = {
+ connector,
+ edid,
+ 1,
+ quirks,
+ 0
+ };
+
+ if (closure.preferred && !version_greater(edid, 1, 3))
+ closure.preferred =
+ (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
+
+ drm_for_each_detailed_block((u8 *)edid, do_detailed_mode, &closure);
+
+ return closure.modes;
}
#define HDMI_IDENTIFIER 0x000C03
@@ -1640,35 +1360,21 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
* - established timing codes
* - modes inferred from GTF or CVT range information
*
- * We don't quite implement this yet, but we're close.
+ * We get this pretty much right.
*
* XXX order for additional mode types in extension blocks?
*/
- num_modes += add_detailed_info(connector, edid, quirks);
- num_modes += add_detailed_info_eedid(connector, edid, quirks);
+ num_modes += add_detailed_modes(connector, edid, quirks);
+ num_modes += add_cvt_modes(connector, edid);
num_modes += add_standard_modes(connector, edid);
num_modes += add_established_modes(connector, edid);
+ num_modes += add_inferred_modes(connector, edid);
if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
edid_fixup_preferred(connector, quirks);
- connector->display_info.serration_vsync = (edid->input & DRM_EDID_INPUT_SERRATION_VSYNC) ? 1 : 0;
- connector->display_info.sync_on_green = (edid->input & DRM_EDID_INPUT_SYNC_ON_GREEN) ? 1 : 0;
- connector->display_info.composite_sync = (edid->input & DRM_EDID_INPUT_COMPOSITE_SYNC) ? 1 : 0;
- connector->display_info.separate_syncs = (edid->input & DRM_EDID_INPUT_SEPARATE_SYNCS) ? 1 : 0;
- connector->display_info.blank_to_black = (edid->input & DRM_EDID_INPUT_BLANK_TO_BLACK) ? 1 : 0;
- connector->display_info.video_level = (edid->input & DRM_EDID_INPUT_VIDEO_LEVEL) >> 5;
- connector->display_info.digital = (edid->input & DRM_EDID_INPUT_DIGITAL) ? 1 : 0;
connector->display_info.width_mm = edid->width_cm * 10;
connector->display_info.height_mm = edid->height_cm * 10;
- connector->display_info.gamma = edid->gamma;
- connector->display_info.gtf_supported = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) ? 1 : 0;
- connector->display_info.standard_color = (edid->features & DRM_EDID_FEATURE_STANDARD_COLOR) ? 1 : 0;
- connector->display_info.display_type = (edid->features & DRM_EDID_FEATURE_DISPLAY_TYPE) >> 3;
- connector->display_info.active_off_supported = (edid->features & DRM_EDID_FEATURE_PM_ACTIVE_OFF) ? 1 : 0;
- connector->display_info.suspend_supported = (edid->features & DRM_EDID_FEATURE_PM_SUSPEND) ? 1 : 0;
- connector->display_info.standby_supported = (edid->features & DRM_EDID_FEATURE_PM_STANDBY) ? 1 : 0;
- connector->display_info.gamma = edid->gamma;
return num_modes;
}
diff --git a/drivers/gpu/drm/drm_edid_modes.h b/drivers/gpu/drm/drm_edid_modes.h
new file mode 100644
index 000000000000..6eb7592e152f
--- /dev/null
+++ b/drivers/gpu/drm/drm_edid_modes.h
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2007-2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include "drmP.h"
+#include "drm_edid.h"
+
+/*
+ * Autogenerated from the DMT spec.
+ * This table is copied from xfree86/modes/xf86EdidModes.c.
+ * But the mode with Reduced blank feature is deleted.
+ */
+static struct drm_display_mode drm_dmt_modes[] = {
+ /* 640x350@85Hz */
+ { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 31500, 640, 672,
+ 736, 832, 0, 350, 382, 385, 445, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 640x400@85Hz */
+ { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 31500, 640, 672,
+ 736, 832, 0, 400, 401, 404, 445, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 720x400@85Hz */
+ { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 756,
+ 828, 936, 0, 400, 401, 404, 446, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 640x480@60Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
+ 752, 800, 0, 480, 489, 492, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 640x480@72Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
+ 704, 832, 0, 480, 489, 492, 520, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 640x480@75Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
+ 720, 840, 0, 480, 481, 484, 500, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 640x480@85Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 36000, 640, 696,
+ 752, 832, 0, 480, 481, 484, 509, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 800x600@56Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
+ 896, 1024, 0, 600, 601, 603, 625, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 800x600@60Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
+ 968, 1056, 0, 600, 601, 605, 628, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 800x600@72Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
+ 976, 1040, 0, 600, 637, 643, 666, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 800x600@75Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
+ 896, 1056, 0, 600, 601, 604, 625, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 800x600@85Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 56250, 800, 832,
+ 896, 1048, 0, 600, 601, 604, 631, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 848x480@60Hz */
+ { DRM_MODE("848x480", DRM_MODE_TYPE_DRIVER, 33750, 848, 864,
+ 976, 1088, 0, 480, 486, 494, 517, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1024x768@43Hz, interlace */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032,
+ 1208, 1264, 0, 768, 768, 772, 817, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
+ DRM_MODE_FLAG_INTERLACE) },
+ /* 1024x768@60Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
+ 1184, 1344, 0, 768, 771, 777, 806, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 1024x768@70Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
+ 1184, 1328, 0, 768, 771, 777, 806, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 1024x768@75Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040,
+ 1136, 1312, 0, 768, 769, 772, 800, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1024x768@85Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 94500, 1024, 1072,
+ 1168, 1376, 0, 768, 769, 772, 808, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1152x864@75Hz */
+ { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
+ 1344, 1600, 0, 864, 865, 868, 900, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x768@60Hz */
+ { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
+ 1472, 1664, 0, 768, 771, 778, 798, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x768@75Hz */
+ { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 102250, 1280, 1360,
+ 1488, 1696, 0, 768, 771, 778, 805, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 1280x768@85Hz */
+ { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 117500, 1280, 1360,
+ 1496, 1712, 0, 768, 771, 778, 809, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x800@60Hz */
+ { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
+ 1480, 1680, 0, 800, 803, 809, 831, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
+ /* 1280x800@75Hz */
+ { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 106500, 1280, 1360,
+ 1488, 1696, 0, 800, 803, 809, 838, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x800@85Hz */
+ { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 122500, 1280, 1360,
+ 1496, 1712, 0, 800, 803, 809, 843, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x960@60Hz */
+ { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
+ 1488, 1800, 0, 960, 961, 964, 1000, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x960@85Hz */
+ { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1344,
+ 1504, 1728, 0, 960, 961, 964, 1011, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x1024@60Hz */
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
+ 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x1024@75Hz */
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
+ 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1280x1024@85Hz */
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 157500, 1280, 1344,
+ 1504, 1728, 0, 1024, 1025, 1028, 1072, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1360x768@60Hz */
+ { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
+ 1536, 1792, 0, 768, 771, 777, 795, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1440x1050@60Hz */
+ { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
+ 1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1440x1050@75Hz */
+ { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 156000, 1400, 1504,
+ 1648, 1896, 0, 1050, 1053, 1057, 1099, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1440x1050@85Hz */
+ { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 179500, 1400, 1504,
+ 1656, 1912, 0, 1050, 1053, 1057, 1105, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1440x900@60Hz */
+ { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
+ 1672, 1904, 0, 900, 903, 909, 934, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1440x900@75Hz */
+ { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 136750, 1440, 1536,
+ 1688, 1936, 0, 900, 903, 909, 942, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1440x900@85Hz */
+ { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 157000, 1440, 1544,
+ 1696, 1952, 0, 900, 903, 909, 948, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1600x1200@60Hz */
+ { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
+ 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1600x1200@65Hz */
+ { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 175500, 1600, 1664,
+ 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1600x1200@70Hz */
+ { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 189000, 1600, 1664,
+ 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1600x1200@75Hz */
+ { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 202500, 1600, 1664,
+ 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1600x1200@85Hz */
+ { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 229500, 1600, 1664,
+ 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1680x1050@60Hz */
+ { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
+ 1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1680x1050@75Hz */
+ { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 187000, 1680, 1800,
+ 1976, 2272, 0, 1050, 1053, 1059, 1099, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1680x1050@85Hz */
+ { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 214750, 1680, 1808,
+ 1984, 2288, 0, 1050, 1053, 1059, 1105, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1792x1344@60Hz */
+ { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
+ 2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1729x1344@75Hz */
+ { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 261000, 1792, 1888,
+ 2104, 2456, 0, 1344, 1345, 1348, 1417, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1853x1392@60Hz */
+ { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
+ 2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1856x1392@75Hz */
+ { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 288000, 1856, 1984,
+ 2208, 2560, 0, 1392, 1395, 1399, 1500, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1920x1200@60Hz */
+ { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
+ 2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1920x1200@75Hz */
+ { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 245250, 1920, 2056,
+ 2264, 2608, 0, 1200, 1203, 1209, 1255, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1920x1200@85Hz */
+ { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 281250, 1920, 2064,
+ 2272, 2624, 0, 1200, 1203, 1209, 1262, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1920x1440@60Hz */
+ { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
+ 2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 1920x1440@75Hz */
+ { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2064,
+ 2288, 2640, 0, 1440, 1441, 1444, 1500, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 2560x1600@60Hz */
+ { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
+ 3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 2560x1600@75HZ */
+ { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 443250, 2560, 2768,
+ 3048, 3536, 0, 1600, 1603, 1609, 1672, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ /* 2560x1600@85HZ */
+ { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 505250, 2560, 2768,
+ 3048, 3536, 0, 1600, 1603, 1609, 1682, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+};
+static const int drm_num_dmt_modes =
+ sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
+
+static struct drm_display_mode edid_est_modes[] = {
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
+ 968, 1056, 0, 600, 601, 605, 628, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
+ 896, 1024, 0, 600, 601, 603, 625, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
+ 720, 840, 0, 480, 481, 484, 500, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
+ 704, 832, 0, 480, 489, 491, 520, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704,
+ 768, 864, 0, 480, 483, 486, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656,
+ 752, 800, 0, 480, 490, 492, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */
+ { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738,
+ 846, 900, 0, 400, 421, 423, 449, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */
+ { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738,
+ 846, 900, 0, 400, 412, 414, 449, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
+ 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040,
+ 1136, 1312, 0, 768, 769, 772, 800, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
+ 1184, 1328, 0, 768, 771, 777, 806, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
+ 1184, 1344, 0, 768, 771, 777, 806, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
+ 1208, 1264, 0, 768, 768, 776, 817, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */
+ { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864,
+ 928, 1152, 0, 624, 625, 628, 667, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
+ 896, 1056, 0, 600, 601, 604, 625, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
+ 976, 1040, 0, 600, 637, 643, 666, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */
+ { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
+ 1344, 1600, 0, 864, 865, 868, 900, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */
+};
+
+static const struct {
+ short w;
+ short h;
+ short r;
+ short rb;
+} est3_modes[] = {
+ /* byte 6 */
+ { 640, 350, 85, 0 },
+ { 640, 400, 85, 0 },
+ { 720, 400, 85, 0 },
+ { 640, 480, 85, 0 },
+ { 848, 480, 60, 0 },
+ { 800, 600, 85, 0 },
+ { 1024, 768, 85, 0 },
+ { 1152, 864, 75, 0 },
+ /* byte 7 */
+ { 1280, 768, 60, 1 },
+ { 1280, 768, 60, 0 },
+ { 1280, 768, 75, 0 },
+ { 1280, 768, 85, 0 },
+ { 1280, 960, 60, 0 },
+ { 1280, 960, 85, 0 },
+ { 1280, 1024, 60, 0 },
+ { 1280, 1024, 85, 0 },
+ /* byte 8 */
+ { 1360, 768, 60, 0 },
+ { 1440, 900, 60, 1 },
+ { 1440, 900, 60, 0 },
+ { 1440, 900, 75, 0 },
+ { 1440, 900, 85, 0 },
+ { 1400, 1050, 60, 1 },
+ { 1400, 1050, 60, 0 },
+ { 1400, 1050, 75, 0 },
+ /* byte 9 */
+ { 1400, 1050, 85, 0 },
+ { 1680, 1050, 60, 1 },
+ { 1680, 1050, 60, 0 },
+ { 1680, 1050, 75, 0 },
+ { 1680, 1050, 85, 0 },
+ { 1600, 1200, 60, 0 },
+ { 1600, 1200, 65, 0 },
+ { 1600, 1200, 70, 0 },
+ /* byte 10 */
+ { 1600, 1200, 75, 0 },
+ { 1600, 1200, 85, 0 },
+ { 1792, 1344, 60, 0 },
+ { 1792, 1344, 85, 0 },
+ { 1856, 1392, 60, 0 },
+ { 1856, 1392, 75, 0 },
+ { 1920, 1200, 60, 1 },
+ { 1920, 1200, 60, 0 },
+ /* byte 11 */
+ { 1920, 1200, 75, 0 },
+ { 1920, 1200, 85, 0 },
+ { 1920, 1440, 60, 0 },
+ { 1920, 1440, 75, 0 },
+};
+static const int num_est3_modes = sizeof(est3_modes) / sizeof(est3_modes[0]);
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index 2ca8df8b6102..3a652a65546f 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -135,15 +135,9 @@ int drm_open(struct inode *inode, struct file *filp)
retcode = drm_open_helper(inode, filp, dev);
if (!retcode) {
atomic_inc(&dev->counts[_DRM_STAT_OPENS]);
- spin_lock(&dev->count_lock);
- if (!dev->open_count++) {
- spin_unlock(&dev->count_lock);
+ if (!dev->open_count++)
retcode = drm_setup(dev);
- goto out;
- }
- spin_unlock(&dev->count_lock);
}
-out:
if (!retcode) {
mutex_lock(&dev->struct_mutex);
if (minor->type == DRM_MINOR_LEGACY) {
@@ -570,18 +564,14 @@ int drm_release(struct inode *inode, struct file *filp)
*/
atomic_inc(&dev->counts[_DRM_STAT_CLOSES]);
- spin_lock(&dev->count_lock);
if (!--dev->open_count) {
if (atomic_read(&dev->ioctl_count)) {
DRM_ERROR("Device busy: %d\n",
atomic_read(&dev->ioctl_count));
retcode = -EBUSY;
- goto out;
- }
- retcode = drm_lastclose(dev);
+ } else
+ retcode = drm_lastclose(dev);
}
-out:
- spin_unlock(&dev->count_lock);
mutex_unlock(&drm_global_mutex);
return retcode;
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 4f1b86714489..bf92d07510df 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -322,7 +322,7 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
- return -EBADF;
+ return -ENOENT;
again:
if (idr_pre_get(&dev->object_name_idr, GFP_KERNEL) == 0) {
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 7b03b197fc00..47db4df37a69 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -392,6 +392,7 @@ int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_pri
if (sv->drm_di_minor >= 1) {
/*
* Version 1.1 includes tying of DRM to specific device
+ * Version 1.4 has proper PCI domain support
*/
retcode = drm_set_busid(dev, file_priv);
if (retcode)
diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c
index 833b35f44a77..08792a740f18 100644
--- a/drivers/gpu/drm/i2c/ch7006_drv.c
+++ b/drivers/gpu/drm/i2c/ch7006_drv.c
@@ -470,6 +470,7 @@ static int ch7006_encoder_init(struct i2c_client *client,
priv->hmargin = 50;
priv->vmargin = 50;
priv->last_dpms = -1;
+ priv->chip_version = ch7006_read(client, CH7006_VERSION_ID);
if (ch7006_tv_norm) {
for (i = 0; i < NUM_TV_NORMS; i++) {
diff --git a/drivers/gpu/drm/i2c/ch7006_mode.c b/drivers/gpu/drm/i2c/ch7006_mode.c
index e447dfb63890..c860f24a5afc 100644
--- a/drivers/gpu/drm/i2c/ch7006_mode.c
+++ b/drivers/gpu/drm/i2c/ch7006_mode.c
@@ -316,7 +316,10 @@ void ch7006_setup_power_state(struct drm_encoder *encoder)
}
} else {
- *power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF);
+ if (priv->chip_version >= 0x20)
+ *power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF);
+ else
+ *power |= bitfs(CH7006_POWER_LEVEL, POWER_OFF);
}
}
diff --git a/drivers/gpu/drm/i2c/ch7006_priv.h b/drivers/gpu/drm/i2c/ch7006_priv.h
index 1c6d2e3bd96f..17667b7d57e7 100644
--- a/drivers/gpu/drm/i2c/ch7006_priv.h
+++ b/drivers/gpu/drm/i2c/ch7006_priv.h
@@ -95,6 +95,7 @@ struct ch7006_priv {
int flicker;
int scale;
+ int chip_version;
int last_dpms;
};
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 2a4ed7ca8b4e..0758c7802e6b 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -456,7 +456,7 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
- return -EBADF;
+ return -ENOENT;
obj_priv = to_intel_bo(obj);
/* Bounds check source.
@@ -919,7 +919,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
- return -EBADF;
+ return -ENOENT;
obj_priv = to_intel_bo(obj);
/* Bounds check destination.
@@ -1002,7 +1002,7 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
- return -EBADF;
+ return -ENOENT;
obj_priv = to_intel_bo(obj);
mutex_lock(&dev->struct_mutex);
@@ -1060,7 +1060,7 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL) {
mutex_unlock(&dev->struct_mutex);
- return -EBADF;
+ return -ENOENT;
}
#if WATCH_BUF
@@ -1099,7 +1099,7 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
- return -EBADF;
+ return -ENOENT;
offset = args->offset;
@@ -1373,7 +1373,7 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
- return -EBADF;
+ return -ENOENT;
mutex_lock(&dev->struct_mutex);
@@ -3364,7 +3364,7 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
reloc->target_handle);
if (target_obj == NULL) {
i915_gem_object_unpin(obj);
- return -EBADF;
+ return -ENOENT;
}
target_obj_priv = to_intel_bo(target_obj);
@@ -3781,7 +3781,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
exec_list[i].handle, i);
/* prevent error path from reading uninitialized data */
args->buffer_count = i + 1;
- ret = -EBADF;
+ ret = -ENOENT;
goto err;
}
@@ -3791,7 +3791,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
object_list[i]);
/* prevent error path from reading uninitialized data */
args->buffer_count = i + 1;
- ret = -EBADF;
+ ret = -EINVAL;
goto err;
}
obj_priv->in_execbuffer = true;
@@ -4265,7 +4265,7 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
DRM_ERROR("Bad handle in i915_gem_pin_ioctl(): %d\n",
args->handle);
mutex_unlock(&dev->struct_mutex);
- return -EBADF;
+ return -ENOENT;
}
obj_priv = to_intel_bo(obj);
@@ -4321,7 +4321,7 @@ i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
DRM_ERROR("Bad handle in i915_gem_unpin_ioctl(): %d\n",
args->handle);
mutex_unlock(&dev->struct_mutex);
- return -EBADF;
+ return -ENOENT;
}
obj_priv = to_intel_bo(obj);
@@ -4355,7 +4355,7 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
if (obj == NULL) {
DRM_ERROR("Bad handle in i915_gem_busy_ioctl(): %d\n",
args->handle);
- return -EBADF;
+ return -ENOENT;
}
mutex_lock(&dev->struct_mutex);
@@ -4408,7 +4408,7 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
if (obj == NULL) {
DRM_ERROR("Bad handle in i915_gem_madvise_ioctl(): %d\n",
args->handle);
- return -EBADF;
+ return -ENOENT;
}
mutex_lock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c
index 155719e4d16f..710eca70b323 100644
--- a/drivers/gpu/drm/i915/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/i915_gem_tiling.c
@@ -275,7 +275,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
- return -EINVAL;
+ return -ENOENT;
obj_priv = to_intel_bo(obj);
if (!i915_tiling_ok(dev, args->stride, obj->size, args->tiling_mode)) {
@@ -362,7 +362,7 @@ i915_gem_get_tiling(struct drm_device *dev, void *data,
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
- return -EINVAL;
+ return -ENOENT;
obj_priv = to_intel_bo(obj);
mutex_lock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 1e5e0d379fa9..5ec10e02341b 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -4429,15 +4429,12 @@ void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
}
static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
- u16 *blue, uint32_t size)
+ u16 *blue, uint32_t start, uint32_t size)
{
+ int end = (start + size > 256) ? 256 : start + size, i;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- int i;
-
- if (size != 256)
- return;
- for (i = 0; i < 256; i++) {
+ for (i = start; i < end; i++) {
intel_crtc->lut_r[i] = red[i] >> 8;
intel_crtc->lut_g[i] = green[i] >> 8;
intel_crtc->lut_b[i] = blue[i] >> 8;
@@ -5412,18 +5409,18 @@ intel_user_framebuffer_create(struct drm_device *dev,
obj = drm_gem_object_lookup(dev, filp, mode_cmd->handle);
if (!obj)
- return NULL;
+ return ERR_PTR(-ENOENT);
intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
if (!intel_fb)
- return NULL;
+ return ERR_PTR(-ENOMEM);
ret = intel_framebuffer_init(dev, intel_fb,
mode_cmd, obj);
if (ret) {
drm_gem_object_unreference_unlocked(obj);
kfree(intel_fb);
- return NULL;
+ return ERR_PTR(ret);
}
return &intel_fb->base;
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
index a79525f434a8..7bdc96256bf5 100644
--- a/drivers/gpu/drm/i915/intel_fb.c
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -121,7 +121,9 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
info->par = ifbdev;
- intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, fbo);
+ ret = intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, fbo);
+ if (ret)
+ goto out_unpin;
fb = &ifbdev->ifb.base;
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index 2405d5ef0ca7..e9b06e4ef2a2 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -12,12 +12,12 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
nouveau_dp.o \
nv04_timer.o \
nv04_mc.o nv40_mc.o nv50_mc.o \
- nv04_fb.o nv10_fb.o nv30_fb.o nv40_fb.o nv50_fb.o \
- nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o \
+ nv04_fb.o nv10_fb.o nv30_fb.o nv40_fb.o nv50_fb.o nvc0_fb.o \
+ nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o nvc0_fifo.o \
nv04_graph.o nv10_graph.o nv20_graph.o \
- nv40_graph.o nv50_graph.o \
+ nv40_graph.o nv50_graph.o nvc0_graph.o \
nv40_grctx.o nv50_grctx.o \
- nv04_instmem.o nv50_instmem.o \
+ nv04_instmem.o nv50_instmem.o nvc0_instmem.o \
nv50_crtc.o nv50_dac.o nv50_sor.o \
nv50_cursor.o nv50_display.o nv50_fbcon.o \
nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 7369b5e73649..0b69a9628c95 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -1928,6 +1928,31 @@ init_condition_time(struct nvbios *bios, uint16_t offset,
}
static int
+init_ltime(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+ /*
+ * INIT_LTIME opcode: 0x57 ('V')
+ *
+ * offset (8 bit): opcode
+ * offset + 1 (16 bit): time
+ *
+ * Sleep for "time" miliseconds.
+ */
+
+ unsigned time = ROM16(bios->data[offset + 1]);
+
+ if (!iexec->execute)
+ return 3;
+
+ BIOSLOG(bios, "0x%04X: Sleeping for 0x%04X miliseconds\n",
+ offset, time);
+
+ msleep(time);
+
+ return 3;
+}
+
+static int
init_zm_reg_sequence(struct nvbios *bios, uint16_t offset,
struct init_exec *iexec)
{
@@ -1995,6 +2020,64 @@ init_sub_direct(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
}
static int
+init_i2c_if(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+ /*
+ * INIT_I2C_IF opcode: 0x5E ('^')
+ *
+ * offset (8 bit): opcode
+ * offset + 1 (8 bit): DCB I2C table entry index
+ * offset + 2 (8 bit): I2C slave address
+ * offset + 3 (8 bit): I2C register
+ * offset + 4 (8 bit): mask
+ * offset + 5 (8 bit): data
+ *
+ * Read the register given by "I2C register" on the device addressed
+ * by "I2C slave address" on the I2C bus given by "DCB I2C table
+ * entry index". Compare the result AND "mask" to "data".
+ * If they're not equal, skip subsequent opcodes until condition is
+ * inverted (INIT_NOT), or we hit INIT_RESUME
+ */
+
+ uint8_t i2c_index = bios->data[offset + 1];
+ uint8_t i2c_address = bios->data[offset + 2] >> 1;
+ uint8_t reg = bios->data[offset + 3];
+ uint8_t mask = bios->data[offset + 4];
+ uint8_t data = bios->data[offset + 5];
+ struct nouveau_i2c_chan *chan;
+ union i2c_smbus_data val;
+ int ret;
+
+ /* no execute check by design */
+
+ BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X\n",
+ offset, i2c_index, i2c_address);
+
+ chan = init_i2c_device_find(bios->dev, i2c_index);
+ if (!chan)
+ return -ENODEV;
+
+ ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
+ I2C_SMBUS_READ, reg,
+ I2C_SMBUS_BYTE_DATA, &val);
+ if (ret < 0) {
+ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Value: [no device], "
+ "Mask: 0x%02X, Data: 0x%02X\n",
+ offset, reg, mask, data);
+ iexec->execute = 0;
+ return 6;
+ }
+
+ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Value: 0x%02X, "
+ "Mask: 0x%02X, Data: 0x%02X\n",
+ offset, reg, val.byte, mask, data);
+
+ iexec->execute = ((val.byte & mask) == data);
+
+ return 6;
+}
+
+static int
init_copy_nv_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
{
/*
@@ -2083,9 +2166,10 @@ peek_fb(struct drm_device *dev, struct io_mapping *fb,
uint32_t val = 0;
if (off < pci_resource_len(dev->pdev, 1)) {
- uint32_t __iomem *p = io_mapping_map_atomic_wc(fb, off, KM_USER0);
+ uint32_t __iomem *p =
+ io_mapping_map_atomic_wc(fb, off & PAGE_MASK, KM_USER0);
- val = ioread32(p);
+ val = ioread32(p + (off & ~PAGE_MASK));
io_mapping_unmap_atomic(p, KM_USER0);
}
@@ -2098,9 +2182,10 @@ poke_fb(struct drm_device *dev, struct io_mapping *fb,
uint32_t off, uint32_t val)
{
if (off < pci_resource_len(dev->pdev, 1)) {
- uint32_t __iomem *p = io_mapping_map_atomic_wc(fb, off, KM_USER0);
+ uint32_t __iomem *p =
+ io_mapping_map_atomic_wc(fb, off & PAGE_MASK, KM_USER0);
- iowrite32(val, p);
+ iowrite32(val, p + (off & ~PAGE_MASK));
wmb();
io_mapping_unmap_atomic(p, KM_USER0);
@@ -2165,7 +2250,7 @@ nv04_init_compute_mem(struct nvbios *bios)
NV04_PFB_BOOT_0_RAM_AMOUNT,
NV04_PFB_BOOT_0_RAM_AMOUNT_4MB);
- } else if (peek_fb(dev, fb, 0) == patt) {
+ } else if (peek_fb(dev, fb, 0) != patt) {
if (read_back_fb(dev, fb, 0x800000, patt))
bios_md32(bios, NV04_PFB_BOOT_0,
NV04_PFB_BOOT_0_RAM_AMOUNT,
@@ -2593,7 +2678,7 @@ init_configure_preinit(struct nvbios *bios, uint16_t offset,
/* no iexec->execute check by design */
uint32_t straps = bios_rd32(bios, NV_PEXTDEV_BOOT_0);
- uint8_t cr3c = ((straps << 2) & 0xf0) | (straps & (1 << 6));
+ uint8_t cr3c = ((straps << 2) & 0xf0) | (straps & 0x40) >> 6;
if (bios->major_version > 2)
return 0;
@@ -3140,7 +3225,7 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c };
int i;
- if (dev_priv->card_type != NV_50) {
+ if (dev_priv->card_type < NV_50) {
NV_ERROR(bios->dev, "INIT_GPIO on unsupported chipset\n");
return 1;
}
@@ -3490,6 +3575,69 @@ init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
return len;
}
+static int
+init_i2c_long_if(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+ /*
+ * INIT_I2C_LONG_IF opcode: 0x9A ('')
+ *
+ * offset (8 bit): opcode
+ * offset + 1 (8 bit): DCB I2C table entry index
+ * offset + 2 (8 bit): I2C slave address
+ * offset + 3 (16 bit): I2C register
+ * offset + 5 (8 bit): mask
+ * offset + 6 (8 bit): data
+ *
+ * Read the register given by "I2C register" on the device addressed
+ * by "I2C slave address" on the I2C bus given by "DCB I2C table
+ * entry index". Compare the result AND "mask" to "data".
+ * If they're not equal, skip subsequent opcodes until condition is
+ * inverted (INIT_NOT), or we hit INIT_RESUME
+ */
+
+ uint8_t i2c_index = bios->data[offset + 1];
+ uint8_t i2c_address = bios->data[offset + 2] >> 1;
+ uint8_t reglo = bios->data[offset + 3];
+ uint8_t reghi = bios->data[offset + 4];
+ uint8_t mask = bios->data[offset + 5];
+ uint8_t data = bios->data[offset + 6];
+ struct nouveau_i2c_chan *chan;
+ uint8_t buf0[2] = { reghi, reglo };
+ uint8_t buf1[1];
+ struct i2c_msg msg[2] = {
+ { i2c_address, 0, 1, buf0 },
+ { i2c_address, I2C_M_RD, 1, buf1 },
+ };
+ int ret;
+
+ /* no execute check by design */
+
+ BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X\n",
+ offset, i2c_index, i2c_address);
+
+ chan = init_i2c_device_find(bios->dev, i2c_index);
+ if (!chan)
+ return -ENODEV;
+
+
+ ret = i2c_transfer(&chan->adapter, msg, 2);
+ if (ret < 0) {
+ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X:0x%02X, Value: [no device], "
+ "Mask: 0x%02X, Data: 0x%02X\n",
+ offset, reghi, reglo, mask, data);
+ iexec->execute = 0;
+ return 7;
+ }
+
+ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X:0x%02X, Value: 0x%02X, "
+ "Mask: 0x%02X, Data: 0x%02X\n",
+ offset, reghi, reglo, buf1[0], mask, data);
+
+ iexec->execute = ((buf1[0] & mask) == data);
+
+ return 7;
+}
+
static struct init_tbl_entry itbl_entry[] = {
/* command name , id , length , offset , mult , command handler */
/* INIT_PROG (0x31, 15, 10, 4) removed due to no example of use */
@@ -3516,9 +3664,11 @@ static struct init_tbl_entry itbl_entry[] = {
{ "INIT_ZM_CR" , 0x53, init_zm_cr },
{ "INIT_ZM_CR_GROUP" , 0x54, init_zm_cr_group },
{ "INIT_CONDITION_TIME" , 0x56, init_condition_time },
+ { "INIT_LTIME" , 0x57, init_ltime },
{ "INIT_ZM_REG_SEQUENCE" , 0x58, init_zm_reg_sequence },
/* INIT_INDIRECT_REG (0x5A, 7, 0, 0) removed due to no example of use */
{ "INIT_SUB_DIRECT" , 0x5B, init_sub_direct },
+ { "INIT_I2C_IF" , 0x5E, init_i2c_if },
{ "INIT_COPY_NV_REG" , 0x5F, init_copy_nv_reg },
{ "INIT_ZM_INDEX_IO" , 0x62, init_zm_index_io },
{ "INIT_COMPUTE_MEM" , 0x63, init_compute_mem },
@@ -3552,6 +3702,7 @@ static struct init_tbl_entry itbl_entry[] = {
{ "INIT_97" , 0x97, init_97 },
{ "INIT_AUXCH" , 0x98, init_auxch },
{ "INIT_ZM_AUXCH" , 0x99, init_zm_auxch },
+ { "INIT_I2C_LONG_IF" , 0x9A, init_i2c_long_if },
{ NULL , 0 , NULL }
};
@@ -4410,7 +4561,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
bios->display.script_table_ptr,
table[2], table[3], table[0] >= 0x21);
if (!otable) {
- NV_ERROR(dev, "Couldn't find matching output script table\n");
+ NV_DEBUG_KMS(dev, "failed to match any output table\n");
return 1;
}
@@ -4467,7 +4618,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
if (script)
script = clkcmptable(bios, script, pxclk);
if (!script) {
- NV_ERROR(dev, "clock script 0 not found\n");
+ NV_DEBUG_KMS(dev, "clock script 0 not found\n");
return 1;
}
@@ -4826,7 +4977,7 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims
pll_lim->min_p = record[12];
pll_lim->max_p = record[13];
/* where did this go to?? */
- if (limit_match == 0x00614100 || limit_match == 0x00614900)
+ if ((entry[0] & 0xf0) == 0x80)
pll_lim->refclk = 27000;
else
pll_lim->refclk = 100000;
@@ -5852,7 +6003,7 @@ static void fabricate_vga_output(struct dcb_table *dcb, int i2c, int heads)
entry->i2c_index = i2c;
entry->heads = heads;
entry->location = DCB_LOC_ON_CHIP;
- /* "or" mostly unused in early gen crt modesetting, 0 is fine */
+ entry->or = 1;
}
static void fabricate_dvi_i_output(struct dcb_table *dcb, bool twoHeads)
@@ -5980,7 +6131,13 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
}
break;
case OUTPUT_TMDS:
- entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4;
+ if (dcb->version >= 0x40)
+ entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4;
+ else if (dcb->version >= 0x30)
+ entry->tmdsconf.slave_addr = (conf & 0x00000700) >> 8;
+ else if (dcb->version >= 0x22)
+ entry->tmdsconf.slave_addr = (conf & 0x00000070) >> 4;
+
break;
case 0xe:
/* weird g80 mobile type that "nv" treats as a terminator */
@@ -6270,6 +6427,19 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
dcb->i2c_table = &bios->data[i2ctabptr];
if (dcb->version >= 0x30)
dcb->i2c_default_indices = dcb->i2c_table[4];
+
+ /*
+ * Parse the "management" I2C bus, used for hardware
+ * monitoring and some external TMDS transmitters.
+ */
+ if (dcb->version >= 0x22) {
+ int idx = (dcb->version >= 0x40 ?
+ dcb->i2c_default_indices & 0xf :
+ 2);
+
+ read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table,
+ idx, &dcb->i2c[idx]);
+ }
}
if (entries > DCB_MAX_NUM_ENTRIES)
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
index 024458a8d060..fd14dfd3d780 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.h
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
@@ -131,6 +131,7 @@ struct dcb_entry {
} dpconf;
struct {
struct sor_conf sor;
+ int slave_addr;
} tmdsconf;
};
bool i2c_upper_default;
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
index 3ca8343c15df..84f85183d041 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -51,9 +51,6 @@ nouveau_bo_del_ttm(struct ttm_buffer_object *bo)
if (nvbo->tile)
nv10_mem_expire_tiling(dev, nvbo->tile, NULL);
- spin_lock(&dev_priv->ttm.bo_list_lock);
- list_del(&nvbo->head);
- spin_unlock(&dev_priv->ttm.bo_list_lock);
kfree(nvbo);
}
@@ -166,9 +163,6 @@ nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan,
}
nvbo->channel = NULL;
- spin_lock(&dev_priv->ttm.bo_list_lock);
- list_add_tail(&nvbo->head, &dev_priv->ttm.bo_list);
- spin_unlock(&dev_priv->ttm.bo_list_lock);
*pnvbo = nvbo;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 734e92635e83..b1b22baf1428 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -37,12 +37,6 @@
#include "nouveau_connector.h"
#include "nouveau_hw.h"
-static inline struct drm_encoder_slave_funcs *
-get_slave_funcs(struct nouveau_encoder *enc)
-{
- return to_encoder_slave(to_drm_encoder(enc))->slave_funcs;
-}
-
static struct nouveau_encoder *
find_encoder_by_type(struct drm_connector *connector, int type)
{
@@ -360,6 +354,7 @@ nouveau_connector_set_property(struct drm_connector *connector,
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
+ struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
struct drm_device *dev = connector->dev;
int ret;
@@ -432,8 +427,8 @@ nouveau_connector_set_property(struct drm_connector *connector,
}
if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV)
- return get_slave_funcs(nv_encoder)->
- set_property(to_drm_encoder(nv_encoder), connector, property, value);
+ return get_slave_funcs(encoder)->set_property(
+ encoder, connector, property, value);
return -EINVAL;
}
@@ -545,6 +540,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
+ struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
int ret = 0;
/* destroy the native mode, the attached monitor could have changed.
@@ -580,8 +576,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
}
if (nv_encoder->dcb->type == OUTPUT_TV)
- ret = get_slave_funcs(nv_encoder)->
- get_modes(to_drm_encoder(nv_encoder), connector);
+ ret = get_slave_funcs(encoder)->get_modes(encoder, connector);
if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS ||
nv_connector->dcb->type == DCB_CONNECTOR_eDP)
@@ -597,6 +592,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
+ struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
unsigned min_clock = 25000, max_clock = min_clock;
unsigned clock = mode->clock;
@@ -623,8 +619,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
max_clock = 350000;
break;
case OUTPUT_TV:
- return get_slave_funcs(nv_encoder)->
- mode_valid(to_drm_encoder(nv_encoder), mode);
+ return get_slave_funcs(encoder)->mode_valid(encoder, mode);
case OUTPUT_DP:
if (nv_encoder->dp.link_bw == DP_LINK_BW_2_7)
max_clock = nv_encoder->dp.link_nr * 270000;
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 74e6b4ed12c0..2e11fd65b4dd 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -84,16 +84,16 @@ nouveau_user_framebuffer_create(struct drm_device *dev,
gem = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle);
if (!gem)
- return NULL;
+ return ERR_PTR(-ENOENT);
nouveau_fb = kzalloc(sizeof(struct nouveau_framebuffer), GFP_KERNEL);
if (!nouveau_fb)
- return NULL;
+ return ERR_PTR(-ENOMEM);
ret = nouveau_framebuffer_init(dev, nouveau_fb, mode_cmd, nouveau_gem_object(gem));
if (ret) {
drm_gem_object_unreference(gem);
- return NULL;
+ return ERR_PTR(ret);
}
return &nouveau_fb->base;
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index 33742b11188b..8a1b188b4cd1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -572,47 +572,64 @@ out:
return ret ? ret : (stat & NV50_AUXCH_STAT_REPLY);
}
-int
-nouveau_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
- uint8_t write_byte, uint8_t *read_byte)
+static int
+nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
- struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
- struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adapter;
+ struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adap;
struct drm_device *dev = auxch->dev;
- int ret = 0, cmd, addr = algo_data->address;
- uint8_t *buf;
-
- if (mode == MODE_I2C_READ) {
- cmd = AUX_I2C_READ;
- buf = read_byte;
- } else {
- cmd = (mode & MODE_I2C_READ) ? AUX_I2C_READ : AUX_I2C_WRITE;
- buf = &write_byte;
- }
+ struct i2c_msg *msg = msgs;
+ int ret, mcnt = num;
- if (!(mode & MODE_I2C_STOP))
- cmd |= AUX_I2C_MOT;
+ while (mcnt--) {
+ u8 remaining = msg->len;
+ u8 *ptr = msg->buf;
- if (mode & MODE_I2C_START)
- return 1;
+ while (remaining) {
+ u8 cnt = (remaining > 16) ? 16 : remaining;
+ u8 cmd;
- for (;;) {
- ret = nouveau_dp_auxch(auxch, cmd, addr, buf, 1);
- if (ret < 0)
- return ret;
-
- switch (ret & NV50_AUXCH_STAT_REPLY_I2C) {
- case NV50_AUXCH_STAT_REPLY_I2C_ACK:
- return 1;
- case NV50_AUXCH_STAT_REPLY_I2C_NACK:
- return -EREMOTEIO;
- case NV50_AUXCH_STAT_REPLY_I2C_DEFER:
- udelay(100);
- break;
- default:
- NV_ERROR(dev, "invalid auxch status: 0x%08x\n", ret);
- return -EREMOTEIO;
+ if (msg->flags & I2C_M_RD)
+ cmd = AUX_I2C_READ;
+ else
+ cmd = AUX_I2C_WRITE;
+
+ if (mcnt || remaining > 16)
+ cmd |= AUX_I2C_MOT;
+
+ ret = nouveau_dp_auxch(auxch, cmd, msg->addr, ptr, cnt);
+ if (ret < 0)
+ return ret;
+
+ switch (ret & NV50_AUXCH_STAT_REPLY_I2C) {
+ case NV50_AUXCH_STAT_REPLY_I2C_ACK:
+ break;
+ case NV50_AUXCH_STAT_REPLY_I2C_NACK:
+ return -EREMOTEIO;
+ case NV50_AUXCH_STAT_REPLY_I2C_DEFER:
+ udelay(100);
+ continue;
+ default:
+ NV_ERROR(dev, "bad auxch reply: 0x%08x\n", ret);
+ return -EREMOTEIO;
+ }
+
+ ptr += cnt;
+ remaining -= cnt;
}
+
+ msg++;
}
+
+ return num;
+}
+
+static u32
+nouveau_dp_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
+const struct i2c_algorithm nouveau_dp_i2c_algo = {
+ .master_xfer = nouveau_dp_i2c_xfer,
+ .functionality = nouveau_dp_i2c_func
+};
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index e15db15dca77..e424bf74d706 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -410,7 +410,7 @@ enum nv04_fp_display_regs {
struct nv04_crtc_reg {
unsigned char MiscOutReg; /* */
- uint8_t CRTC[0x9f];
+ uint8_t CRTC[0xa0];
uint8_t CR58[0x10];
uint8_t Sequencer[5];
uint8_t Graphics[9];
@@ -509,6 +509,7 @@ enum nouveau_card_type {
NV_30 = 0x30,
NV_40 = 0x40,
NV_50 = 0x50,
+ NV_C0 = 0xc0,
};
struct drm_nouveau_private {
@@ -536,8 +537,6 @@ struct drm_nouveau_private {
struct drm_global_reference mem_global_ref;
struct ttm_bo_global_ref bo_global_ref;
struct ttm_bo_device bdev;
- spinlock_t bo_list_lock;
- struct list_head bo_list;
atomic_t validate_sequence;
} ttm;
@@ -931,6 +930,10 @@ extern void nv40_fb_set_region_tiling(struct drm_device *, int, uint32_t,
extern int nv50_fb_init(struct drm_device *);
extern void nv50_fb_takedown(struct drm_device *);
+/* nvc0_fb.c */
+extern int nvc0_fb_init(struct drm_device *);
+extern void nvc0_fb_takedown(struct drm_device *);
+
/* nv04_fifo.c */
extern int nv04_fifo_init(struct drm_device *);
extern void nv04_fifo_disable(struct drm_device *);
@@ -968,6 +971,20 @@ extern void nv50_fifo_destroy_context(struct nouveau_channel *);
extern int nv50_fifo_load_context(struct nouveau_channel *);
extern int nv50_fifo_unload_context(struct drm_device *);
+/* nvc0_fifo.c */
+extern int nvc0_fifo_init(struct drm_device *);
+extern void nvc0_fifo_takedown(struct drm_device *);
+extern void nvc0_fifo_disable(struct drm_device *);
+extern void nvc0_fifo_enable(struct drm_device *);
+extern bool nvc0_fifo_reassign(struct drm_device *, bool);
+extern bool nvc0_fifo_cache_flush(struct drm_device *);
+extern bool nvc0_fifo_cache_pull(struct drm_device *, bool);
+extern int nvc0_fifo_channel_id(struct drm_device *);
+extern int nvc0_fifo_create_context(struct nouveau_channel *);
+extern void nvc0_fifo_destroy_context(struct nouveau_channel *);
+extern int nvc0_fifo_load_context(struct nouveau_channel *);
+extern int nvc0_fifo_unload_context(struct drm_device *);
+
/* nv04_graph.c */
extern struct nouveau_pgraph_object_class nv04_graph_grclass[];
extern int nv04_graph_init(struct drm_device *);
@@ -1032,6 +1049,16 @@ extern int nv50_graph_unload_context(struct drm_device *);
extern void nv50_graph_context_switch(struct drm_device *);
extern int nv50_grctx_init(struct nouveau_grctx *);
+/* nvc0_graph.c */
+extern int nvc0_graph_init(struct drm_device *);
+extern void nvc0_graph_takedown(struct drm_device *);
+extern void nvc0_graph_fifo_access(struct drm_device *, bool);
+extern struct nouveau_channel *nvc0_graph_channel(struct drm_device *);
+extern int nvc0_graph_create_context(struct nouveau_channel *);
+extern void nvc0_graph_destroy_context(struct nouveau_channel *);
+extern int nvc0_graph_load_context(struct nouveau_channel *);
+extern int nvc0_graph_unload_context(struct drm_device *);
+
/* nv04_instmem.c */
extern int nv04_instmem_init(struct drm_device *);
extern void nv04_instmem_takedown(struct drm_device *);
@@ -1058,6 +1085,18 @@ extern void nv50_instmem_flush(struct drm_device *);
extern void nv84_instmem_flush(struct drm_device *);
extern void nv50_vm_flush(struct drm_device *, int engine);
+/* nvc0_instmem.c */
+extern int nvc0_instmem_init(struct drm_device *);
+extern void nvc0_instmem_takedown(struct drm_device *);
+extern int nvc0_instmem_suspend(struct drm_device *);
+extern void nvc0_instmem_resume(struct drm_device *);
+extern int nvc0_instmem_populate(struct drm_device *, struct nouveau_gpuobj *,
+ uint32_t *size);
+extern void nvc0_instmem_clear(struct drm_device *, struct nouveau_gpuobj *);
+extern int nvc0_instmem_bind(struct drm_device *, struct nouveau_gpuobj *);
+extern int nvc0_instmem_unbind(struct drm_device *, struct nouveau_gpuobj *);
+extern void nvc0_instmem_flush(struct drm_device *);
+
/* nv04_mc.c */
extern int nv04_mc_init(struct drm_device *);
extern void nv04_mc_takedown(struct drm_device *);
diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h
index a1a0d48ae70c..7c82d68bc155 100644
--- a/drivers/gpu/drm/nouveau/nouveau_encoder.h
+++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h
@@ -71,6 +71,12 @@ static inline struct drm_encoder *to_drm_encoder(struct nouveau_encoder *enc)
return &enc->base.base;
}
+static inline struct drm_encoder_slave_funcs *
+get_slave_funcs(struct drm_encoder *enc)
+{
+ return to_encoder_slave(enc)->slave_funcs;
+}
+
struct nouveau_connector *
nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
int nv50_sor_create(struct drm_connector *, struct dcb_entry *);
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 099f637264aa..dbd30b2e43fd 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -281,6 +281,8 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev,
if (dev_priv->channel && !nouveau_nofbaccel) {
switch (dev_priv->card_type) {
+ case NV_C0:
+ break;
case NV_50:
nv50_fbcon_accel_init(info);
info->fbops = &nv50_fbcon_ops;
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index 547f2c24c1e7..0f417ac1b696 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -284,7 +284,7 @@ retry:
if (!gem) {
NV_ERROR(dev, "Unknown handle 0x%08x\n", b->handle);
validate_fini(op, NULL);
- return -EINVAL;
+ return -ENOENT;
}
nvbo = gem->driver_private;
@@ -759,7 +759,7 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,
gem = drm_gem_object_lookup(dev, file_priv, req->handle);
if (!gem)
- return ret;
+ return -ENOENT;
nvbo = nouveau_gem_object(gem);
if (nvbo->cpu_filp) {
@@ -797,7 +797,7 @@ nouveau_gem_ioctl_cpu_fini(struct drm_device *dev, void *data,
gem = drm_gem_object_lookup(dev, file_priv, req->handle);
if (!gem)
- return ret;
+ return -ENOENT;
nvbo = nouveau_gem_object(gem);
if (nvbo->cpu_filp != file_priv)
@@ -822,7 +822,7 @@ nouveau_gem_ioctl_info(struct drm_device *dev, void *data,
gem = drm_gem_object_lookup(dev, file_priv, req->handle);
if (!gem)
- return -EINVAL;
+ return -ENOENT;
ret = nouveau_gem_info(gem, req);
drm_gem_object_unreference_unlocked(gem);
diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c b/drivers/gpu/drm/nouveau/nouveau_hw.c
index 7855b35effc3..7b613682e400 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hw.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hw.c
@@ -865,8 +865,12 @@ nv_save_state_ext(struct drm_device *dev, int head,
rd_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX);
rd_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
rd_cio_state(dev, head, regp, NV_CIO_CRE_21);
- if (dev_priv->card_type >= NV_30)
+
+ if (dev_priv->card_type >= NV_30) {
rd_cio_state(dev, head, regp, NV_CIO_CRE_47);
+ rd_cio_state(dev, head, regp, 0x9f);
+ }
+
rd_cio_state(dev, head, regp, NV_CIO_CRE_49);
rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
@@ -971,8 +975,11 @@ nv_load_state_ext(struct drm_device *dev, int head,
wr_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX);
wr_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX);
wr_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
- if (dev_priv->card_type >= NV_30)
+
+ if (dev_priv->card_type >= NV_30) {
wr_cio_state(dev, head, regp, NV_CIO_CRE_47);
+ wr_cio_state(dev, head, regp, 0x9f);
+ }
wr_cio_state(dev, head, regp, NV_CIO_CRE_49);
wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c
index cb0cb34440c6..0bd407ca3d42 100644
--- a/drivers/gpu/drm/nouveau/nouveau_i2c.c
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c
@@ -163,7 +163,7 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
if (entry->chan)
return -EEXIST;
- if (dev_priv->card_type == NV_50 && entry->read >= NV50_I2C_PORTS) {
+ if (dev_priv->card_type == NV_C0 && entry->read >= NV50_I2C_PORTS) {
NV_ERROR(dev, "unknown i2c port %d\n", entry->read);
return -EINVAL;
}
@@ -174,26 +174,26 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
switch (entry->port_type) {
case 0:
- i2c->algo.bit.setsda = nv04_i2c_setsda;
- i2c->algo.bit.setscl = nv04_i2c_setscl;
- i2c->algo.bit.getsda = nv04_i2c_getsda;
- i2c->algo.bit.getscl = nv04_i2c_getscl;
+ i2c->bit.setsda = nv04_i2c_setsda;
+ i2c->bit.setscl = nv04_i2c_setscl;
+ i2c->bit.getsda = nv04_i2c_getsda;
+ i2c->bit.getscl = nv04_i2c_getscl;
i2c->rd = entry->read;
i2c->wr = entry->write;
break;
case 4:
- i2c->algo.bit.setsda = nv4e_i2c_setsda;
- i2c->algo.bit.setscl = nv4e_i2c_setscl;
- i2c->algo.bit.getsda = nv4e_i2c_getsda;
- i2c->algo.bit.getscl = nv4e_i2c_getscl;
+ i2c->bit.setsda = nv4e_i2c_setsda;
+ i2c->bit.setscl = nv4e_i2c_setscl;
+ i2c->bit.getsda = nv4e_i2c_getsda;
+ i2c->bit.getscl = nv4e_i2c_getscl;
i2c->rd = 0x600800 + entry->read;
i2c->wr = 0x600800 + entry->write;
break;
case 5:
- i2c->algo.bit.setsda = nv50_i2c_setsda;
- i2c->algo.bit.setscl = nv50_i2c_setscl;
- i2c->algo.bit.getsda = nv50_i2c_getsda;
- i2c->algo.bit.getscl = nv50_i2c_getscl;
+ i2c->bit.setsda = nv50_i2c_setsda;
+ i2c->bit.setscl = nv50_i2c_setscl;
+ i2c->bit.getsda = nv50_i2c_getsda;
+ i2c->bit.getscl = nv50_i2c_getscl;
i2c->rd = nv50_i2c_port[entry->read];
i2c->wr = i2c->rd;
break;
@@ -216,17 +216,14 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
i2c_set_adapdata(&i2c->adapter, i2c);
if (entry->port_type < 6) {
- i2c->adapter.algo_data = &i2c->algo.bit;
- i2c->algo.bit.udelay = 40;
- i2c->algo.bit.timeout = usecs_to_jiffies(5000);
- i2c->algo.bit.data = i2c;
+ i2c->adapter.algo_data = &i2c->bit;
+ i2c->bit.udelay = 40;
+ i2c->bit.timeout = usecs_to_jiffies(5000);
+ i2c->bit.data = i2c;
ret = i2c_bit_add_bus(&i2c->adapter);
} else {
- i2c->adapter.algo_data = &i2c->algo.dp;
- i2c->algo.dp.running = false;
- i2c->algo.dp.address = 0;
- i2c->algo.dp.aux_ch = nouveau_dp_i2c_aux_ch;
- ret = i2c_dp_aux_add_bus(&i2c->adapter);
+ i2c->adapter.algo = &nouveau_dp_i2c_algo;
+ ret = i2c_add_adapter(&i2c->adapter);
}
if (ret) {
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h
index 6dd2f8713cd1..f71cb32f7571 100644
--- a/drivers/gpu/drm/nouveau/nouveau_i2c.h
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h
@@ -33,10 +33,7 @@ struct dcb_i2c_entry;
struct nouveau_i2c_chan {
struct i2c_adapter adapter;
struct drm_device *dev;
- union {
- struct i2c_algo_bit_data bit;
- struct i2c_algo_dp_aux_data dp;
- } algo;
+ struct i2c_algo_bit_data bit;
unsigned rd;
unsigned wr;
unsigned data;
@@ -49,7 +46,6 @@ bool nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr);
int nouveau_i2c_identify(struct drm_device *dev, const char *what,
struct i2c_board_info *info, int index);
-int nouveau_dp_i2c_aux_ch(struct i2c_adapter *, int mode, uint8_t write_byte,
- uint8_t *read_byte);
+extern const struct i2c_algorithm nouveau_dp_i2c_algo;
#endif /* __NOUVEAU_I2C_H__ */
diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c
index 53360f156063..794b0ee30cf6 100644
--- a/drivers/gpu/drm/nouveau/nouveau_irq.c
+++ b/drivers/gpu/drm/nouveau/nouveau_irq.c
@@ -49,7 +49,7 @@ nouveau_irq_preinstall(struct drm_device *dev)
/* Master disable */
nv_wr32(dev, NV03_PMC_INTR_EN_0, 0);
- if (dev_priv->card_type == NV_50) {
+ if (dev_priv->card_type >= NV_50) {
INIT_WORK(&dev_priv->irq_work, nv50_display_irq_handler_bh);
INIT_WORK(&dev_priv->hpd_work, nv50_display_irq_hotplug_bh);
INIT_LIST_HEAD(&dev_priv->vbl_waiting);
@@ -586,11 +586,11 @@ nouveau_pgraph_irq_handler(struct drm_device *dev)
}
if (status & NV_PGRAPH_INTR_CONTEXT_SWITCH) {
- nouveau_pgraph_intr_context_switch(dev);
-
status &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH;
nv_wr32(dev, NV03_PGRAPH_INTR,
NV_PGRAPH_INTR_CONTEXT_SWITCH);
+
+ nouveau_pgraph_intr_context_switch(dev);
}
if (status) {
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
index a9f36ab256b7..9689d4147686 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -320,7 +320,8 @@ nouveau_mem_detect(struct drm_device *dev)
if (dev_priv->card_type < NV_50) {
dev_priv->vram_size = nv_rd32(dev, NV04_PFB_FIFO_DATA);
dev_priv->vram_size &= NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK;
- } else {
+ } else
+ if (dev_priv->card_type < NV_C0) {
dev_priv->vram_size = nv_rd32(dev, NV04_PFB_FIFO_DATA);
dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32;
dev_priv->vram_size &= 0xffffffff00ll;
@@ -328,6 +329,9 @@ nouveau_mem_detect(struct drm_device *dev)
dev_priv->vram_sys_base = nv_rd32(dev, 0x100e10);
dev_priv->vram_sys_base <<= 12;
}
+ } else {
+ dev_priv->vram_size = nv_rd32(dev, 0x10f20c) << 20;
+ dev_priv->vram_size *= nv_rd32(dev, 0x121c74);
}
NV_INFO(dev, "Detected %dMiB VRAM\n", (int)(dev_priv->vram_size >> 20));
@@ -351,7 +355,7 @@ nouveau_mem_reset_agp(struct drm_device *dev)
/* First of all, disable fast writes, otherwise if it's
* already enabled in the AGP bridge and we disable the card's
* AGP controller we might be locking ourselves out of it. */
- if (dev->agp->acquired) {
+ if (nv_rd32(dev, NV04_PBUS_PCI_NV_19) & PCI_AGP_COMMAND_FW) {
struct drm_agp_info info;
struct drm_agp_mode mode;
@@ -359,7 +363,7 @@ nouveau_mem_reset_agp(struct drm_device *dev)
if (ret)
return ret;
- mode.mode = info.mode & ~0x10;
+ mode.mode = info.mode & ~PCI_AGP_COMMAND_FW;
ret = drm_agp_enable(dev, mode);
if (ret)
return ret;
@@ -405,6 +409,8 @@ nouveau_mem_init_agp(struct drm_device *dev)
}
}
+ nouveau_mem_reset_agp(dev);
+
ret = drm_agp_info(dev, &info);
if (ret) {
NV_ERROR(dev, "Unable to get AGP info: %d\n", ret);
@@ -459,8 +465,6 @@ nouveau_mem_init(struct drm_device *dev)
return ret;
}
- INIT_LIST_HEAD(&dev_priv->ttm.bo_list);
- spin_lock_init(&dev_priv->ttm.bo_list_lock);
spin_lock_init(&dev_priv->tile.lock);
dev_priv->fb_available_size = dev_priv->vram_size;
@@ -494,7 +498,6 @@ nouveau_mem_init(struct drm_device *dev)
/* GART */
#if !defined(__powerpc__) && !defined(__ia64__)
if (drm_device_is_agp(dev) && dev->agp && !nouveau_noagp) {
- nouveau_mem_reset_agp(dev);
ret = nouveau_mem_init_agp(dev);
if (ret)
NV_ERROR(dev, "Error initialising AGP: %d\n", ret);
diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h
index 9c1056cb8a90..21a6e453b975 100644
--- a/drivers/gpu/drm/nouveau/nouveau_reg.h
+++ b/drivers/gpu/drm/nouveau/nouveau_reg.h
@@ -220,28 +220,21 @@
# define NV_PGRAPH_INTR_ERROR (1<<20)
#define NV10_PGRAPH_CTX_CONTROL 0x00400144
#define NV10_PGRAPH_CTX_USER 0x00400148
-#define NV10_PGRAPH_CTX_SWITCH1 0x0040014C
-#define NV10_PGRAPH_CTX_SWITCH2 0x00400150
-#define NV10_PGRAPH_CTX_SWITCH3 0x00400154
-#define NV10_PGRAPH_CTX_SWITCH4 0x00400158
-#define NV10_PGRAPH_CTX_SWITCH5 0x0040015C
+#define NV10_PGRAPH_CTX_SWITCH(i) (0x0040014C + 0x4*(i))
#define NV04_PGRAPH_CTX_SWITCH1 0x00400160
-#define NV10_PGRAPH_CTX_CACHE1 0x00400160
+#define NV10_PGRAPH_CTX_CACHE(i, j) (0x00400160 \
+ + 0x4*(i) + 0x20*(j))
#define NV04_PGRAPH_CTX_SWITCH2 0x00400164
#define NV04_PGRAPH_CTX_SWITCH3 0x00400168
#define NV04_PGRAPH_CTX_SWITCH4 0x0040016C
#define NV04_PGRAPH_CTX_CONTROL 0x00400170
#define NV04_PGRAPH_CTX_USER 0x00400174
#define NV04_PGRAPH_CTX_CACHE1 0x00400180
-#define NV10_PGRAPH_CTX_CACHE2 0x00400180
#define NV03_PGRAPH_CTX_CONTROL 0x00400190
#define NV03_PGRAPH_CTX_USER 0x00400194
#define NV04_PGRAPH_CTX_CACHE2 0x004001A0
-#define NV10_PGRAPH_CTX_CACHE3 0x004001A0
#define NV04_PGRAPH_CTX_CACHE3 0x004001C0
-#define NV10_PGRAPH_CTX_CACHE4 0x004001C0
#define NV04_PGRAPH_CTX_CACHE4 0x004001E0
-#define NV10_PGRAPH_CTX_CACHE5 0x004001E0
#define NV40_PGRAPH_CTXCTL_0304 0x00400304
#define NV40_PGRAPH_CTXCTL_0304_XFER_CTX 0x00000001
#define NV40_PGRAPH_CTXCTL_UCODE_STAT 0x00400308
@@ -356,9 +349,12 @@
#define NV04_PGRAPH_FFINTFC_ST2 0x00400754
#define NV10_PGRAPH_RDI_DATA 0x00400754
#define NV04_PGRAPH_DMA_PITCH 0x00400760
-#define NV10_PGRAPH_FFINTFC_ST2 0x00400764
+#define NV10_PGRAPH_FFINTFC_FIFO_PTR 0x00400760
#define NV04_PGRAPH_DVD_COLORFMT 0x00400764
+#define NV10_PGRAPH_FFINTFC_ST2 0x00400764
#define NV04_PGRAPH_SCALED_FORMAT 0x00400768
+#define NV10_PGRAPH_FFINTFC_ST2_DL 0x00400768
+#define NV10_PGRAPH_FFINTFC_ST2_DH 0x0040076c
#define NV10_PGRAPH_DMA_PITCH 0x00400770
#define NV10_PGRAPH_DVD_COLORFMT 0x00400774
#define NV10_PGRAPH_SCALED_FORMAT 0x00400778
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index ee3729e7823b..989322be3728 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -359,6 +359,54 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->gpio.set = nv50_gpio_set;
engine->gpio.irq_enable = nv50_gpio_irq_enable;
break;
+ case 0xC0:
+ engine->instmem.init = nvc0_instmem_init;
+ engine->instmem.takedown = nvc0_instmem_takedown;
+ engine->instmem.suspend = nvc0_instmem_suspend;
+ engine->instmem.resume = nvc0_instmem_resume;
+ engine->instmem.populate = nvc0_instmem_populate;
+ engine->instmem.clear = nvc0_instmem_clear;
+ engine->instmem.bind = nvc0_instmem_bind;
+ engine->instmem.unbind = nvc0_instmem_unbind;
+ engine->instmem.flush = nvc0_instmem_flush;
+ engine->mc.init = nv50_mc_init;
+ engine->mc.takedown = nv50_mc_takedown;
+ engine->timer.init = nv04_timer_init;
+ engine->timer.read = nv04_timer_read;
+ engine->timer.takedown = nv04_timer_takedown;
+ engine->fb.init = nvc0_fb_init;
+ engine->fb.takedown = nvc0_fb_takedown;
+ engine->graph.grclass = NULL; //nvc0_graph_grclass;
+ engine->graph.init = nvc0_graph_init;
+ engine->graph.takedown = nvc0_graph_takedown;
+ engine->graph.fifo_access = nvc0_graph_fifo_access;
+ engine->graph.channel = nvc0_graph_channel;
+ engine->graph.create_context = nvc0_graph_create_context;
+ engine->graph.destroy_context = nvc0_graph_destroy_context;
+ engine->graph.load_context = nvc0_graph_load_context;
+ engine->graph.unload_context = nvc0_graph_unload_context;
+ engine->fifo.channels = 128;
+ engine->fifo.init = nvc0_fifo_init;
+ engine->fifo.takedown = nvc0_fifo_takedown;
+ engine->fifo.disable = nvc0_fifo_disable;
+ engine->fifo.enable = nvc0_fifo_enable;
+ engine->fifo.reassign = nvc0_fifo_reassign;
+ engine->fifo.channel_id = nvc0_fifo_channel_id;
+ engine->fifo.create_context = nvc0_fifo_create_context;
+ engine->fifo.destroy_context = nvc0_fifo_destroy_context;
+ engine->fifo.load_context = nvc0_fifo_load_context;
+ engine->fifo.unload_context = nvc0_fifo_unload_context;
+ engine->display.early_init = nv50_display_early_init;
+ engine->display.late_takedown = nv50_display_late_takedown;
+ engine->display.create = nv50_display_create;
+ engine->display.init = nv50_display_init;
+ engine->display.destroy = nv50_display_destroy;
+ engine->gpio.init = nv50_gpio_init;
+ engine->gpio.takedown = nouveau_stub_takedown;
+ engine->gpio.get = nv50_gpio_get;
+ engine->gpio.set = nv50_gpio_set;
+ engine->gpio.irq_enable = nv50_gpio_irq_enable;
+ break;
default:
NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset);
return 1;
@@ -739,8 +787,10 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
int ret;
dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
- if (!dev_priv)
- return -ENOMEM;
+ if (!dev_priv) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
dev->dev_private = dev_priv;
dev_priv->dev = dev;
@@ -750,8 +800,10 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
dev->pci_vendor, dev->pci_device, dev->pdev->class);
dev_priv->wq = create_workqueue("nouveau");
- if (!dev_priv->wq)
- return -EINVAL;
+ if (!dev_priv->wq) {
+ ret = -EINVAL;
+ goto err_priv;
+ }
/* resource 0 is mmio regs */
/* resource 1 is linear FB */
@@ -764,7 +816,8 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
if (!dev_priv->mmio) {
NV_ERROR(dev, "Unable to initialize the mmio mapping. "
"Please report your setup to " DRIVER_EMAIL "\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_wq;
}
NV_DEBUG(dev, "regs mapped ok at 0x%llx\n",
(unsigned long long)mmio_start_offs);
@@ -810,9 +863,13 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
case 0xa0:
dev_priv->card_type = NV_50;
break;
+ case 0xc0:
+ dev_priv->card_type = NV_C0;
+ break;
default:
NV_INFO(dev, "Unsupported chipset 0x%08x\n", reg0);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_mmio;
}
NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n",
@@ -820,7 +877,7 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
ret = nouveau_remove_conflicting_drivers(dev);
if (ret)
- return ret;
+ goto err_mmio;
/* Map PRAMIN BAR, or on older cards, the aperture withing BAR0 */
if (dev_priv->card_type >= NV_40) {
@@ -834,7 +891,8 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
dev_priv->ramin_size);
if (!dev_priv->ramin) {
NV_ERROR(dev, "Failed to PRAMIN BAR");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_mmio;
}
} else {
dev_priv->ramin_size = 1 * 1024 * 1024;
@@ -842,7 +900,8 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
dev_priv->ramin_size);
if (!dev_priv->ramin) {
NV_ERROR(dev, "Failed to map BAR0 PRAMIN.\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_mmio;
}
}
@@ -857,9 +916,21 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
/* For kernel modesetting, init card now and bring up fbcon */
ret = nouveau_card_init(dev);
if (ret)
- return ret;
+ goto err_ramin;
return 0;
+
+err_ramin:
+ iounmap(dev_priv->ramin);
+err_mmio:
+ iounmap(dev_priv->mmio);
+err_wq:
+ destroy_workqueue(dev_priv->wq);
+err_priv:
+ kfree(dev_priv);
+ dev->dev_private = NULL;
+err_out:
+ return ret;
}
void nouveau_lastclose(struct drm_device *dev)
diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c
index 1c20c08ce67c..497df8765f28 100644
--- a/drivers/gpu/drm/nouveau/nv04_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv04_crtc.c
@@ -542,6 +542,9 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
* 1 << 30 on 0x60.830), for no apparent reason */
regp->CRTC[NV_CIO_CRE_59] = off_chip_digital;
+ if (dev_priv->card_type >= NV_30)
+ regp->CRTC[0x9f] = off_chip_digital ? 0x11 : 0x1;
+
regp->crtc_830 = mode->crtc_vdisplay - 3;
regp->crtc_834 = mode->crtc_vdisplay - 1;
@@ -739,15 +742,13 @@ nv_crtc_gamma_load(struct drm_crtc *crtc)
}
static void
-nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t size)
+nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t start,
+ uint32_t size)
{
+ int end = (start + size > 256) ? 256 : start + size, i;
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
- int i;
- if (size != 256)
- return;
-
- for (i = 0; i < 256; i++) {
+ for (i = start; i < end; i++) {
nv_crtc->lut.r[i] = r[i];
nv_crtc->lut.g[i] = g[i];
nv_crtc->lut.b[i] = b[i];
@@ -914,7 +915,7 @@ nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
gem = drm_gem_object_lookup(dev, file_priv, buffer_handle);
if (!gem)
- return -EINVAL;
+ return -ENOENT;
cursor = nouveau_gem_object(gem);
ret = nouveau_bo_map(cursor);
diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c
index 3311f3a8c818..a5dcf7685800 100644
--- a/drivers/gpu/drm/nouveau/nv04_dfp.c
+++ b/drivers/gpu/drm/nouveau/nv04_dfp.c
@@ -34,6 +34,8 @@
#include "nouveau_hw.h"
#include "nvreg.h"
+#include "i2c/sil164.h"
+
#define FP_TG_CONTROL_ON (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | \
NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | \
NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS)
@@ -144,6 +146,36 @@ void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode)
}
}
+static struct drm_encoder *get_tmds_slave(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+ struct drm_encoder *slave;
+
+ if (dcb->type != OUTPUT_TMDS || dcb->location == DCB_LOC_ON_CHIP)
+ return NULL;
+
+ /* Some BIOSes (e.g. the one in a Quadro FX1000) report several
+ * TMDS transmitters at the same I2C address, in the same I2C
+ * bus. This can still work because in that case one of them is
+ * always hard-wired to a reasonable configuration using straps,
+ * and the other one needs to be programmed.
+ *
+ * I don't think there's a way to know which is which, even the
+ * blob programs the one exposed via I2C for *both* heads, so
+ * let's do the same.
+ */
+ list_for_each_entry(slave, &dev->mode_config.encoder_list, head) {
+ struct dcb_entry *slave_dcb = nouveau_encoder(slave)->dcb;
+
+ if (slave_dcb->type == OUTPUT_TMDS && get_slave_funcs(slave) &&
+ slave_dcb->tmdsconf.slave_addr == dcb->tmdsconf.slave_addr)
+ return slave;
+ }
+
+ return NULL;
+}
+
static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
@@ -429,6 +461,11 @@ static void nv04_dfp_commit(struct drm_encoder *encoder)
else
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
+ /* Init external transmitters */
+ if (get_tmds_slave(encoder))
+ get_slave_funcs(get_tmds_slave(encoder))->mode_set(
+ encoder, &nv_encoder->mode, &nv_encoder->mode);
+
helper->dpms(encoder, DRM_MODE_DPMS_ON);
NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
@@ -550,10 +587,42 @@ static void nv04_dfp_destroy(struct drm_encoder *encoder)
NV_DEBUG_KMS(encoder->dev, "\n");
+ if (get_slave_funcs(encoder))
+ get_slave_funcs(encoder)->destroy(encoder);
+
drm_encoder_cleanup(encoder);
kfree(nv_encoder);
}
+static void nv04_tmds_slave_init(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+ struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, 2);
+ struct i2c_board_info info[] = {
+ {
+ .type = "sil164",
+ .addr = (dcb->tmdsconf.slave_addr == 0x7 ? 0x3a : 0x38),
+ .platform_data = &(struct sil164_encoder_params) {
+ SIL164_INPUT_EDGE_RISING
+ }
+ },
+ { }
+ };
+ int type;
+
+ if (!nv_gf4_disp_arch(dev) || !i2c ||
+ get_tmds_slave(encoder))
+ return;
+
+ type = nouveau_i2c_identify(dev, "TMDS transmitter", info, 2);
+ if (type < 0)
+ return;
+
+ drm_i2c_encoder_init(dev, to_encoder_slave(encoder),
+ &i2c->adapter, &info[type]);
+}
+
static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = {
.dpms = nv04_lvds_dpms,
.save = nv04_dfp_save,
@@ -616,6 +685,10 @@ nv04_dfp_create(struct drm_connector *connector, struct dcb_entry *entry)
encoder->possible_crtcs = entry->heads;
encoder->possible_clones = 0;
+ if (entry->type == OUTPUT_TMDS &&
+ entry->location != DCB_LOC_ON_CHIP)
+ nv04_tmds_slave_init(encoder);
+
drm_mode_connector_attach_encoder(connector, encoder);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c
index 94e299cef0b2..0b5d012d7c28 100644
--- a/drivers/gpu/drm/nouveau/nv04_tv.c
+++ b/drivers/gpu/drm/nouveau/nv04_tv.c
@@ -89,7 +89,7 @@ static void nv04_tv_dpms(struct drm_encoder *encoder, int mode)
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel);
- to_encoder_slave(encoder)->slave_funcs->dpms(encoder, mode);
+ get_slave_funcs(encoder)->dpms(encoder, mode);
}
static void nv04_tv_bind(struct drm_device *dev, int head, bool bind)
@@ -152,7 +152,7 @@ static void nv04_tv_mode_set(struct drm_encoder *encoder,
regp->tv_vskew = 1;
regp->tv_vsync_delay = 1;
- to_encoder_slave(encoder)->slave_funcs->mode_set(encoder, mode, adjusted_mode);
+ get_slave_funcs(encoder)->mode_set(encoder, mode, adjusted_mode);
}
static void nv04_tv_commit(struct drm_encoder *encoder)
@@ -171,8 +171,7 @@ static void nv04_tv_commit(struct drm_encoder *encoder)
static void nv04_tv_destroy(struct drm_encoder *encoder)
{
- to_encoder_slave(encoder)->slave_funcs->destroy(encoder);
-
+ get_slave_funcs(encoder)->destroy(encoder);
drm_encoder_cleanup(encoder);
kfree(encoder->helper_private);
@@ -229,7 +228,7 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
goto fail_cleanup;
/* Fill the function pointers */
- sfuncs = to_encoder_slave(encoder)->slave_funcs;
+ sfuncs = get_slave_funcs(encoder);
*hfuncs = (struct drm_encoder_helper_funcs) {
.dpms = nv04_tv_dpms,
@@ -243,7 +242,6 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
};
/* Attach it to the specified connector. */
- sfuncs->set_config(encoder, nv04_tv_encoder_info[type].platform_data);
sfuncs->create_resources(encoder, connector);
drm_mode_connector_attach_encoder(connector, encoder);
diff --git a/drivers/gpu/drm/nouveau/nv10_graph.c b/drivers/gpu/drm/nouveau/nv10_graph.c
index fcf2cdd19493..b2f6a57c0cc5 100644
--- a/drivers/gpu/drm/nouveau/nv10_graph.c
+++ b/drivers/gpu/drm/nouveau/nv10_graph.c
@@ -43,51 +43,51 @@ struct pipe_state {
};
static int nv10_graph_ctx_regs[] = {
- NV10_PGRAPH_CTX_SWITCH1,
- NV10_PGRAPH_CTX_SWITCH2,
- NV10_PGRAPH_CTX_SWITCH3,
- NV10_PGRAPH_CTX_SWITCH4,
- NV10_PGRAPH_CTX_SWITCH5,
- NV10_PGRAPH_CTX_CACHE1, /* 8 values from 0x400160 to 0x40017c */
- NV10_PGRAPH_CTX_CACHE2, /* 8 values from 0x400180 to 0x40019c */
- NV10_PGRAPH_CTX_CACHE3, /* 8 values from 0x4001a0 to 0x4001bc */
- NV10_PGRAPH_CTX_CACHE4, /* 8 values from 0x4001c0 to 0x4001dc */
- NV10_PGRAPH_CTX_CACHE5, /* 8 values from 0x4001e0 to 0x4001fc */
- 0x00400164,
- 0x00400184,
- 0x004001a4,
- 0x004001c4,
- 0x004001e4,
- 0x00400168,
- 0x00400188,
- 0x004001a8,
- 0x004001c8,
- 0x004001e8,
- 0x0040016c,
- 0x0040018c,
- 0x004001ac,
- 0x004001cc,
- 0x004001ec,
- 0x00400170,
- 0x00400190,
- 0x004001b0,
- 0x004001d0,
- 0x004001f0,
- 0x00400174,
- 0x00400194,
- 0x004001b4,
- 0x004001d4,
- 0x004001f4,
- 0x00400178,
- 0x00400198,
- 0x004001b8,
- 0x004001d8,
- 0x004001f8,
- 0x0040017c,
- 0x0040019c,
- 0x004001bc,
- 0x004001dc,
- 0x004001fc,
+ NV10_PGRAPH_CTX_SWITCH(0),
+ NV10_PGRAPH_CTX_SWITCH(1),
+ NV10_PGRAPH_CTX_SWITCH(2),
+ NV10_PGRAPH_CTX_SWITCH(3),
+ NV10_PGRAPH_CTX_SWITCH(4),
+ NV10_PGRAPH_CTX_CACHE(0, 0),
+ NV10_PGRAPH_CTX_CACHE(0, 1),
+ NV10_PGRAPH_CTX_CACHE(0, 2),
+ NV10_PGRAPH_CTX_CACHE(0, 3),
+ NV10_PGRAPH_CTX_CACHE(0, 4),
+ NV10_PGRAPH_CTX_CACHE(1, 0),
+ NV10_PGRAPH_CTX_CACHE(1, 1),
+ NV10_PGRAPH_CTX_CACHE(1, 2),
+ NV10_PGRAPH_CTX_CACHE(1, 3),
+ NV10_PGRAPH_CTX_CACHE(1, 4),
+ NV10_PGRAPH_CTX_CACHE(2, 0),
+ NV10_PGRAPH_CTX_CACHE(2, 1),
+ NV10_PGRAPH_CTX_CACHE(2, 2),
+ NV10_PGRAPH_CTX_CACHE(2, 3),
+ NV10_PGRAPH_CTX_CACHE(2, 4),
+ NV10_PGRAPH_CTX_CACHE(3, 0),
+ NV10_PGRAPH_CTX_CACHE(3, 1),
+ NV10_PGRAPH_CTX_CACHE(3, 2),
+ NV10_PGRAPH_CTX_CACHE(3, 3),
+ NV10_PGRAPH_CTX_CACHE(3, 4),
+ NV10_PGRAPH_CTX_CACHE(4, 0),
+ NV10_PGRAPH_CTX_CACHE(4, 1),
+ NV10_PGRAPH_CTX_CACHE(4, 2),
+ NV10_PGRAPH_CTX_CACHE(4, 3),
+ NV10_PGRAPH_CTX_CACHE(4, 4),
+ NV10_PGRAPH_CTX_CACHE(5, 0),
+ NV10_PGRAPH_CTX_CACHE(5, 1),
+ NV10_PGRAPH_CTX_CACHE(5, 2),
+ NV10_PGRAPH_CTX_CACHE(5, 3),
+ NV10_PGRAPH_CTX_CACHE(5, 4),
+ NV10_PGRAPH_CTX_CACHE(6, 0),
+ NV10_PGRAPH_CTX_CACHE(6, 1),
+ NV10_PGRAPH_CTX_CACHE(6, 2),
+ NV10_PGRAPH_CTX_CACHE(6, 3),
+ NV10_PGRAPH_CTX_CACHE(6, 4),
+ NV10_PGRAPH_CTX_CACHE(7, 0),
+ NV10_PGRAPH_CTX_CACHE(7, 1),
+ NV10_PGRAPH_CTX_CACHE(7, 2),
+ NV10_PGRAPH_CTX_CACHE(7, 3),
+ NV10_PGRAPH_CTX_CACHE(7, 4),
NV10_PGRAPH_CTX_USER,
NV04_PGRAPH_DMA_START_0,
NV04_PGRAPH_DMA_START_1,
@@ -653,6 +653,78 @@ static int nv17_graph_ctx_regs_find_offset(struct drm_device *dev, int reg)
return -1;
}
+static void nv10_graph_load_dma_vtxbuf(struct nouveau_channel *chan,
+ uint32_t inst)
+{
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+ uint32_t st2, st2_dl, st2_dh, fifo_ptr, fifo[0x60/4];
+ uint32_t ctx_user, ctx_switch[5];
+ int i, subchan = -1;
+
+ /* NV10TCL_DMA_VTXBUF (method 0x18c) modifies hidden state
+ * that cannot be restored via MMIO. Do it through the FIFO
+ * instead.
+ */
+
+ /* Look for a celsius object */
+ for (i = 0; i < 8; i++) {
+ int class = nv_rd32(dev, NV10_PGRAPH_CTX_CACHE(i, 0)) & 0xfff;
+
+ if (class == 0x56 || class == 0x96 || class == 0x99) {
+ subchan = i;
+ break;
+ }
+ }
+
+ if (subchan < 0 || !inst)
+ return;
+
+ /* Save the current ctx object */
+ ctx_user = nv_rd32(dev, NV10_PGRAPH_CTX_USER);
+ for (i = 0; i < 5; i++)
+ ctx_switch[i] = nv_rd32(dev, NV10_PGRAPH_CTX_SWITCH(i));
+
+ /* Save the FIFO state */
+ st2 = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2);
+ st2_dl = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2_DL);
+ st2_dh = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2_DH);
+ fifo_ptr = nv_rd32(dev, NV10_PGRAPH_FFINTFC_FIFO_PTR);
+
+ for (i = 0; i < ARRAY_SIZE(fifo); i++)
+ fifo[i] = nv_rd32(dev, 0x4007a0 + 4 * i);
+
+ /* Switch to the celsius subchannel */
+ for (i = 0; i < 5; i++)
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(i),
+ nv_rd32(dev, NV10_PGRAPH_CTX_CACHE(subchan, i)));
+ nv_mask(dev, NV10_PGRAPH_CTX_USER, 0xe000, subchan << 13);
+
+ /* Inject NV10TCL_DMA_VTXBUF */
+ nv_wr32(dev, NV10_PGRAPH_FFINTFC_FIFO_PTR, 0);
+ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2,
+ 0x2c000000 | chan->id << 20 | subchan << 16 | 0x18c);
+ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DL, inst);
+ nv_mask(dev, NV10_PGRAPH_CTX_CONTROL, 0, 0x10000);
+ pgraph->fifo_access(dev, true);
+ pgraph->fifo_access(dev, false);
+
+ /* Restore the FIFO state */
+ for (i = 0; i < ARRAY_SIZE(fifo); i++)
+ nv_wr32(dev, 0x4007a0 + 4 * i, fifo[i]);
+
+ nv_wr32(dev, NV10_PGRAPH_FFINTFC_FIFO_PTR, fifo_ptr);
+ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2, st2);
+ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DL, st2_dl);
+ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DH, st2_dh);
+
+ /* Restore the current ctx object */
+ for (i = 0; i < 5; i++)
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(i), ctx_switch[i]);
+ nv_wr32(dev, NV10_PGRAPH_CTX_USER, ctx_user);
+}
+
int nv10_graph_load_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
@@ -670,6 +742,8 @@ int nv10_graph_load_context(struct nouveau_channel *chan)
}
nv10_graph_load_pipe(chan);
+ nv10_graph_load_dma_vtxbuf(chan, (nv_rd32(dev, NV10_PGRAPH_GLOBALSTATE1)
+ & 0xffff));
nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100);
tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER);
@@ -856,11 +930,12 @@ int nv10_graph_init(struct drm_device *dev)
for (i = 0; i < NV10_PFB_TILE__SIZE; i++)
nv10_graph_set_region_tiling(dev, i, 0, 0, 0);
- nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH1, 0x00000000);
- nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH2, 0x00000000);
- nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH3, 0x00000000);
- nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH4, 0x00000000);
- nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF);
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(0), 0x00000000);
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(1), 0x00000000);
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(2), 0x00000000);
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(3), 0x00000000);
+ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(4), 0x00000000);
+ nv_wr32(dev, NV10_PGRAPH_STATE, 0xFFFFFFFF);
tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
tmp |= (dev_priv->engine.fifo.channels - 1) << 24;
diff --git a/drivers/gpu/drm/nouveau/nv30_fb.c b/drivers/gpu/drm/nouveau/nv30_fb.c
index 9d35c8b3b839..4a3f2f095128 100644
--- a/drivers/gpu/drm/nouveau/nv30_fb.c
+++ b/drivers/gpu/drm/nouveau/nv30_fb.c
@@ -30,15 +30,25 @@
#include "nouveau_drm.h"
static int
-calc_ref(int b, int l, int i)
+calc_bias(struct drm_device *dev, int k, int i, int j)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int b = (dev_priv->chipset > 0x30 ?
+ nv_rd32(dev, 0x122c + 0x10 * k + 0x4 * j) >> (4 * (i ^ 1)) :
+ 0) & 0xf;
+
+ return 2 * (b & 0x8 ? b - 0x10 : b);
+}
+
+static int
+calc_ref(struct drm_device *dev, int l, int k, int i)
{
int j, x = 0;
for (j = 0; j < 4; j++) {
- int n = (b >> (8 * j) & 0xf);
- int m = (l >> (8 * i) & 0xff) + 2 * (n & 0x8 ? n - 0x10 : n);
+ int m = (l >> (8 * i) & 0xff) + calc_bias(dev, k, i, j);
- x |= (0x80 | (m & 0x1f)) << (8 * j);
+ x |= (0x80 | clamp(m, 0, 0x1f)) << (8 * j);
}
return x;
@@ -63,18 +73,16 @@ nv30_fb_init(struct drm_device *dev)
dev_priv->chipset == 0x35) {
/* Related to ROP count */
int n = (dev_priv->chipset == 0x31 ? 2 : 4);
- int b = (dev_priv->chipset > 0x30 ?
- nv_rd32(dev, 0x122c) & 0xf : 0);
int l = nv_rd32(dev, 0x1003d0);
for (i = 0; i < n; i++) {
for (j = 0; j < 3; j++)
nv_wr32(dev, 0x10037c + 0xc * i + 0x4 * j,
- calc_ref(b, l, j));
+ calc_ref(dev, l, 0, j));
for (j = 0; j < 2; j++)
nv_wr32(dev, 0x1003ac + 0x8 * i + 0x4 * j,
- calc_ref(b, l, j));
+ calc_ref(dev, l, 1, j));
}
}
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
index 5d11ea101666..bfd4ca2fe7ef 100644
--- a/drivers/gpu/drm/nouveau/nv50_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
@@ -264,11 +264,16 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update)
int
nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
{
- uint32_t reg = NV50_PDISPLAY_CRTC_CLK_CTRL1(head);
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
struct pll_lims pll;
- uint32_t reg1, reg2;
+ uint32_t reg, reg1, reg2;
int ret, N1, M1, N2, M2, P;
+ if (dev_priv->chipset < NV_C0)
+ reg = NV50_PDISPLAY_CRTC_CLK_CTRL1(head);
+ else
+ reg = 0x614140 + (head * 0x800);
+
ret = get_pll_limits(dev, reg, &pll);
if (ret)
return ret;
@@ -286,7 +291,8 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
nv_wr32(dev, reg, 0x10000611);
nv_wr32(dev, reg + 4, reg1 | (M1 << 16) | N1);
nv_wr32(dev, reg + 8, reg2 | (P << 28) | (M2 << 16) | N2);
- } else {
+ } else
+ if (dev_priv->chipset < NV_C0) {
ret = nv50_calc_pll2(dev, &pll, pclk, &N1, &N2, &M1, &P);
if (ret <= 0)
return 0;
@@ -298,6 +304,17 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
nv_wr32(dev, reg, 0x50000610);
nv_wr32(dev, reg + 4, reg1 | (P << 16) | (M1 << 8) | N1);
nv_wr32(dev, reg + 8, N2);
+ } else {
+ ret = nv50_calc_pll2(dev, &pll, pclk, &N1, &N2, &M1, &P);
+ if (ret <= 0)
+ return 0;
+
+ NV_DEBUG(dev, "pclk %d out %d N %d fN 0x%04x M %d P %d\n",
+ pclk, ret, N1, N2, M1, P);
+
+ nv_mask(dev, reg + 0x0c, 0x00000000, 0x00000100);
+ nv_wr32(dev, reg + 0x04, (P << 16) | (N1 << 8) | M1);
+ nv_wr32(dev, reg + 0x10, N2 << 16);
}
return 0;
@@ -348,7 +365,7 @@ nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
gem = drm_gem_object_lookup(dev, file_priv, buffer_handle);
if (!gem)
- return -EINVAL;
+ return -ENOENT;
cursor = nouveau_gem_object(gem);
ret = nouveau_bo_map(cursor);
@@ -381,15 +398,12 @@ nv50_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
static void
nv50_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
- uint32_t size)
+ uint32_t start, uint32_t size)
{
+ int end = (start + size > 256) ? 256 : start + size, i;
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
- int i;
-
- if (size != 256)
- return;
- for (i = 0; i < 256; i++) {
+ for (i = start; i < end; i++) {
nv_crtc->lut.r[i] = r[i];
nv_crtc->lut.g[i] = g[i];
nv_crtc->lut.b[i] = b[i];
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index f13ad0de9c8f..612fa6d6a0cb 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -76,7 +76,10 @@ nv50_evo_dmaobj_new(struct nouveau_channel *evo, uint32_t class, uint32_t name,
nv_wo32(dev, obj, 2, offset);
nv_wo32(dev, obj, 3, 0x00000000);
nv_wo32(dev, obj, 4, 0x00000000);
- nv_wo32(dev, obj, 5, 0x00010000);
+ if (dev_priv->card_type < NV_C0)
+ nv_wo32(dev, obj, 5, 0x00010000);
+ else
+ nv_wo32(dev, obj, 5, 0x00020000);
dev_priv->engine.instmem.flush(dev);
return 0;
diff --git a/drivers/gpu/drm/nouveau/nvc0_fb.c b/drivers/gpu/drm/nouveau/nvc0_fb.c
new file mode 100644
index 000000000000..26a996025dd2
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_fb.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+
+#include "nouveau_drv.h"
+
+int
+nvc0_fb_init(struct drm_device *dev)
+{
+ return 0;
+}
+
+void
+nvc0_fb_takedown(struct drm_device *dev)
+{
+}
diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c
new file mode 100644
index 000000000000..d64375871979
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+
+#include "nouveau_drv.h"
+
+void
+nvc0_fifo_disable(struct drm_device *dev)
+{
+}
+
+void
+nvc0_fifo_enable(struct drm_device *dev)
+{
+}
+
+bool
+nvc0_fifo_reassign(struct drm_device *dev, bool enable)
+{
+ return false;
+}
+
+bool
+nvc0_fifo_cache_flush(struct drm_device *dev)
+{
+ return true;
+}
+
+bool
+nvc0_fifo_cache_pull(struct drm_device *dev, bool enable)
+{
+ return false;
+}
+
+int
+nvc0_fifo_channel_id(struct drm_device *dev)
+{
+ return 127;
+}
+
+int
+nvc0_fifo_create_context(struct nouveau_channel *chan)
+{
+ return 0;
+}
+
+void
+nvc0_fifo_destroy_context(struct nouveau_channel *chan)
+{
+}
+
+int
+nvc0_fifo_load_context(struct nouveau_channel *chan)
+{
+ return 0;
+}
+
+int
+nvc0_fifo_unload_context(struct drm_device *dev)
+{
+ return 0;
+}
+
+void
+nvc0_fifo_takedown(struct drm_device *dev)
+{
+}
+
+int
+nvc0_fifo_init(struct drm_device *dev)
+{
+ return 0;
+}
+
diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c
new file mode 100644
index 000000000000..717a5177a8d8
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_graph.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+
+#include "nouveau_drv.h"
+
+void
+nvc0_graph_fifo_access(struct drm_device *dev, bool enabled)
+{
+}
+
+struct nouveau_channel *
+nvc0_graph_channel(struct drm_device *dev)
+{
+ return NULL;
+}
+
+int
+nvc0_graph_create_context(struct nouveau_channel *chan)
+{
+ return 0;
+}
+
+void
+nvc0_graph_destroy_context(struct nouveau_channel *chan)
+{
+}
+
+int
+nvc0_graph_load_context(struct nouveau_channel *chan)
+{
+ return 0;
+}
+
+int
+nvc0_graph_unload_context(struct drm_device *dev)
+{
+ return 0;
+}
+
+void
+nvc0_graph_takedown(struct drm_device *dev)
+{
+}
+
+int
+nvc0_graph_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ dev_priv->engine.graph.accel_blocked = true;
+ return 0;
+}
+
diff --git a/drivers/gpu/drm/nouveau/nvc0_instmem.c b/drivers/gpu/drm/nouveau/nvc0_instmem.c
new file mode 100644
index 000000000000..3ab3cdc42173
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_instmem.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+
+#include "nouveau_drv.h"
+
+int
+nvc0_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,
+ uint32_t *size)
+{
+ int ret;
+
+ *size = ALIGN(*size, 4096);
+ if (*size == 0)
+ return -EINVAL;
+
+ ret = nouveau_bo_new(dev, NULL, *size, 0, TTM_PL_FLAG_VRAM, 0, 0x0000,
+ true, false, &gpuobj->im_backing);
+ if (ret) {
+ NV_ERROR(dev, "error getting PRAMIN backing pages: %d\n", ret);
+ return ret;
+ }
+
+ ret = nouveau_bo_pin(gpuobj->im_backing, TTM_PL_FLAG_VRAM);
+ if (ret) {
+ NV_ERROR(dev, "error pinning PRAMIN backing VRAM: %d\n", ret);
+ nouveau_bo_ref(NULL, &gpuobj->im_backing);
+ return ret;
+ }
+
+ gpuobj->im_backing_start = gpuobj->im_backing->bo.mem.mm_node->start;
+ gpuobj->im_backing_start <<= PAGE_SHIFT;
+ return 0;
+}
+
+void
+nvc0_instmem_clear(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ if (gpuobj && gpuobj->im_backing) {
+ if (gpuobj->im_bound)
+ dev_priv->engine.instmem.unbind(dev, gpuobj);
+ nouveau_bo_unpin(gpuobj->im_backing);
+ nouveau_bo_ref(NULL, &gpuobj->im_backing);
+ gpuobj->im_backing = NULL;
+ }
+}
+
+int
+nvc0_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t pte, pte_end;
+ uint64_t vram;
+
+ if (!gpuobj->im_backing || !gpuobj->im_pramin || gpuobj->im_bound)
+ return -EINVAL;
+
+ NV_DEBUG(dev, "st=0x%lx sz=0x%lx\n",
+ gpuobj->im_pramin->start, gpuobj->im_pramin->size);
+
+ pte = gpuobj->im_pramin->start >> 12;
+ pte_end = (gpuobj->im_pramin->size >> 12) + pte;
+ vram = gpuobj->im_backing_start;
+
+ NV_DEBUG(dev, "pramin=0x%lx, pte=%d, pte_end=%d\n",
+ gpuobj->im_pramin->start, pte, pte_end);
+ NV_DEBUG(dev, "first vram page: 0x%08x\n", gpuobj->im_backing_start);
+
+ while (pte < pte_end) {
+ nv_wr32(dev, 0x702000 + (pte * 8), (vram >> 8) | 1);
+ nv_wr32(dev, 0x702004 + (pte * 8), 0);
+ vram += 4096;
+ pte++;
+ }
+ dev_priv->engine.instmem.flush(dev);
+
+ if (1) {
+ u32 chan = nv_rd32(dev, 0x1700) << 16;
+ nv_wr32(dev, 0x100cb8, (chan + 0x1000) >> 8);
+ nv_wr32(dev, 0x100cbc, 0x80000005);
+ }
+
+ gpuobj->im_bound = 1;
+ return 0;
+}
+
+int
+nvc0_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t pte, pte_end;
+
+ if (gpuobj->im_bound == 0)
+ return -EINVAL;
+
+ pte = gpuobj->im_pramin->start >> 12;
+ pte_end = (gpuobj->im_pramin->size >> 12) + pte;
+ while (pte < pte_end) {
+ nv_wr32(dev, 0x702000 + (pte * 8), 0);
+ nv_wr32(dev, 0x702004 + (pte * 8), 0);
+ pte++;
+ }
+ dev_priv->engine.instmem.flush(dev);
+
+ gpuobj->im_bound = 0;
+ return 0;
+}
+
+void
+nvc0_instmem_flush(struct drm_device *dev)
+{
+ nv_wr32(dev, 0x070000, 1);
+ if (!nv_wait(0x070000, 0x00000002, 0x00000000))
+ NV_ERROR(dev, "PRAMIN flush timeout\n");
+}
+
+int
+nvc0_instmem_suspend(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int i;
+
+ dev_priv->susres.ramin_copy = vmalloc(65536);
+ if (!dev_priv->susres.ramin_copy)
+ return -ENOMEM;
+
+ for (i = 0x700000; i < 0x710000; i += 4)
+ dev_priv->susres.ramin_copy[i/4] = nv_rd32(dev, i);
+ return 0;
+}
+
+void
+nvc0_instmem_resume(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u64 chan;
+ int i;
+
+ chan = dev_priv->vram_size - dev_priv->ramin_rsvd_vram;
+ nv_wr32(dev, 0x001700, chan >> 16);
+
+ for (i = 0x700000; i < 0x710000; i += 4)
+ nv_wr32(dev, i, dev_priv->susres.ramin_copy[i/4]);
+ vfree(dev_priv->susres.ramin_copy);
+ dev_priv->susres.ramin_copy = NULL;
+
+ nv_wr32(dev, 0x001714, 0xc0000000 | (chan >> 12));
+}
+
+int
+nvc0_instmem_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u64 chan, pgt3, imem, lim3 = dev_priv->ramin_size - 1;
+ int ret, i;
+
+ dev_priv->ramin_rsvd_vram = 1 * 1024 * 1024;
+ chan = dev_priv->vram_size - dev_priv->ramin_rsvd_vram;
+ imem = 4096 + 4096 + 32768;
+
+ nv_wr32(dev, 0x001700, chan >> 16);
+
+ /* channel setup */
+ nv_wr32(dev, 0x700200, lower_32_bits(chan + 0x1000));
+ nv_wr32(dev, 0x700204, upper_32_bits(chan + 0x1000));
+ nv_wr32(dev, 0x700208, lower_32_bits(lim3));
+ nv_wr32(dev, 0x70020c, upper_32_bits(lim3));
+
+ /* point pgd -> pgt */
+ nv_wr32(dev, 0x701000, 0);
+ nv_wr32(dev, 0x701004, ((chan + 0x2000) >> 8) | 1);
+
+ /* point pgt -> physical vram for channel */
+ pgt3 = 0x2000;
+ for (i = 0; i < dev_priv->ramin_rsvd_vram; i += 4096, pgt3 += 8) {
+ nv_wr32(dev, 0x700000 + pgt3, ((chan + i) >> 8) | 1);
+ nv_wr32(dev, 0x700004 + pgt3, 0);
+ }
+
+ /* clear rest of pgt */
+ for (; i < dev_priv->ramin_size; i += 4096, pgt3 += 8) {
+ nv_wr32(dev, 0x700000 + pgt3, 0);
+ nv_wr32(dev, 0x700004 + pgt3, 0);
+ }
+
+ /* point bar3 at the channel */
+ nv_wr32(dev, 0x001714, 0xc0000000 | (chan >> 12));
+
+ /* Global PRAMIN heap */
+ ret = drm_mm_init(&dev_priv->ramin_heap, imem,
+ dev_priv->ramin_size - imem);
+ if (ret) {
+ NV_ERROR(dev, "Failed to init RAMIN heap\n");
+ return -ENOMEM;
+ }
+
+ /*XXX: incorrect, but needed to make hash func "work" */
+ dev_priv->ramht_offset = 0x10000;
+ dev_priv->ramht_bits = 9;
+ dev_priv->ramht_size = (1 << dev_priv->ramht_bits);
+ return 0;
+}
+
+void
+nvc0_instmem_takedown(struct drm_device *dev)
+{
+}
+
diff --git a/drivers/gpu/drm/radeon/r600_cp.c b/drivers/gpu/drm/radeon/r600_cp.c
index 68e6f4349309..4f4cd8b286d5 100644
--- a/drivers/gpu/drm/radeon/r600_cp.c
+++ b/drivers/gpu/drm/radeon/r600_cp.c
@@ -200,7 +200,7 @@ int r600_page_table_init(struct drm_device *dev)
entry->pagelist[i], 0,
PAGE_SIZE,
PCI_DMA_BIDIRECTIONAL);
- if (entry->busaddr[i] == 0) {
+ if (pci_dma_mapping_error(dev->pdev, entry->busaddr[i])) {
DRM_ERROR("unable to map PCIGART pages!\n");
r600_page_table_cleanup(dev, gart_info);
goto done;
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
index c3ea212e0c3c..d8864949e387 100644
--- a/drivers/gpu/drm/radeon/r600_cs.c
+++ b/drivers/gpu/drm/radeon/r600_cs.c
@@ -133,6 +133,7 @@ static inline int r600_bpe_from_format(u32 *bpe, u32 format)
case V_038004_FMT_GB_GR:
case V_038004_FMT_BG_RG:
case V_038004_COLOR_INVALID:
+ default:
*bpe = 16;
return -EINVAL;
}
@@ -174,7 +175,7 @@ static inline int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
dev_warn(p->dev, "FMASK or CMASK buffer are not supported by this kernel\n");
return -EINVAL;
}
- size = radeon_bo_size(track->cb_color_bo[i]);
+ size = radeon_bo_size(track->cb_color_bo[i]) - track->cb_color_bo_offset[i];
if (r600_bpe_from_format(&bpe, G_0280A0_FORMAT(track->cb_color_info[i]))) {
dev_warn(p->dev, "%s:%d cb invalid format %d for %d (0x%08X)\n",
__func__, __LINE__, G_0280A0_FORMAT(track->cb_color_info[i]),
@@ -327,7 +328,6 @@ static int r600_cs_track_check(struct radeon_cs_parser *p)
dev_warn(p->dev, "z/stencil buffer size not set\n");
return -EINVAL;
}
- printk_once(KERN_WARNING "You have old & broken userspace please consider updating mesa\n");
tmp = radeon_bo_size(track->db_bo) - track->db_offset;
tmp = (tmp / bpe) >> 6;
if (!tmp) {
@@ -882,8 +882,6 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx
return -EINVAL;
}
ib[idx] = track->cb_color_base_last[tmp];
- printk_once(KERN_WARNING "You have old & broken userspace "
- "please consider updating mesa & xf86-video-ati\n");
track->cb_color_frag_bo[tmp] = track->cb_color_bo[tmp];
} else {
r = r600_cs_packet_next_reloc(p, &reloc);
@@ -910,8 +908,6 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx
return -EINVAL;
}
ib[idx] = track->cb_color_base_last[tmp];
- printk_once(KERN_WARNING "You have old & broken userspace "
- "please consider updating mesa & xf86-video-ati\n");
track->cb_color_tile_bo[tmp] = track->cb_color_bo[tmp];
} else {
r = r600_cs_packet_next_reloc(p, &reloc);
@@ -938,7 +934,7 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx
return -EINVAL;
}
tmp = (reg - CB_COLOR0_BASE) / 4;
- track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx);
+ track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx) << 8;
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->cb_color_base_last[tmp] = ib[idx];
track->cb_color_bo[tmp] = reloc->robj;
@@ -950,7 +946,7 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx
"0x%04X\n", reg);
return -EINVAL;
}
- track->db_offset = radeon_get_ib_value(p, idx);
+ track->db_offset = radeon_get_ib_value(p, idx) << 8;
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->db_bo = reloc->robj;
break;
@@ -1055,10 +1051,10 @@ static void r600_texture_size(unsigned nfaces, unsigned blevel, unsigned nlevels
}
*l0_size = ALIGN((w0 * bpe), pitch_align) * h0 * d0;
*mipmap_size = offset;
- if (!blevel)
- *mipmap_size -= *l0_size;
if (!nlevels)
*mipmap_size = *l0_size;
+ if (!blevel)
+ *mipmap_size -= *l0_size;
}
/**
@@ -1165,14 +1161,14 @@ static inline int r600_check_texture_resource(struct radeon_cs_parser *p, u32 i
(pitch_align * bpe),
&l0_size, &mipmap_size);
/* using get ib will give us the offset into the texture bo */
- word0 = radeon_get_ib_value(p, idx + 2);
+ word0 = radeon_get_ib_value(p, idx + 2) << 8;
if ((l0_size + word0) > radeon_bo_size(texture)) {
dev_warn(p->dev, "texture bo too small (%d %d %d %d -> %d have %ld)\n",
w0, h0, bpe, word0, l0_size, radeon_bo_size(texture));
return -EINVAL;
}
/* using get ib will give us the offset into the mipmap bo */
- word0 = radeon_get_ib_value(p, idx + 3);
+ word0 = radeon_get_ib_value(p, idx + 3) << 8;
if ((mipmap_size + word0) > radeon_bo_size(mipmap)) {
dev_warn(p->dev, "mipmap bo too small (%d %d %d %d %d %d -> %d have %ld)\n",
w0, h0, bpe, blevel, nlevels, word0, mipmap_size, radeon_bo_size(texture));
@@ -1366,7 +1362,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
}
for (i = 0; i < (pkt->count / 7); i++) {
struct radeon_bo *texture, *mipmap;
- u32 size, offset;
+ u32 size, offset, base_offset, mip_offset;
switch (G__SQ_VTX_CONSTANT_TYPE(radeon_get_ib_value(p, idx+(i*7)+6+1))) {
case SQ_TEX_VTX_VALID_TEXTURE:
@@ -1376,7 +1372,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad SET_RESOURCE\n");
return -EINVAL;
}
- ib[idx+1+(i*7)+2] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ base_offset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_2D_TILED_THIN1);
else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
@@ -1388,12 +1384,14 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad SET_RESOURCE\n");
return -EINVAL;
}
- ib[idx+1+(i*7)+3] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ mip_offset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
mipmap = reloc->robj;
r = r600_check_texture_resource(p, idx+(i*7)+1,
texture, mipmap, reloc->lobj.tiling_flags);
if (r)
return r;
+ ib[idx+1+(i*7)+2] += base_offset;
+ ib[idx+1+(i*7)+3] += mip_offset;
break;
case SQ_TEX_VTX_VALID_BUFFER:
/* vtx base */
@@ -1403,10 +1401,11 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
offset = radeon_get_ib_value(p, idx+1+(i*7)+0);
- size = radeon_get_ib_value(p, idx+1+(i*7)+1);
+ size = radeon_get_ib_value(p, idx+1+(i*7)+1) + 1;
if (p->rdev && (size + offset) > radeon_bo_size(reloc->robj)) {
/* force size to size of the buffer */
- dev_warn(p->dev, "vbo resource seems too big for the bo\n");
+ dev_warn(p->dev, "vbo resource seems too big (%d) for the bo (%ld)\n",
+ size + offset, radeon_bo_size(reloc->robj));
ib[idx+1+(i*7)+1] = radeon_bo_size(reloc->robj);
}
ib[idx+1+(i*7)+0] += (u32)((reloc->lobj.gpu_offset) & 0xffffffff);
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 3cd1c470b777..3dfcfa3ca425 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -1100,6 +1100,8 @@ struct radeon_device {
struct notifier_block acpi_nb;
/* only one userspace can use Hyperz features at a time */
struct drm_file *hyperz_filp;
+ /* i2c buses */
+ struct radeon_i2c_chan *i2c_bus[RADEON_MAX_I2C_BUS];
};
int radeon_device_init(struct radeon_device *rdev,
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index 3bc2bcdf5308..6d30868744ee 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -48,7 +48,8 @@ radeon_add_atom_connector(struct drm_device *dev,
struct radeon_i2c_bus_rec *i2c_bus,
bool linkb, uint32_t igp_lane_info,
uint16_t connector_object_id,
- struct radeon_hpd *hpd);
+ struct radeon_hpd *hpd,
+ struct radeon_router *router);
/* from radeon_legacy_encoder.c */
extern void
@@ -114,7 +115,8 @@ static inline struct radeon_i2c_bus_rec radeon_lookup_i2c_gpio(struct radeon_dev
i2c.i2c_id = gpio->sucI2cId.ucAccess;
- i2c.valid = true;
+ if (i2c.mask_clk_reg)
+ i2c.valid = true;
break;
}
}
@@ -123,6 +125,66 @@ static inline struct radeon_i2c_bus_rec radeon_lookup_i2c_gpio(struct radeon_dev
return i2c;
}
+void radeon_atombios_i2c_init(struct radeon_device *rdev)
+{
+ struct atom_context *ctx = rdev->mode_info.atom_context;
+ ATOM_GPIO_I2C_ASSIGMENT *gpio;
+ struct radeon_i2c_bus_rec i2c;
+ int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info);
+ struct _ATOM_GPIO_I2C_INFO *i2c_info;
+ uint16_t data_offset, size;
+ int i, num_indices;
+ char stmp[32];
+
+ memset(&i2c, 0, sizeof(struct radeon_i2c_bus_rec));
+
+ if (atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) {
+ i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset);
+
+ num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
+ sizeof(ATOM_GPIO_I2C_ASSIGMENT);
+
+ for (i = 0; i < num_indices; i++) {
+ gpio = &i2c_info->asGPIO_Info[i];
+ i2c.valid = false;
+ i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex) * 4;
+ i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex) * 4;
+ i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex) * 4;
+ i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex) * 4;
+ i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex) * 4;
+ i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex) * 4;
+ i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex) * 4;
+ i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex) * 4;
+ i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift);
+ i2c.mask_data_mask = (1 << gpio->ucDataMaskShift);
+ i2c.en_clk_mask = (1 << gpio->ucClkEnShift);
+ i2c.en_data_mask = (1 << gpio->ucDataEnShift);
+ i2c.y_clk_mask = (1 << gpio->ucClkY_Shift);
+ i2c.y_data_mask = (1 << gpio->ucDataY_Shift);
+ i2c.a_clk_mask = (1 << gpio->ucClkA_Shift);
+ i2c.a_data_mask = (1 << gpio->ucDataA_Shift);
+
+ if (gpio->sucI2cId.sbfAccess.bfHW_Capable)
+ i2c.hw_capable = true;
+ else
+ i2c.hw_capable = false;
+
+ if (gpio->sucI2cId.ucAccess == 0xa0)
+ i2c.mm_i2c = true;
+ else
+ i2c.mm_i2c = false;
+
+ i2c.i2c_id = gpio->sucI2cId.ucAccess;
+
+ if (i2c.mask_clk_reg) {
+ i2c.valid = true;
+ sprintf(stmp, "0x%x", i2c.i2c_id);
+ rdev->i2c_bus[i] = radeon_i2c_create(rdev->ddev, &i2c, stmp);
+ }
+ }
+ }
+}
+
static inline struct radeon_gpio_rec radeon_lookup_gpio(struct radeon_device *rdev,
u8 id)
{
@@ -206,6 +268,7 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
uint16_t *line_mux,
struct radeon_hpd *hpd)
{
+ struct radeon_device *rdev = dev->dev_private;
/* Asus M2A-VM HDMI board lists the DVI port as HDMI */
if ((dev->pdev->device == 0x791e) &&
@@ -308,13 +371,22 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
}
}
- /* Acer laptop reports DVI-D as DVI-I */
+ /* Acer laptop reports DVI-D as DVI-I and hpd pins reversed */
if ((dev->pdev->device == 0x95c4) &&
(dev->pdev->subsystem_vendor == 0x1025) &&
(dev->pdev->subsystem_device == 0x013c)) {
+ struct radeon_gpio_rec gpio;
+
if ((*connector_type == DRM_MODE_CONNECTOR_DVII) &&
- (supported_device == ATOM_DEVICE_DFP1_SUPPORT))
+ (supported_device == ATOM_DEVICE_DFP1_SUPPORT)) {
+ gpio = radeon_lookup_gpio(rdev, 6);
+ *hpd = radeon_atom_get_hpd_info_from_gpio(rdev, &gpio);
*connector_type = DRM_MODE_CONNECTOR_DVID;
+ } else if ((*connector_type == DRM_MODE_CONNECTOR_HDMIA) &&
+ (supported_device == ATOM_DEVICE_DFP1_SUPPORT)) {
+ gpio = radeon_lookup_gpio(rdev, 7);
+ *hpd = radeon_atom_get_hpd_info_from_gpio(rdev, &gpio);
+ }
}
/* XFX Pine Group device rv730 reports no VGA DDC lines
@@ -399,13 +471,15 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
u16 size, data_offset;
u8 frev, crev;
ATOM_CONNECTOR_OBJECT_TABLE *con_obj;
+ ATOM_OBJECT_TABLE *router_obj;
ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj;
ATOM_OBJECT_HEADER *obj_header;
- int i, j, path_size, device_support;
+ int i, j, k, path_size, device_support;
int connector_type;
u16 igp_lane_info, conn_id, connector_object_id;
bool linkb;
struct radeon_i2c_bus_rec ddc_bus;
+ struct radeon_router router;
struct radeon_gpio_rec gpio;
struct radeon_hpd hpd;
@@ -415,6 +489,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
if (crev < 2)
return false;
+ router.valid = false;
+
obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset);
path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *)
(ctx->bios + data_offset +
@@ -422,6 +498,9 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
con_obj = (ATOM_CONNECTOR_OBJECT_TABLE *)
(ctx->bios + data_offset +
le16_to_cpu(obj_header->usConnectorObjectTableOffset));
+ router_obj = (ATOM_OBJECT_TABLE *)
+ (ctx->bios + data_offset +
+ le16_to_cpu(obj_header->usRouterObjectTableOffset));
device_support = le16_to_cpu(obj_header->usDeviceSupport);
path_size = 0;
@@ -508,33 +587,86 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
if (connector_type == DRM_MODE_CONNECTOR_Unknown)
continue;
- for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2);
- j++) {
- uint8_t enc_obj_id, enc_obj_num, enc_obj_type;
+ for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); j++) {
+ uint8_t grph_obj_id, grph_obj_num, grph_obj_type;
- enc_obj_id =
+ grph_obj_id =
(le16_to_cpu(path->usGraphicObjIds[j]) &
OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
- enc_obj_num =
+ grph_obj_num =
(le16_to_cpu(path->usGraphicObjIds[j]) &
ENUM_ID_MASK) >> ENUM_ID_SHIFT;
- enc_obj_type =
+ grph_obj_type =
(le16_to_cpu(path->usGraphicObjIds[j]) &
OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT;
- /* FIXME: add support for router objects */
- if (enc_obj_type == GRAPH_OBJECT_TYPE_ENCODER) {
- if (enc_obj_num == 2)
+ if (grph_obj_type == GRAPH_OBJECT_TYPE_ENCODER) {
+ if (grph_obj_num == 2)
linkb = true;
else
linkb = false;
radeon_add_atom_encoder(dev,
- enc_obj_id,
+ grph_obj_id,
le16_to_cpu
(path->
usDeviceTag));
+ } else if (grph_obj_type == GRAPH_OBJECT_TYPE_ROUTER) {
+ router.valid = false;
+ for (k = 0; k < router_obj->ucNumberOfObjects; k++) {
+ u16 router_obj_id = le16_to_cpu(router_obj->asObjects[j].usObjectID);
+ if (le16_to_cpu(path->usGraphicObjIds[j]) == router_obj_id) {
+ ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *)
+ (ctx->bios + data_offset +
+ le16_to_cpu(router_obj->asObjects[k].usRecordOffset));
+ ATOM_I2C_RECORD *i2c_record;
+ ATOM_I2C_ID_CONFIG_ACCESS *i2c_config;
+ ATOM_ROUTER_DDC_PATH_SELECT_RECORD *ddc_path;
+ ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *router_src_dst_table =
+ (ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *)
+ (ctx->bios + data_offset +
+ le16_to_cpu(router_obj->asObjects[k].usSrcDstTableOffset));
+ int enum_id;
+
+ router.router_id = router_obj_id;
+ for (enum_id = 0; enum_id < router_src_dst_table->ucNumberOfDst;
+ enum_id++) {
+ if (le16_to_cpu(path->usConnObjectId) ==
+ le16_to_cpu(router_src_dst_table->usDstObjectID[enum_id]))
+ break;
+ }
+
+ while (record->ucRecordType > 0 &&
+ record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) {
+ switch (record->ucRecordType) {
+ case ATOM_I2C_RECORD_TYPE:
+ i2c_record =
+ (ATOM_I2C_RECORD *)
+ record;
+ i2c_config =
+ (ATOM_I2C_ID_CONFIG_ACCESS *)
+ &i2c_record->sucI2cId;
+ router.i2c_info =
+ radeon_lookup_i2c_gpio(rdev,
+ i2c_config->
+ ucAccess);
+ router.i2c_addr = i2c_record->ucI2CAddr >> 1;
+ break;
+ case ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE:
+ ddc_path = (ATOM_ROUTER_DDC_PATH_SELECT_RECORD *)
+ record;
+ router.valid = true;
+ router.mux_type = ddc_path->ucMuxType;
+ router.mux_control_pin = ddc_path->ucMuxControlPin;
+ router.mux_state = ddc_path->ucMuxState[enum_id];
+ break;
+ }
+ record = (ATOM_COMMON_RECORD_HEADER *)
+ ((char *)record + record->ucRecordSize);
+ }
+ }
+ }
}
}
@@ -614,7 +746,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
connector_type, &ddc_bus,
linkb, igp_lane_info,
connector_object_id,
- &hpd);
+ &hpd,
+ &router);
}
}
@@ -691,6 +824,9 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
int i, j, max_device;
struct bios_connector *bios_connectors;
size_t bc_size = sizeof(*bios_connectors) * ATOM_MAX_SUPPORTED_DEVICE;
+ struct radeon_router router;
+
+ router.valid = false;
bios_connectors = kzalloc(bc_size, GFP_KERNEL);
if (!bios_connectors)
@@ -862,7 +998,8 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
&bios_connectors[i].ddc_bus,
false, 0,
connector_object_id,
- &bios_connectors[i].hpd);
+ &bios_connectors[i].hpd,
+ &router);
}
}
@@ -1521,7 +1658,7 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev)
thermal_controller_names[power_info->info.ucOverdriveThermalController],
power_info->info.ucOverdriveControllerAddress >> 1);
i2c_bus = radeon_lookup_i2c_gpio(rdev, power_info->info.ucOverdriveI2cLine);
- rdev->pm.i2c_bus = radeon_i2c_create(rdev->ddev, &i2c_bus, "Thermal");
+ rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus);
if (rdev->pm.i2c_bus) {
struct i2c_board_info info = { };
const char *name = thermal_controller_names[power_info->info.
@@ -1814,7 +1951,7 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev)
(controller->ucFanParameters &
ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with");
i2c_bus = radeon_lookup_i2c_gpio(rdev, controller->ucI2cLine);
- rdev->pm.i2c_bus = radeon_i2c_create(rdev->ddev, &i2c_bus, "Thermal");
+ rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus);
if (rdev->pm.i2c_bus) {
struct i2c_board_info info = { };
const char *name = pp_lib_thermal_controller_names[controller->ucType];
@@ -1927,6 +2064,11 @@ void radeon_atombios_get_power_modes(struct radeon_device *rdev)
rdev->pm.power_state[state_index].type =
POWER_STATE_TYPE_PERFORMANCE;
break;
+ case ATOM_PPLIB_CLASSIFICATION_UI_NONE:
+ if (misc2 & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE)
+ rdev->pm.power_state[state_index].type =
+ POWER_STATE_TYPE_PERFORMANCE;
+ break;
}
rdev->pm.power_state[state_index].flags = 0;
if (misc & ATOM_PPLIB_SINGLE_DISPLAY_ONLY)
diff --git a/drivers/gpu/drm/radeon/radeon_clocks.c b/drivers/gpu/drm/radeon/radeon_clocks.c
index f64936cc4dd9..14448a740ba6 100644
--- a/drivers/gpu/drm/radeon/radeon_clocks.c
+++ b/drivers/gpu/drm/radeon/radeon_clocks.c
@@ -91,6 +91,85 @@ uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev)
return mclk;
}
+#ifdef CONFIG_OF
+/*
+ * Read XTAL (ref clock), SCLK and MCLK from Open Firmware device
+ * tree. Hopefully, ATI OF driver is kind enough to fill these
+ */
+static bool __devinit radeon_read_clocks_OF(struct drm_device *dev)
+{
+ struct radeon_device *rdev = dev->dev_private;
+ struct device_node *dp = rdev->pdev->dev.of_node;
+ const u32 *val;
+ struct radeon_pll *p1pll = &rdev->clock.p1pll;
+ struct radeon_pll *p2pll = &rdev->clock.p2pll;
+ struct radeon_pll *spll = &rdev->clock.spll;
+ struct radeon_pll *mpll = &rdev->clock.mpll;
+
+ if (dp == NULL)
+ return false;
+ val = of_get_property(dp, "ATY,RefCLK", NULL);
+ if (!val || !*val) {
+ printk(KERN_WARNING "radeonfb: No ATY,RefCLK property !\n");
+ return false;
+ }
+ p1pll->reference_freq = p2pll->reference_freq = (*val) / 10;
+ p1pll->reference_div = RREG32_PLL(RADEON_PPLL_REF_DIV) & 0x3ff;
+ if (p1pll->reference_div < 2)
+ p1pll->reference_div = 12;
+ p2pll->reference_div = p1pll->reference_div;
+
+ /* These aren't in the device-tree */
+ if (rdev->family >= CHIP_R420) {
+ p1pll->pll_in_min = 100;
+ p1pll->pll_in_max = 1350;
+ p1pll->pll_out_min = 20000;
+ p1pll->pll_out_max = 50000;
+ p2pll->pll_in_min = 100;
+ p2pll->pll_in_max = 1350;
+ p2pll->pll_out_min = 20000;
+ p2pll->pll_out_max = 50000;
+ } else {
+ p1pll->pll_in_min = 40;
+ p1pll->pll_in_max = 500;
+ p1pll->pll_out_min = 12500;
+ p1pll->pll_out_max = 35000;
+ p2pll->pll_in_min = 40;
+ p2pll->pll_in_max = 500;
+ p2pll->pll_out_min = 12500;
+ p2pll->pll_out_max = 35000;
+ }
+
+ spll->reference_freq = mpll->reference_freq = p1pll->reference_freq;
+ spll->reference_div = mpll->reference_div =
+ RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) &
+ RADEON_M_SPLL_REF_DIV_MASK;
+
+ val = of_get_property(dp, "ATY,SCLK", NULL);
+ if (val && *val)
+ rdev->clock.default_sclk = (*val) / 10;
+ else
+ rdev->clock.default_sclk =
+ radeon_legacy_get_engine_clock(rdev);
+
+ val = of_get_property(dp, "ATY,MCLK", NULL);
+ if (val && *val)
+ rdev->clock.default_mclk = (*val) / 10;
+ else
+ rdev->clock.default_mclk =
+ radeon_legacy_get_memory_clock(rdev);
+
+ DRM_INFO("Using device-tree clock info\n");
+
+ return true;
+}
+#else
+static bool __devinit radeon_read_clocks_OF(struct drm_device *dev)
+{
+ return false;
+}
+#endif /* CONFIG_OF */
+
void radeon_get_clock_info(struct drm_device *dev)
{
struct radeon_device *rdev = dev->dev_private;
@@ -105,6 +184,8 @@ void radeon_get_clock_info(struct drm_device *dev)
ret = radeon_atom_get_clock_info(dev);
else
ret = radeon_combios_get_clock_info(dev);
+ if (!ret)
+ ret = radeon_read_clocks_OF(dev);
if (ret) {
if (p1pll->reference_div < 2) {
diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c
index 5e1474cde4b4..885dcfac1838 100644
--- a/drivers/gpu/drm/radeon/radeon_combios.c
+++ b/drivers/gpu/drm/radeon/radeon_combios.c
@@ -480,9 +480,66 @@ radeon_combios_get_hardcoded_edid(struct radeon_device *rdev)
}
static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rdev,
- int ddc_line)
+ enum radeon_combios_ddc ddc,
+ u32 clk_mask,
+ u32 data_mask)
{
struct radeon_i2c_bus_rec i2c;
+ int ddc_line = 0;
+
+ /* ddc id = mask reg
+ * DDC_NONE_DETECTED = none
+ * DDC_DVI = RADEON_GPIO_DVI_DDC
+ * DDC_VGA = RADEON_GPIO_VGA_DDC
+ * DDC_LCD = RADEON_GPIOPAD_MASK
+ * DDC_GPIO = RADEON_MDGPIO_MASK
+ * r1xx/r2xx
+ * DDC_MONID = RADEON_GPIO_MONID
+ * DDC_CRT2 = RADEON_GPIO_CRT2_DDC
+ * r3xx
+ * DDC_MONID = RADEON_GPIO_MONID
+ * DDC_CRT2 = RADEON_GPIO_DVI_DDC
+ * rs3xx/rs4xx
+ * DDC_MONID = RADEON_GPIOPAD_MASK
+ * DDC_CRT2 = RADEON_GPIO_MONID
+ */
+ switch (ddc) {
+ case DDC_NONE_DETECTED:
+ default:
+ ddc_line = 0;
+ break;
+ case DDC_DVI:
+ ddc_line = RADEON_GPIO_DVI_DDC;
+ break;
+ case DDC_VGA:
+ ddc_line = RADEON_GPIO_VGA_DDC;
+ break;
+ case DDC_LCD:
+ ddc_line = RADEON_GPIOPAD_MASK;
+ break;
+ case DDC_GPIO:
+ ddc_line = RADEON_MDGPIO_MASK;
+ break;
+ case DDC_MONID:
+ if (rdev->family == CHIP_RS300 ||
+ rdev->family == CHIP_RS400 ||
+ rdev->family == CHIP_RS480)
+ ddc_line = RADEON_GPIOPAD_MASK;
+ else
+ ddc_line = RADEON_GPIO_MONID;
+ break;
+ case DDC_CRT2:
+ if (rdev->family == CHIP_RS300 ||
+ rdev->family == CHIP_RS400 ||
+ rdev->family == CHIP_RS480)
+ ddc_line = RADEON_GPIO_MONID;
+ else if (rdev->family >= CHIP_R300) {
+ ddc_line = RADEON_GPIO_DVI_DDC;
+ ddc = DDC_DVI;
+ } else
+ ddc_line = RADEON_GPIO_CRT2_DDC;
+ break;
+ }
if (ddc_line == RADEON_GPIOPAD_MASK) {
i2c.mask_clk_reg = RADEON_GPIOPAD_MASK;
@@ -503,15 +560,6 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde
i2c.y_clk_reg = RADEON_MDGPIO_Y;
i2c.y_data_reg = RADEON_MDGPIO_Y;
} else {
- i2c.mask_clk_mask = RADEON_GPIO_EN_1;
- i2c.mask_data_mask = RADEON_GPIO_EN_0;
- i2c.a_clk_mask = RADEON_GPIO_A_1;
- i2c.a_data_mask = RADEON_GPIO_A_0;
- i2c.en_clk_mask = RADEON_GPIO_EN_1;
- i2c.en_data_mask = RADEON_GPIO_EN_0;
- i2c.y_clk_mask = RADEON_GPIO_Y_1;
- i2c.y_data_mask = RADEON_GPIO_Y_0;
-
i2c.mask_clk_reg = ddc_line;
i2c.mask_data_reg = ddc_line;
i2c.a_clk_reg = ddc_line;
@@ -522,6 +570,26 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde
i2c.y_data_reg = ddc_line;
}
+ if (clk_mask && data_mask) {
+ i2c.mask_clk_mask = clk_mask;
+ i2c.mask_data_mask = data_mask;
+ i2c.a_clk_mask = clk_mask;
+ i2c.a_data_mask = data_mask;
+ i2c.en_clk_mask = clk_mask;
+ i2c.en_data_mask = data_mask;
+ i2c.y_clk_mask = clk_mask;
+ i2c.y_data_mask = data_mask;
+ } else {
+ i2c.mask_clk_mask = RADEON_GPIO_EN_1;
+ i2c.mask_data_mask = RADEON_GPIO_EN_0;
+ i2c.a_clk_mask = RADEON_GPIO_A_1;
+ i2c.a_data_mask = RADEON_GPIO_A_0;
+ i2c.en_clk_mask = RADEON_GPIO_EN_1;
+ i2c.en_data_mask = RADEON_GPIO_EN_0;
+ i2c.y_clk_mask = RADEON_GPIO_Y_1;
+ i2c.y_data_mask = RADEON_GPIO_Y_0;
+ }
+
switch (rdev->family) {
case CHIP_R100:
case CHIP_RV100:
@@ -599,7 +667,8 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde
break;
}
i2c.mm_i2c = false;
- i2c.i2c_id = 0;
+
+ i2c.i2c_id = ddc;
i2c.hpd = RADEON_HPD_NONE;
if (ddc_line)
@@ -610,6 +679,62 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde
return i2c;
}
+void radeon_combios_i2c_init(struct radeon_device *rdev)
+{
+ struct drm_device *dev = rdev->ddev;
+ struct radeon_i2c_bus_rec i2c;
+
+
+ i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
+ rdev->i2c_bus[0] = radeon_i2c_create(dev, &i2c, "DVI_DDC");
+
+ i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
+ rdev->i2c_bus[1] = radeon_i2c_create(dev, &i2c, "VGA_DDC");
+
+ i2c.valid = true;
+ i2c.hw_capable = true;
+ i2c.mm_i2c = true;
+ i2c.i2c_id = 0xa0;
+ rdev->i2c_bus[2] = radeon_i2c_create(dev, &i2c, "MM_I2C");
+
+ if (rdev->family == CHIP_RS300 ||
+ rdev->family == CHIP_RS400 ||
+ rdev->family == CHIP_RS480) {
+ u16 offset;
+ u8 id, blocks, clk, data;
+ int i;
+
+ i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0);
+ rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID");
+
+ offset = combios_get_table_offset(dev, COMBIOS_I2C_INFO_TABLE);
+ if (offset) {
+ blocks = RBIOS8(offset + 2);
+ for (i = 0; i < blocks; i++) {
+ id = RBIOS8(offset + 3 + (i * 5) + 0);
+ if (id == 136) {
+ clk = RBIOS8(offset + 3 + (i * 5) + 3);
+ data = RBIOS8(offset + 3 + (i * 5) + 4);
+ i2c = combios_setup_i2c_bus(rdev, DDC_MONID,
+ clk, data);
+ rdev->i2c_bus[4] = radeon_i2c_create(dev, &i2c, "GPIOPAD_MASK");
+ break;
+ }
+ }
+ }
+
+ } else if (rdev->family >= CHIP_R300) {
+ i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
+ rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID");
+ } else {
+ i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
+ rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID");
+
+ i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0);
+ rdev->i2c_bus[4] = radeon_i2c_create(dev, &i2c, "CRT2_DDC");
+ }
+}
+
bool radeon_combios_get_clock_info(struct drm_device *dev)
{
struct radeon_device *rdev = dev->dev_private;
@@ -1247,8 +1372,8 @@ bool radeon_legacy_get_ext_tmds_info_from_table(struct radeon_encoder *encoder,
struct radeon_i2c_bus_rec i2c_bus;
/* default for macs */
- i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
- tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
+ i2c_bus = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
+ tmds->i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus);
/* XXX some macs have duallink chips */
switch (rdev->mode_info.connector_table) {
@@ -1269,47 +1394,16 @@ bool radeon_legacy_get_ext_tmds_info_from_combios(struct radeon_encoder *encoder
struct drm_device *dev = encoder->base.dev;
struct radeon_device *rdev = dev->dev_private;
uint16_t offset;
- uint8_t ver, id, blocks, clk, data;
- int i;
+ uint8_t ver;
enum radeon_combios_ddc gpio;
struct radeon_i2c_bus_rec i2c_bus;
tmds->i2c_bus = NULL;
if (rdev->flags & RADEON_IS_IGP) {
- offset = combios_get_table_offset(dev, COMBIOS_I2C_INFO_TABLE);
- if (offset) {
- ver = RBIOS8(offset);
- DRM_INFO("GPIO Table revision: %d\n", ver);
- blocks = RBIOS8(offset + 2);
- for (i = 0; i < blocks; i++) {
- id = RBIOS8(offset + 3 + (i * 5) + 0);
- if (id == 136) {
- clk = RBIOS8(offset + 3 + (i * 5) + 3);
- data = RBIOS8(offset + 3 + (i * 5) + 4);
- i2c_bus.valid = true;
- i2c_bus.mask_clk_mask = (1 << clk);
- i2c_bus.mask_data_mask = (1 << data);
- i2c_bus.a_clk_mask = (1 << clk);
- i2c_bus.a_data_mask = (1 << data);
- i2c_bus.en_clk_mask = (1 << clk);
- i2c_bus.en_data_mask = (1 << data);
- i2c_bus.y_clk_mask = (1 << clk);
- i2c_bus.y_data_mask = (1 << data);
- i2c_bus.mask_clk_reg = RADEON_GPIOPAD_MASK;
- i2c_bus.mask_data_reg = RADEON_GPIOPAD_MASK;
- i2c_bus.a_clk_reg = RADEON_GPIOPAD_A;
- i2c_bus.a_data_reg = RADEON_GPIOPAD_A;
- i2c_bus.en_clk_reg = RADEON_GPIOPAD_EN;
- i2c_bus.en_data_reg = RADEON_GPIOPAD_EN;
- i2c_bus.y_clk_reg = RADEON_GPIOPAD_Y;
- i2c_bus.y_data_reg = RADEON_GPIOPAD_Y;
- tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
- tmds->dvo_chip = DVO_SIL164;
- tmds->slave_addr = 0x70 >> 1; /* 7 bit addressing */
- break;
- }
- }
- }
+ i2c_bus = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
+ tmds->i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus);
+ tmds->dvo_chip = DVO_SIL164;
+ tmds->slave_addr = 0x70 >> 1; /* 7 bit addressing */
} else {
offset = combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE);
if (offset) {
@@ -1318,37 +1412,15 @@ bool radeon_legacy_get_ext_tmds_info_from_combios(struct radeon_encoder *encoder
tmds->slave_addr = RBIOS8(offset + 4 + 2);
tmds->slave_addr >>= 1; /* 7 bit addressing */
gpio = RBIOS8(offset + 4 + 3);
- switch (gpio) {
- case DDC_MONID:
- i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
- tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
- break;
- case DDC_DVI:
- i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
- tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
- break;
- case DDC_VGA:
- i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
- tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
- break;
- case DDC_CRT2:
- /* R3xx+ chips don't have GPIO_CRT2_DDC gpio pad */
- if (rdev->family >= CHIP_R300)
- i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
- else
- i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
- tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
- break;
- case DDC_LCD: /* MM i2c */
+ if (gpio == DDC_LCD) {
+ /* MM i2c */
i2c_bus.valid = true;
i2c_bus.hw_capable = true;
i2c_bus.mm_i2c = true;
- tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
- break;
- default:
- DRM_ERROR("Unsupported gpio %d\n", gpio);
- break;
- }
+ i2c_bus.i2c_id = 0xa0;
+ } else
+ i2c_bus = combios_setup_i2c_bus(rdev, gpio, 0, 0);
+ tmds->i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus);
}
}
@@ -1430,7 +1502,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
/* these are the most common settings */
if (rdev->flags & RADEON_SINGLE_CRTC) {
/* VGA - primary dac */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1445,7 +1517,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
&hpd);
} else if (rdev->flags & RADEON_IS_MOBILITY) {
/* LVDS */
- ddc_i2c = combios_setup_i2c_bus(rdev, 0);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_NONE_DETECTED, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1460,7 +1532,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
&hpd);
/* VGA - primary dac */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1475,7 +1547,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
&hpd);
} else {
/* DVI-I - tv dac, int tmds */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
hpd.hpd = RADEON_HPD_1;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1496,7 +1568,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
&hpd);
/* VGA - primary dac */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1532,7 +1604,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (ibook)\n",
rdev->mode_info.connector_table);
/* LVDS */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1544,7 +1616,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
CONNECTOR_OBJECT_ID_LVDS,
&hpd);
/* VGA - TV DAC */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1573,7 +1645,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (powerbook external tmds)\n",
rdev->mode_info.connector_table);
/* LVDS */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1585,7 +1657,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
CONNECTOR_OBJECT_ID_LVDS,
&hpd);
/* DVI-I - primary dac, ext tmds */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_2; /* ??? */
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1622,7 +1694,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (powerbook internal tmds)\n",
rdev->mode_info.connector_table);
/* LVDS */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1634,7 +1706,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
CONNECTOR_OBJECT_ID_LVDS,
&hpd);
/* DVI-I - primary dac, int tmds */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_1; /* ??? */
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1670,7 +1742,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (powerbook vga)\n",
rdev->mode_info.connector_table);
/* LVDS */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1682,7 +1754,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
CONNECTOR_OBJECT_ID_LVDS,
&hpd);
/* VGA - primary dac */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1711,7 +1783,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (mini external tmds)\n",
rdev->mode_info.connector_table);
/* DVI-I - tv dac, ext tmds */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0);
hpd.hpd = RADEON_HPD_2; /* ??? */
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1748,7 +1820,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (mini internal tmds)\n",
rdev->mode_info.connector_table);
/* DVI-I - tv dac, int tmds */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0);
hpd.hpd = RADEON_HPD_1; /* ??? */
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1784,7 +1856,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (imac g5 isight)\n",
rdev->mode_info.connector_table);
/* DVI-D - int tmds */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
hpd.hpd = RADEON_HPD_1; /* ??? */
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1796,7 +1868,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D,
&hpd);
/* VGA - tv dac */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1825,7 +1897,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (emac)\n",
rdev->mode_info.connector_table);
/* VGA - primary dac */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1837,7 +1909,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
CONNECTOR_OBJECT_ID_VGA,
&hpd);
/* VGA - tv dac */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1866,7 +1938,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_INFO("Connector Table: %d (rn50-power)\n",
rdev->mode_info.connector_table);
/* VGA - primary dac */
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1877,7 +1949,7 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
CONNECTOR_OBJECT_ID_VGA,
&hpd);
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_encoder(dev,
radeon_get_encoder_id(dev,
@@ -1907,31 +1979,6 @@ static bool radeon_apply_legacy_quirks(struct drm_device *dev,
struct radeon_i2c_bus_rec *ddc_i2c,
struct radeon_hpd *hpd)
{
- struct radeon_device *rdev = dev->dev_private;
-
- /* XPRESS DDC quirks */
- if ((rdev->family == CHIP_RS400 ||
- rdev->family == CHIP_RS480) &&
- ddc_i2c->mask_clk_reg == RADEON_GPIO_CRT2_DDC)
- *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
- else if ((rdev->family == CHIP_RS400 ||
- rdev->family == CHIP_RS480) &&
- ddc_i2c->mask_clk_reg == RADEON_GPIO_MONID) {
- *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIOPAD_MASK);
- ddc_i2c->mask_clk_mask = (0x20 << 8);
- ddc_i2c->mask_data_mask = 0x80;
- ddc_i2c->a_clk_mask = (0x20 << 8);
- ddc_i2c->a_data_mask = 0x80;
- ddc_i2c->en_clk_mask = (0x20 << 8);
- ddc_i2c->en_data_mask = 0x80;
- ddc_i2c->y_clk_mask = (0x20 << 8);
- ddc_i2c->y_data_mask = 0x80;
- }
-
- /* R3xx+ chips don't have GPIO_CRT2_DDC gpio pad */
- if ((rdev->family >= CHIP_R300) &&
- ddc_i2c->mask_clk_reg == RADEON_GPIO_CRT2_DDC)
- *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
/* Certain IBM chipset RN50s have a BIOS reporting two VGAs,
one with VGA DDC and one with CRT2 DDC. - kill the CRT2 DDC one */
@@ -2035,27 +2082,7 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
connector = (tmp >> 12) & 0xf;
ddc_type = (tmp >> 8) & 0xf;
- switch (ddc_type) {
- case DDC_MONID:
- ddc_i2c =
- combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
- break;
- case DDC_DVI:
- ddc_i2c =
- combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
- break;
- case DDC_VGA:
- ddc_i2c =
- combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
- break;
- case DDC_CRT2:
- ddc_i2c =
- combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
- break;
- default:
- ddc_i2c.valid = false;
- break;
- }
+ ddc_i2c = combios_setup_i2c_bus(rdev, ddc_type, 0, 0);
switch (connector) {
case CONNECTOR_PROPRIETARY_LEGACY:
@@ -2225,7 +2252,7 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
0),
ATOM_DEVICE_DFP1_SUPPORT);
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
hpd.hpd = RADEON_HPD_1;
radeon_add_legacy_connector(dev,
0,
@@ -2245,7 +2272,7 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
ATOM_DEVICE_CRT1_SUPPORT,
1),
ATOM_DEVICE_CRT1_SUPPORT);
- ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ ddc_i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
hpd.hpd = RADEON_HPD_NONE;
radeon_add_legacy_connector(dev,
0,
@@ -2278,70 +2305,25 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
if (lcd_ddc_info) {
ddc_type = RBIOS8(lcd_ddc_info + 2);
switch (ddc_type) {
- case DDC_MONID:
- ddc_i2c =
- combios_setup_i2c_bus
- (rdev, RADEON_GPIO_MONID);
- break;
- case DDC_DVI:
- ddc_i2c =
- combios_setup_i2c_bus
- (rdev, RADEON_GPIO_DVI_DDC);
- break;
- case DDC_VGA:
- ddc_i2c =
- combios_setup_i2c_bus
- (rdev, RADEON_GPIO_VGA_DDC);
- break;
- case DDC_CRT2:
- ddc_i2c =
- combios_setup_i2c_bus
- (rdev, RADEON_GPIO_CRT2_DDC);
- break;
case DDC_LCD:
ddc_i2c =
- combios_setup_i2c_bus
- (rdev, RADEON_GPIOPAD_MASK);
- ddc_i2c.mask_clk_mask =
- RBIOS32(lcd_ddc_info + 3);
- ddc_i2c.mask_data_mask =
- RBIOS32(lcd_ddc_info + 7);
- ddc_i2c.a_clk_mask =
- RBIOS32(lcd_ddc_info + 3);
- ddc_i2c.a_data_mask =
- RBIOS32(lcd_ddc_info + 7);
- ddc_i2c.en_clk_mask =
- RBIOS32(lcd_ddc_info + 3);
- ddc_i2c.en_data_mask =
- RBIOS32(lcd_ddc_info + 7);
- ddc_i2c.y_clk_mask =
- RBIOS32(lcd_ddc_info + 3);
- ddc_i2c.y_data_mask =
- RBIOS32(lcd_ddc_info + 7);
+ combios_setup_i2c_bus(rdev,
+ DDC_LCD,
+ RBIOS32(lcd_ddc_info + 3),
+ RBIOS32(lcd_ddc_info + 7));
+ radeon_i2c_add(rdev, &ddc_i2c, "LCD");
break;
case DDC_GPIO:
ddc_i2c =
- combios_setup_i2c_bus
- (rdev, RADEON_MDGPIO_MASK);
- ddc_i2c.mask_clk_mask =
- RBIOS32(lcd_ddc_info + 3);
- ddc_i2c.mask_data_mask =
- RBIOS32(lcd_ddc_info + 7);
- ddc_i2c.a_clk_mask =
- RBIOS32(lcd_ddc_info + 3);
- ddc_i2c.a_data_mask =
- RBIOS32(lcd_ddc_info + 7);
- ddc_i2c.en_clk_mask =
- RBIOS32(lcd_ddc_info + 3);
- ddc_i2c.en_data_mask =
- RBIOS32(lcd_ddc_info + 7);
- ddc_i2c.y_clk_mask =
- RBIOS32(lcd_ddc_info + 3);
- ddc_i2c.y_data_mask =
- RBIOS32(lcd_ddc_info + 7);
+ combios_setup_i2c_bus(rdev,
+ DDC_GPIO,
+ RBIOS32(lcd_ddc_info + 3),
+ RBIOS32(lcd_ddc_info + 7));
+ radeon_i2c_add(rdev, &ddc_i2c, "LCD");
break;
default:
- ddc_i2c.valid = false;
+ ddc_i2c =
+ combios_setup_i2c_bus(rdev, ddc_type, 0, 0);
break;
}
DRM_DEBUG_KMS("LCD DDC Info Table found!\n");
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 2395c8600cf4..47c4b276d30c 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -518,8 +518,6 @@ static void radeon_connector_destroy(struct drm_connector *connector)
{
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- if (radeon_connector->ddc_bus)
- radeon_i2c_destroy(radeon_connector->ddc_bus);
if (radeon_connector->edid)
kfree(radeon_connector->edid);
kfree(radeon_connector->con_priv);
@@ -955,8 +953,6 @@ static void radeon_dp_connector_destroy(struct drm_connector *connector)
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
- if (radeon_connector->ddc_bus)
- radeon_i2c_destroy(radeon_connector->ddc_bus);
if (radeon_connector->edid)
kfree(radeon_connector->edid);
if (radeon_dig_connector->dp_i2c_bus)
@@ -1044,7 +1040,8 @@ radeon_add_atom_connector(struct drm_device *dev,
bool linkb,
uint32_t igp_lane_info,
uint16_t connector_object_id,
- struct radeon_hpd *hpd)
+ struct radeon_hpd *hpd,
+ struct radeon_router *router)
{
struct radeon_device *rdev = dev->dev_private;
struct drm_connector *connector;
@@ -1069,6 +1066,11 @@ radeon_add_atom_connector(struct drm_device *dev,
radeon_connector->shared_ddc = true;
shared_ddc = true;
}
+ if (radeon_connector->router_bus && router->valid &&
+ (radeon_connector->router.router_id == router->router_id)) {
+ radeon_connector->shared_ddc = false;
+ shared_ddc = false;
+ }
}
}
@@ -1083,12 +1085,18 @@ radeon_add_atom_connector(struct drm_device *dev,
radeon_connector->shared_ddc = shared_ddc;
radeon_connector->connector_object_id = connector_object_id;
radeon_connector->hpd = *hpd;
+ radeon_connector->router = *router;
+ if (router->valid) {
+ radeon_connector->router_bus = radeon_i2c_lookup(rdev, &router->i2c_info);
+ if (!radeon_connector->router_bus)
+ goto failed;
+ }
switch (connector_type) {
case DRM_MODE_CONNECTOR_VGA:
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "VGA");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1104,7 +1112,7 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1126,7 +1134,7 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1156,7 +1164,7 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "HDMI");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1187,10 +1195,7 @@ radeon_add_atom_connector(struct drm_device *dev,
radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch");
if (!radeon_dig_connector->dp_i2c_bus)
goto failed;
- if (connector_type == DRM_MODE_CONNECTOR_eDP)
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "eDP");
- else
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DP");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1230,7 +1235,7 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "LVDS");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1252,8 +1257,6 @@ radeon_add_atom_connector(struct drm_device *dev,
return;
failed:
- if (radeon_connector->ddc_bus)
- radeon_i2c_destroy(radeon_connector->ddc_bus);
drm_connector_cleanup(connector);
kfree(connector);
}
@@ -1300,7 +1303,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "VGA");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1316,7 +1319,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1332,7 +1335,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1372,7 +1375,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type);
drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs);
if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "LVDS");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
goto failed;
}
@@ -1393,8 +1396,6 @@ radeon_add_legacy_connector(struct drm_device *dev,
return;
failed:
- if (radeon_connector->ddc_bus)
- radeon_i2c_destroy(radeon_connector->ddc_bus);
drm_connector_cleanup(connector);
kfree(connector);
}
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index ae0fb7356e62..fcc79b5d22d1 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -72,7 +72,7 @@ int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
if (p->relocs[i].gobj == NULL) {
DRM_ERROR("gem object lookup failed 0x%x\n",
r->handle);
- return -EINVAL;
+ return -ENOENT;
}
p->relocs_ptr[i] = &p->relocs[i];
p->relocs[i].robj = p->relocs[i].gobj->driver_private;
diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c
index 4eb67c0e0996..5731fc9b1ae3 100644
--- a/drivers/gpu/drm/radeon/radeon_cursor.c
+++ b/drivers/gpu/drm/radeon/radeon_cursor.c
@@ -170,7 +170,7 @@ int radeon_crtc_cursor_set(struct drm_crtc *crtc,
obj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
if (!obj) {
DRM_ERROR("Cannot find cursor object %x for crtc %d\n", handle, radeon_crtc->crtc_id);
- return -EINVAL;
+ return -ENOENT;
}
ret = radeon_gem_object_pin(obj, RADEON_GEM_DOMAIN_VRAM, &gpu_addr);
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index a64811a94519..4f7a170d1566 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -347,7 +347,8 @@ int radeon_dummy_page_init(struct radeon_device *rdev)
return -ENOMEM;
rdev->dummy_page.addr = pci_map_page(rdev->pdev, rdev->dummy_page.page,
0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
- if (!rdev->dummy_page.addr) {
+ if (pci_dma_mapping_error(rdev->pdev, rdev->dummy_page.addr)) {
+ dev_err(&rdev->pdev->dev, "Failed to DMA MAP the dummy page\n");
__free_page(rdev->dummy_page.page);
rdev->dummy_page.page = NULL;
return -ENOMEM;
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index 74dac9635d70..5764f4d3b4f1 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -161,17 +161,13 @@ void radeon_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
}
static void radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
- u16 *blue, uint32_t size)
+ u16 *blue, uint32_t start, uint32_t size)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
- int i;
-
- if (size != 256) {
- return;
- }
+ int end = (start + size > 256) ? 256 : start + size, i;
/* userspace palettes are always correct as is */
- for (i = 0; i < 256; i++) {
+ for (i = start; i < end; i++) {
radeon_crtc->lut_r[i] = red[i] >> 6;
radeon_crtc->lut_g[i] = green[i] >> 6;
radeon_crtc->lut_b[i] = blue[i] >> 6;
@@ -319,6 +315,10 @@ static void radeon_print_display_setup(struct drm_device *dev)
radeon_connector->ddc_bus->rec.en_data_reg,
radeon_connector->ddc_bus->rec.y_clk_reg,
radeon_connector->ddc_bus->rec.y_data_reg);
+ if (radeon_connector->router_bus)
+ DRM_INFO(" DDC Router 0x%x/0x%x\n",
+ radeon_connector->router.mux_control_pin,
+ radeon_connector->router.mux_state);
} else {
if (connector->connector_type == DRM_MODE_CONNECTOR_VGA ||
connector->connector_type == DRM_MODE_CONNECTOR_DVII ||
@@ -395,6 +395,10 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
struct radeon_device *rdev = dev->dev_private;
int ret = 0;
+ /* on hw with routers, select right port */
+ if (radeon_connector->router.valid)
+ radeon_router_select_port(radeon_connector);
+
if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
(radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) {
struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
@@ -425,6 +429,10 @@ static int radeon_ddc_dump(struct drm_connector *connector)
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
int ret = 0;
+ /* on hw with routers, select right port */
+ if (radeon_connector->router.valid)
+ radeon_router_select_port(radeon_connector);
+
if (!radeon_connector->ddc_bus)
return -1;
edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter);
@@ -876,13 +884,12 @@ radeon_user_framebuffer_create(struct drm_device *dev,
if (obj == NULL) {
dev_err(&dev->pdev->dev, "No GEM object associated to handle 0x%08X, "
"can't create framebuffer\n", mode_cmd->handle);
- return NULL;
+ return ERR_PTR(-ENOENT);
}
radeon_fb = kzalloc(sizeof(*radeon_fb), GFP_KERNEL);
- if (radeon_fb == NULL) {
- return NULL;
- }
+ if (radeon_fb == NULL)
+ return ERR_PTR(-ENOMEM);
radeon_framebuffer_init(dev, radeon_fb, mode_cmd, obj);
@@ -1040,6 +1047,9 @@ int radeon_modeset_init(struct radeon_device *rdev)
return ret;
}
+ /* init i2c buses */
+ radeon_i2c_init(rdev);
+
/* check combios for a valid hardcoded EDID - Sun servers */
if (!rdev->is_atom_bios) {
/* check for hardcoded EDID in BIOS */
@@ -1080,6 +1090,8 @@ void radeon_modeset_fini(struct radeon_device *rdev)
drm_mode_config_cleanup(rdev->ddev);
rdev->mode_info.mode_config_initialized = false;
}
+ /* free i2c buses */
+ radeon_i2c_fini(rdev);
}
bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
index a72a3ee5d69b..c578f265b24c 100644
--- a/drivers/gpu/drm/radeon/radeon_gem.c
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
@@ -226,7 +226,7 @@ int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data,
/* just do a BO wait for now */
gobj = drm_gem_object_lookup(dev, filp, args->handle);
if (gobj == NULL) {
- return -EINVAL;
+ return -ENOENT;
}
robj = gobj->driver_private;
@@ -245,7 +245,7 @@ int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data,
gobj = drm_gem_object_lookup(dev, filp, args->handle);
if (gobj == NULL) {
- return -EINVAL;
+ return -ENOENT;
}
robj = gobj->driver_private;
args->addr_ptr = radeon_bo_mmap_offset(robj);
@@ -264,7 +264,7 @@ int radeon_gem_busy_ioctl(struct drm_device *dev, void *data,
gobj = drm_gem_object_lookup(dev, filp, args->handle);
if (gobj == NULL) {
- return -EINVAL;
+ return -ENOENT;
}
robj = gobj->driver_private;
r = radeon_bo_wait(robj, &cur_placement, true);
@@ -294,7 +294,7 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
gobj = drm_gem_object_lookup(dev, filp, args->handle);
if (gobj == NULL) {
- return -EINVAL;
+ return -ENOENT;
}
robj = gobj->driver_private;
r = radeon_bo_wait(robj, NULL, false);
@@ -316,7 +316,7 @@ int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data,
DRM_DEBUG("%d \n", args->handle);
gobj = drm_gem_object_lookup(dev, filp, args->handle);
if (gobj == NULL)
- return -EINVAL;
+ return -ENOENT;
robj = gobj->driver_private;
r = radeon_bo_set_tiling_flags(robj, args->tiling_flags, args->pitch);
drm_gem_object_unreference_unlocked(gobj);
@@ -334,7 +334,7 @@ int radeon_gem_get_tiling_ioctl(struct drm_device *dev, void *data,
DRM_DEBUG("\n");
gobj = drm_gem_object_lookup(dev, filp, args->handle);
if (gobj == NULL)
- return -EINVAL;
+ return -ENOENT;
rbo = gobj->driver_private;
r = radeon_bo_reserve(rbo, false);
if (unlikely(r != 0))
diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c
index 5def6f5dff38..bfd2ce5f5372 100644
--- a/drivers/gpu/drm/radeon/radeon_i2c.c
+++ b/drivers/gpu/drm/radeon/radeon_i2c.c
@@ -52,6 +52,10 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector)
}
};
+ /* on hw with routers, select right port */
+ if (radeon_connector->router.valid)
+ radeon_router_select_port(radeon_connector);
+
ret = i2c_transfer(&radeon_connector->ddc_bus->adapter, msgs, 2);
if (ret == 2)
return true;
@@ -960,6 +964,59 @@ void radeon_i2c_destroy(struct radeon_i2c_chan *i2c)
kfree(i2c);
}
+/* Add the default buses */
+void radeon_i2c_init(struct radeon_device *rdev)
+{
+ if (rdev->is_atom_bios)
+ radeon_atombios_i2c_init(rdev);
+ else
+ radeon_combios_i2c_init(rdev);
+}
+
+/* remove all the buses */
+void radeon_i2c_fini(struct radeon_device *rdev)
+{
+ int i;
+
+ for (i = 0; i < RADEON_MAX_I2C_BUS; i++) {
+ if (rdev->i2c_bus[i]) {
+ radeon_i2c_destroy(rdev->i2c_bus[i]);
+ rdev->i2c_bus[i] = NULL;
+ }
+ }
+}
+
+/* Add additional buses */
+void radeon_i2c_add(struct radeon_device *rdev,
+ struct radeon_i2c_bus_rec *rec,
+ const char *name)
+{
+ struct drm_device *dev = rdev->ddev;
+ int i;
+
+ for (i = 0; i < RADEON_MAX_I2C_BUS; i++) {
+ if (!rdev->i2c_bus[i]) {
+ rdev->i2c_bus[i] = radeon_i2c_create(dev, rec, name);
+ return;
+ }
+ }
+}
+
+/* looks up bus based on id */
+struct radeon_i2c_chan *radeon_i2c_lookup(struct radeon_device *rdev,
+ struct radeon_i2c_bus_rec *i2c_bus)
+{
+ int i;
+
+ for (i = 0; i < RADEON_MAX_I2C_BUS; i++) {
+ if (rdev->i2c_bus[i] &&
+ (rdev->i2c_bus[i]->rec.i2c_id == i2c_bus->i2c_id)) {
+ return rdev->i2c_bus[i];
+ }
+ }
+ return NULL;
+}
+
struct drm_encoder *radeon_best_encoder(struct drm_connector *connector)
{
return NULL;
@@ -1020,3 +1077,28 @@ void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c_bus,
addr, val);
}
+/* router switching */
+void radeon_router_select_port(struct radeon_connector *radeon_connector)
+{
+ u8 val;
+
+ if (!radeon_connector->router.valid)
+ return;
+
+ radeon_i2c_get_byte(radeon_connector->router_bus,
+ radeon_connector->router.i2c_addr,
+ 0x3, &val);
+ val &= radeon_connector->router.mux_control_pin;
+ radeon_i2c_put_byte(radeon_connector->router_bus,
+ radeon_connector->router.i2c_addr,
+ 0x3, val);
+ radeon_i2c_get_byte(radeon_connector->router_bus,
+ radeon_connector->router.i2c_addr,
+ 0x1, &val);
+ val &= radeon_connector->router.mux_control_pin;
+ val |= radeon_connector->router.mux_state;
+ radeon_i2c_put_byte(radeon_connector->router_bus,
+ radeon_connector->router.i2c_addr,
+ 0x1, val);
+}
+
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index ddcd3b13f151..b1c8ace5f080 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -112,7 +112,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
info = data;
value_ptr = (uint32_t *)((unsigned long)info->value);
- value = *value_ptr;
+ if (DRM_COPY_FROM_USER(&value, value_ptr, sizeof(value)))
+ return -EFAULT;
+
switch (info->request) {
case RADEON_INFO_DEVICE_ID:
value = dev->pci_device;
@@ -160,13 +162,27 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
return -EINVAL;
}
case RADEON_INFO_WANT_HYPERZ:
+ /* The "value" here is both an input and output parameter.
+ * If the input value is 1, filp requests hyper-z access.
+ * If the input value is 0, filp revokes its hyper-z access.
+ *
+ * When returning, the value is 1 if filp owns hyper-z access,
+ * 0 otherwise. */
+ if (value >= 2) {
+ DRM_DEBUG_KMS("WANT_HYPERZ: invalid value %d\n", value);
+ return -EINVAL;
+ }
mutex_lock(&dev->struct_mutex);
- if (rdev->hyperz_filp)
- value = 0;
- else {
- rdev->hyperz_filp = filp;
- value = 1;
+ if (value == 1) {
+ /* wants hyper-z */
+ if (!rdev->hyperz_filp)
+ rdev->hyperz_filp = filp;
+ } else if (value == 0) {
+ /* revokes hyper-z */
+ if (rdev->hyperz_filp == filp)
+ rdev->hyperz_filp = NULL;
}
+ value = rdev->hyperz_filp == filp ? 1 : 0;
mutex_unlock(&dev->struct_mutex);
break;
default:
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 71aea4037e90..5bbc086b9267 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -82,6 +82,8 @@ enum radeon_hpd_id {
RADEON_HPD_NONE = 0xff,
};
+#define RADEON_MAX_I2C_BUS 16
+
/* radeon gpio-based i2c
* 1. "mask" reg and bits
* grabs the gpio pins for software use
@@ -398,6 +400,16 @@ struct radeon_hpd {
struct radeon_gpio_rec gpio;
};
+struct radeon_router {
+ bool valid;
+ u32 router_id;
+ struct radeon_i2c_bus_rec i2c_info;
+ u8 i2c_addr;
+ u8 mux_type;
+ u8 mux_control_pin;
+ u8 mux_state;
+};
+
struct radeon_connector {
struct drm_connector base;
uint32_t connector_id;
@@ -413,6 +425,8 @@ struct radeon_connector {
bool dac_load_detect;
uint16_t connector_object_id;
struct radeon_hpd hpd;
+ struct radeon_router router;
+ struct radeon_i2c_chan *router_bus;
};
struct radeon_framebuffer {
@@ -445,6 +459,15 @@ extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder,
extern int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
uint8_t write_byte, uint8_t *read_byte);
+extern void radeon_i2c_init(struct radeon_device *rdev);
+extern void radeon_i2c_fini(struct radeon_device *rdev);
+extern void radeon_combios_i2c_init(struct radeon_device *rdev);
+extern void radeon_atombios_i2c_init(struct radeon_device *rdev);
+extern void radeon_i2c_add(struct radeon_device *rdev,
+ struct radeon_i2c_bus_rec *rec,
+ const char *name);
+extern struct radeon_i2c_chan *radeon_i2c_lookup(struct radeon_device *rdev,
+ struct radeon_i2c_bus_rec *i2c_bus);
extern struct radeon_i2c_chan *radeon_i2c_create_dp(struct drm_device *dev,
struct radeon_i2c_bus_rec *rec,
const char *name);
@@ -460,6 +483,7 @@ extern void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c,
u8 slave_addr,
u8 addr,
u8 val);
+extern void radeon_router_select_port(struct radeon_connector *radeon_connector);
extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector);
extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector);
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index 95f8b3a3c43d..58038f5cab38 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -472,9 +472,9 @@ static const struct attribute_group hwmon_attrgroup = {
.attrs = hwmon_attributes,
};
-static void radeon_hwmon_init(struct radeon_device *rdev)
+static int radeon_hwmon_init(struct radeon_device *rdev)
{
- int err;
+ int err = 0;
rdev->pm.int_hwmon_dev = NULL;
@@ -483,15 +483,26 @@ static void radeon_hwmon_init(struct radeon_device *rdev)
case THERMAL_TYPE_RV770:
case THERMAL_TYPE_EVERGREEN:
rdev->pm.int_hwmon_dev = hwmon_device_register(rdev->dev);
+ if (IS_ERR(rdev->pm.int_hwmon_dev)) {
+ err = PTR_ERR(rdev->pm.int_hwmon_dev);
+ dev_err(rdev->dev,
+ "Unable to register hwmon device: %d\n", err);
+ break;
+ }
dev_set_drvdata(rdev->pm.int_hwmon_dev, rdev->ddev);
err = sysfs_create_group(&rdev->pm.int_hwmon_dev->kobj,
&hwmon_attrgroup);
- if (err)
- DRM_ERROR("Unable to create hwmon sysfs file: %d\n", err);
+ if (err) {
+ dev_err(rdev->dev,
+ "Unable to create hwmon sysfs file: %d\n", err);
+ hwmon_device_unregister(rdev->dev);
+ }
break;
default:
break;
}
+
+ return err;
}
static void radeon_hwmon_fini(struct radeon_device *rdev)
@@ -540,6 +551,7 @@ void radeon_pm_resume(struct radeon_device *rdev)
int radeon_pm_init(struct radeon_device *rdev)
{
int ret;
+
/* default to profile method */
rdev->pm.pm_method = PM_METHOD_PROFILE;
rdev->pm.profile = PM_PROFILE_DEFAULT;
@@ -561,7 +573,9 @@ int radeon_pm_init(struct radeon_device *rdev)
}
/* set up the internal thermal sensor if applicable */
- radeon_hwmon_init(rdev);
+ ret = radeon_hwmon_init(rdev);
+ if (ret)
+ return ret;
if (rdev->pm.num_power_states > 1) {
/* where's the best place to put these? */
ret = device_create_file(rdev->dev, &dev_attr_power_profile);
diff --git a/drivers/gpu/drm/radeon/reg_srcs/rv515 b/drivers/gpu/drm/radeon/reg_srcs/rv515
index 8293855f5f0d..b3f9f1d92005 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/rv515
+++ b/drivers/gpu/drm/radeon/reg_srcs/rv515
@@ -316,6 +316,7 @@ rv515 0x6d40
0x4BD0 FG_FOG_COLOR_B
0x4BD4 FG_ALPHA_FUNC
0x4BD8 FG_DEPTH_SRC
+0x4BE0 FG_ALPHA_VALUE
0x4C00 US_ALU_CONST_R_0
0x4C04 US_ALU_CONST_G_0
0x4C08 US_ALU_CONST_B_0
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index 437ac786277a..64d7f47df868 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -737,7 +737,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
if (ret) {
DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
- return NULL;
+ return ERR_PTR(ret);
}
return &vfb->base;
@@ -747,7 +747,7 @@ try_dmabuf:
ret = vmw_user_dmabuf_lookup(tfile, mode_cmd->handle, &bo);
if (ret) {
DRM_ERROR("failed to find buffer: %i\n", ret);
- return NULL;
+ return ERR_PTR(-ENOENT);
}
ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, bo, &vfb,
@@ -758,7 +758,7 @@ try_dmabuf:
if (ret) {
DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
- return NULL;
+ return ERR_PTR(ret);
}
return &vfb->base;
@@ -768,7 +768,7 @@ err_not_scanout:
/* vmw_user_surface_lookup takes one ref */
vmw_surface_unreference(&surface);
- return NULL;
+ return ERR_PTR(-EINVAL);
}
static struct drm_mode_config_funcs vmw_kms_funcs = {
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index cfaf690a5b2f..2ff5cf78235f 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -79,7 +79,7 @@ static void vmw_ldu_crtc_restore(struct drm_crtc *crtc)
static void vmw_ldu_crtc_gamma_set(struct drm_crtc *crtc,
u16 *r, u16 *g, u16 *b,
- uint32_t size)
+ uint32_t start, uint32_t size)
{
}
diff --git a/drivers/hwmon/ams/ams.h b/drivers/hwmon/ams/ams.h
index b28d7e27a031..90f094d45450 100644
--- a/drivers/hwmon/ams/ams.h
+++ b/drivers/hwmon/ams/ams.h
@@ -23,7 +23,7 @@ struct ams {
/* General properties */
struct device_node *of_node;
- struct of_device *of_dev;
+ struct platform_device *of_dev;
char has_device;
char vflag;
u32 orient1;
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index a92e28a35767..c070c9714cbe 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -501,13 +501,10 @@ static int __cpuinit coretemp_cpu_callback(struct notifier_block *nfb,
switch (action) {
case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
case CPU_DOWN_FAILED:
- case CPU_DOWN_FAILED_FROZEN:
coretemp_device_add(cpu);
break;
case CPU_DOWN_PREPARE:
- case CPU_DOWN_PREPARE_FROZEN:
coretemp_device_remove(cpu);
break;
}
diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c
index ce3c7bc81814..d5226c9e1201 100644
--- a/drivers/hwmon/mc13783-adc.c
+++ b/drivers/hwmon/mc13783-adc.c
@@ -18,7 +18,7 @@
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <linux/mfd/mc13783-private.h>
+#include <linux/mfd/mc13783.h>
#include <linux/platform_device.h>
#include <linux/hwmon-sysfs.h>
#include <linux/kernel.h>
@@ -144,6 +144,14 @@ static const struct attribute_group mc13783_group_ts = {
.attrs = mc13783_attr_ts,
};
+static int mc13783_adc_use_touchscreen(struct platform_device *pdev)
+{
+ struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
+ unsigned flags = mc13783_get_flags(priv->mc13783);
+
+ return flags & MC13783_USE_TOUCHSCREEN;
+}
+
static int __init mc13783_adc_probe(struct platform_device *pdev)
{
struct mc13783_adc_priv *priv;
@@ -162,10 +170,11 @@ static int __init mc13783_adc_probe(struct platform_device *pdev)
if (ret)
goto out_err_create1;
- if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN))
+ if (!mc13783_adc_use_touchscreen(pdev)) {
ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_ts);
if (ret)
goto out_err_create2;
+ }
priv->hwmon_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(priv->hwmon_dev)) {
@@ -180,7 +189,7 @@ static int __init mc13783_adc_probe(struct platform_device *pdev)
out_err_register:
- if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN))
+ if (!mc13783_adc_use_touchscreen(pdev))
sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts);
out_err_create2:
@@ -199,7 +208,7 @@ static int __devexit mc13783_adc_remove(struct platform_device *pdev)
hwmon_device_unregister(priv->hwmon_dev);
- if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN))
+ if (!mc13783_adc_use_touchscreen(pdev))
sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts);
sysfs_remove_group(&pdev->dev.kobj, &mc13783_group);
diff --git a/drivers/hwmon/ultra45_env.c b/drivers/hwmon/ultra45_env.c
index 89643261ccdb..d863e13a50b8 100644
--- a/drivers/hwmon/ultra45_env.c
+++ b/drivers/hwmon/ultra45_env.c
@@ -234,7 +234,7 @@ static const struct attribute_group env_group = {
.attrs = env_attributes,
};
-static int __devinit env_probe(struct of_device *op,
+static int __devinit env_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct env *p = kzalloc(sizeof(*p), GFP_KERNEL);
@@ -276,7 +276,7 @@ out_free:
goto out;
}
-static int __devexit env_remove(struct of_device *op)
+static int __devexit env_remove(struct platform_device *op)
{
struct env *p = dev_get_drvdata(&op->dev);
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bceafbfa7268..15a9702e2941 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -521,12 +521,19 @@ config I2C_PXA_SLAVE
is necessary for systems where the PXA may be a target on the
I2C bus.
+config HAVE_S3C2410_I2C
+ bool
+ help
+ This will include I2C support for Samsung SoCs. If you want to
+ include I2C support for any machine, kindly select this in the
+ respective Kconfig file.
+
config I2C_S3C2410
tristate "S3C2410 I2C Driver"
- depends on ARCH_S3C2410 || ARCH_S3C64XX
+ depends on HAVE_S3C2410_I2C
help
Say Y here to include support for I2C controller in the
- Samsung S3C2410 based System-on-Chip devices.
+ Samsung SoCs.
config I2C_S6000
tristate "S6000 I2C support"
@@ -549,7 +556,7 @@ config I2C_SH7760
config I2C_SH_MOBILE
tristate "SuperH Mobile I2C Controller"
- depends on SUPERH
+ depends on SUPERH || ARCH_SHMOBILE
help
If you say yes to this option, support will be included for the
built-in I2C interface on the Renesas SH-Mobile processor.
diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c
index e591de1bc704..f7bd2613cecc 100644
--- a/drivers/i2c/busses/i2c-cpm.c
+++ b/drivers/i2c/busses/i2c-cpm.c
@@ -105,7 +105,7 @@ struct i2c_reg {
struct cpm_i2c {
char *base;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
struct i2c_adapter adap;
uint dp_addr;
int version; /* CPM1=1, CPM2=2 */
@@ -428,7 +428,7 @@ static const struct i2c_adapter cpm_ops = {
static int __devinit cpm_i2c_setup(struct cpm_i2c *cpm)
{
- struct of_device *ofdev = cpm->ofdev;
+ struct platform_device *ofdev = cpm->ofdev;
const u32 *data;
int len, ret, i;
void __iomem *i2c_base;
@@ -634,7 +634,7 @@ static void cpm_i2c_shutdown(struct cpm_i2c *cpm)
cpm_muram_free(cpm->i2c_addr);
}
-static int __devinit cpm_i2c_probe(struct of_device *ofdev,
+static int __devinit cpm_i2c_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
int result, len;
@@ -687,7 +687,7 @@ out_free:
return result;
}
-static int __devexit cpm_i2c_remove(struct of_device *ofdev)
+static int __devexit cpm_i2c_remove(struct platform_device *ofdev)
{
struct cpm_i2c *cpm = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c
index 1168d61418c9..43ca32fddde2 100644
--- a/drivers/i2c/busses/i2c-ibm_iic.c
+++ b/drivers/i2c/busses/i2c-ibm_iic.c
@@ -661,7 +661,7 @@ static inline u8 iic_clckdiv(unsigned int opb)
return (u8)((opb + 9) / 10 - 1);
}
-static int __devinit iic_request_irq(struct of_device *ofdev,
+static int __devinit iic_request_irq(struct platform_device *ofdev,
struct ibm_iic_private *dev)
{
struct device_node *np = ofdev->dev.of_node;
@@ -692,7 +692,7 @@ static int __devinit iic_request_irq(struct of_device *ofdev,
/*
* Register single IIC interface
*/
-static int __devinit iic_probe(struct of_device *ofdev,
+static int __devinit iic_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -780,7 +780,7 @@ error_cleanup:
/*
* Cleanup initialized IIC interface
*/
-static int __devexit iic_remove(struct of_device *ofdev)
+static int __devexit iic_remove(struct platform_device *ofdev)
{
struct ibm_iic_private *dev = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index 6545d1c99b61..a1c419a716af 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -560,7 +560,7 @@ static struct i2c_adapter mpc_ops = {
.timeout = HZ,
};
-static int __devinit fsl_i2c_probe(struct of_device *op,
+static int __devinit fsl_i2c_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct mpc_i2c *i2c;
@@ -646,7 +646,7 @@ static int __devinit fsl_i2c_probe(struct of_device *op,
return result;
};
-static int __devexit fsl_i2c_remove(struct of_device *op)
+static int __devexit fsl_i2c_remove(struct platform_device *op)
{
struct mpc_i2c *i2c = dev_get_drvdata(&op->dev);
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index ffb405d7c6f2..598c49acaeb5 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -119,8 +119,10 @@ struct sh_mobile_i2c_data {
struct i2c_adapter adap;
struct clk *clk;
+ u_int8_t icic;
u_int8_t iccl;
u_int8_t icch;
+ u_int8_t flags;
spinlock_t lock;
wait_queue_head_t wait;
@@ -129,15 +131,17 @@ struct sh_mobile_i2c_data {
int sr;
};
+#define IIC_FLAG_HAS_ICIC67 (1 << 0)
+
#define NORMAL_SPEED 100000 /* FAST_SPEED 400000 */
/* Register offsets */
-#define ICDR(pd) (pd->reg + 0x00)
-#define ICCR(pd) (pd->reg + 0x04)
-#define ICSR(pd) (pd->reg + 0x08)
-#define ICIC(pd) (pd->reg + 0x0c)
-#define ICCL(pd) (pd->reg + 0x10)
-#define ICCH(pd) (pd->reg + 0x14)
+#define ICDR 0x00
+#define ICCR 0x04
+#define ICSR 0x08
+#define ICIC 0x0c
+#define ICCL 0x10
+#define ICCH 0x14
/* Register bits */
#define ICCR_ICE 0x80
@@ -155,11 +159,32 @@ struct sh_mobile_i2c_data {
#define ICSR_WAIT 0x02
#define ICSR_DTE 0x01
+#define ICIC_ICCLB8 0x80
+#define ICIC_ICCHB8 0x40
#define ICIC_ALE 0x08
#define ICIC_TACKE 0x04
#define ICIC_WAITE 0x02
#define ICIC_DTEE 0x01
+static void iic_wr(struct sh_mobile_i2c_data *pd, int offs, unsigned char data)
+{
+ if (offs == ICIC)
+ data |= pd->icic;
+
+ iowrite8(data, pd->reg + offs);
+}
+
+static unsigned char iic_rd(struct sh_mobile_i2c_data *pd, int offs)
+{
+ return ioread8(pd->reg + offs);
+}
+
+static void iic_set_clr(struct sh_mobile_i2c_data *pd, int offs,
+ unsigned char set, unsigned char clr)
+{
+ iic_wr(pd, offs, (iic_rd(pd, offs) | set) & ~clr);
+}
+
static void activate_ch(struct sh_mobile_i2c_data *pd)
{
unsigned long i2c_clk;
@@ -187,6 +212,14 @@ static void activate_ch(struct sh_mobile_i2c_data *pd)
else
pd->iccl = (u_int8_t)(num/denom);
+ /* one more bit of ICCL in ICIC */
+ if (pd->flags & IIC_FLAG_HAS_ICIC67) {
+ if ((num/denom) > 0xff)
+ pd->icic |= ICIC_ICCLB8;
+ else
+ pd->icic &= ~ICIC_ICCLB8;
+ }
+
/* Calculate the value for icch. From the data sheet:
icch = (p clock / transfer rate) * (H / (L + H)) */
num = i2c_clk * 4;
@@ -196,25 +229,33 @@ static void activate_ch(struct sh_mobile_i2c_data *pd)
else
pd->icch = (u_int8_t)(num/denom);
+ /* one more bit of ICCH in ICIC */
+ if (pd->flags & IIC_FLAG_HAS_ICIC67) {
+ if ((num/denom) > 0xff)
+ pd->icic |= ICIC_ICCHB8;
+ else
+ pd->icic &= ~ICIC_ICCHB8;
+ }
+
/* Enable channel and configure rx ack */
- iowrite8(ioread8(ICCR(pd)) | ICCR_ICE, ICCR(pd));
+ iic_set_clr(pd, ICCR, ICCR_ICE, 0);
/* Mask all interrupts */
- iowrite8(0, ICIC(pd));
+ iic_wr(pd, ICIC, 0);
/* Set the clock */
- iowrite8(pd->iccl, ICCL(pd));
- iowrite8(pd->icch, ICCH(pd));
+ iic_wr(pd, ICCL, pd->iccl);
+ iic_wr(pd, ICCH, pd->icch);
}
static void deactivate_ch(struct sh_mobile_i2c_data *pd)
{
/* Clear/disable interrupts */
- iowrite8(0, ICSR(pd));
- iowrite8(0, ICIC(pd));
+ iic_wr(pd, ICSR, 0);
+ iic_wr(pd, ICIC, 0);
/* Disable channel */
- iowrite8(ioread8(ICCR(pd)) & ~ICCR_ICE, ICCR(pd));
+ iic_set_clr(pd, ICCR, 0, ICCR_ICE);
/* Disable clock and mark device as idle */
clk_disable(pd->clk);
@@ -233,35 +274,35 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
switch (op) {
case OP_START: /* issue start and trigger DTE interrupt */
- iowrite8(0x94, ICCR(pd));
+ iic_wr(pd, ICCR, 0x94);
break;
case OP_TX_FIRST: /* disable DTE interrupt and write data */
- iowrite8(ICIC_WAITE | ICIC_ALE | ICIC_TACKE, ICIC(pd));
- iowrite8(data, ICDR(pd));
+ iic_wr(pd, ICIC, ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
+ iic_wr(pd, ICDR, data);
break;
case OP_TX: /* write data */
- iowrite8(data, ICDR(pd));
+ iic_wr(pd, ICDR, data);
break;
case OP_TX_STOP: /* write data and issue a stop afterwards */
- iowrite8(data, ICDR(pd));
- iowrite8(0x90, ICCR(pd));
+ iic_wr(pd, ICDR, data);
+ iic_wr(pd, ICCR, 0x90);
break;
case OP_TX_TO_RX: /* select read mode */
- iowrite8(0x81, ICCR(pd));
+ iic_wr(pd, ICCR, 0x81);
break;
case OP_RX: /* just read data */
- ret = ioread8(ICDR(pd));
+ ret = iic_rd(pd, ICDR);
break;
case OP_RX_STOP: /* enable DTE interrupt, issue stop */
- iowrite8(ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE,
- ICIC(pd));
- iowrite8(0xc0, ICCR(pd));
+ iic_wr(pd, ICIC,
+ ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
+ iic_wr(pd, ICCR, 0xc0);
break;
case OP_RX_STOP_DATA: /* enable DTE interrupt, read data, issue stop */
- iowrite8(ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE,
- ICIC(pd));
- ret = ioread8(ICDR(pd));
- iowrite8(0xc0, ICCR(pd));
+ iic_wr(pd, ICIC,
+ ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
+ ret = iic_rd(pd, ICDR);
+ iic_wr(pd, ICCR, 0xc0);
break;
}
@@ -367,7 +408,7 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
unsigned char sr;
int wakeup;
- sr = ioread8(ICSR(pd));
+ sr = iic_rd(pd, ICSR);
pd->sr |= sr; /* remember state */
dev_dbg(pd->dev, "i2c_isr 0x%02x 0x%02x %s %d %d!\n", sr, pd->sr,
@@ -376,7 +417,7 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
if (sr & (ICSR_AL | ICSR_TACK)) {
/* don't interrupt transaction - continue to issue stop */
- iowrite8(sr & ~(ICSR_AL | ICSR_TACK), ICSR(pd));
+ iic_wr(pd, ICSR, sr & ~(ICSR_AL | ICSR_TACK));
wakeup = 0;
} else if (pd->msg->flags & I2C_M_RD)
wakeup = sh_mobile_i2c_isr_rx(pd);
@@ -384,7 +425,7 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
wakeup = sh_mobile_i2c_isr_tx(pd);
if (sr & ICSR_WAIT) /* TODO: add delay here to support slow acks */
- iowrite8(sr & ~ICSR_WAIT, ICSR(pd));
+ iic_wr(pd, ICSR, sr & ~ICSR_WAIT);
if (wakeup) {
pd->sr |= SW_DONE;
@@ -402,21 +443,21 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg)
}
/* Initialize channel registers */
- iowrite8(ioread8(ICCR(pd)) & ~ICCR_ICE, ICCR(pd));
+ iic_set_clr(pd, ICCR, 0, ICCR_ICE);
/* Enable channel and configure rx ack */
- iowrite8(ioread8(ICCR(pd)) | ICCR_ICE, ICCR(pd));
+ iic_set_clr(pd, ICCR, ICCR_ICE, 0);
/* Set the clock */
- iowrite8(pd->iccl, ICCL(pd));
- iowrite8(pd->icch, ICCH(pd));
+ iic_wr(pd, ICCL, pd->iccl);
+ iic_wr(pd, ICCH, pd->icch);
pd->msg = usr_msg;
pd->pos = -1;
pd->sr = 0;
/* Enable all interrupts to begin with */
- iowrite8(ICIC_WAITE | ICIC_ALE | ICIC_TACKE | ICIC_DTEE, ICIC(pd));
+ iic_wr(pd, ICIC, ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
return 0;
}
@@ -451,7 +492,7 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
retry_count = 1000;
again:
- val = ioread8(ICSR(pd));
+ val = iic_rd(pd, ICSR);
dev_dbg(pd->dev, "val 0x%02x pd->sr 0x%02x\n", val, pd->sr);
@@ -576,6 +617,12 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
goto err_irq;
}
+ /* The IIC blocks on SH-Mobile ARM processors
+ * come with two new bits in ICIC.
+ */
+ if (size > 0x17)
+ pd->flags |= IIC_FLAG_HAS_ICIC67;
+
/* Enable Runtime PM for this device.
*
* Also tell the Runtime PM core to ignore children
diff --git a/drivers/infiniband/hw/ehca/ehca_classes.h b/drivers/infiniband/hw/ehca/ehca_classes.h
index 0136abd50dd4..aaf6023a4835 100644
--- a/drivers/infiniband/hw/ehca/ehca_classes.h
+++ b/drivers/infiniband/hw/ehca/ehca_classes.h
@@ -112,7 +112,7 @@ struct ehca_sport {
struct ehca_shca {
struct ib_device ib_device;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
u8 num_ports;
int hw_level;
struct list_head shca_list;
diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c
index cfc4de7a5da4..c240e9972cb0 100644
--- a/drivers/infiniband/hw/ehca/ehca_main.c
+++ b/drivers/infiniband/hw/ehca/ehca_main.c
@@ -713,7 +713,7 @@ static struct attribute_group ehca_dev_attr_grp = {
.attrs = ehca_dev_attrs
};
-static int __devinit ehca_probe(struct of_device *dev,
+static int __devinit ehca_probe(struct platform_device *dev,
const struct of_device_id *id)
{
struct ehca_shca *shca;
@@ -879,7 +879,7 @@ probe1:
return -EINVAL;
}
-static int __devexit ehca_remove(struct of_device *dev)
+static int __devexit ehca_remove(struct platform_device *dev)
{
struct ehca_shca *shca = dev_get_drvdata(&dev->dev);
unsigned long flags;
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index b171f63fe4d7..9cc488d21490 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -395,6 +395,16 @@ config KEYBOARD_SH_KEYSC
To compile this driver as a module, choose M here: the
module will be called sh_keysc.
+config KEYBOARD_STMPE
+ tristate "STMPE keypad support"
+ depends on MFD_STMPE
+ help
+ Say Y here if you want to use the keypad controller on STMPE I/O
+ expanders.
+
+ To compile this driver as a module, choose M here: the module will be
+ called stmpe-keypad.
+
config KEYBOARD_DAVINCI
tristate "TI DaVinci Key Scan"
depends on ARCH_DAVINCI_DM365
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 1a66d5f1ca8b..504b591be0cd 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o
obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
+obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c
new file mode 100644
index 000000000000..ab7610ca10eb
--- /dev/null
+++ b/drivers/input/keyboard/stmpe-keypad.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/mfd/stmpe.h>
+
+/* These are at the same addresses in all STMPE variants */
+#define STMPE_KPC_COL 0x60
+#define STMPE_KPC_ROW_MSB 0x61
+#define STMPE_KPC_ROW_LSB 0x62
+#define STMPE_KPC_CTRL_MSB 0x63
+#define STMPE_KPC_CTRL_LSB 0x64
+#define STMPE_KPC_COMBI_KEY_0 0x65
+#define STMPE_KPC_COMBI_KEY_1 0x66
+#define STMPE_KPC_COMBI_KEY_2 0x67
+#define STMPE_KPC_DATA_BYTE0 0x68
+#define STMPE_KPC_DATA_BYTE1 0x69
+#define STMPE_KPC_DATA_BYTE2 0x6a
+#define STMPE_KPC_DATA_BYTE3 0x6b
+#define STMPE_KPC_DATA_BYTE4 0x6c
+
+#define STMPE_KPC_CTRL_LSB_SCAN (0x1 << 0)
+#define STMPE_KPC_CTRL_LSB_DEBOUNCE (0x7f << 1)
+#define STMPE_KPC_CTRL_MSB_SCAN_COUNT (0xf << 4)
+
+#define STMPE_KPC_ROW_MSB_ROWS 0xff
+
+#define STMPE_KPC_DATA_UP (0x1 << 7)
+#define STMPE_KPC_DATA_ROW (0xf << 3)
+#define STMPE_KPC_DATA_COL (0x7 << 0)
+#define STMPE_KPC_DATA_NOKEY_MASK 0x78
+
+#define STMPE_KEYPAD_MAX_DEBOUNCE 127
+#define STMPE_KEYPAD_MAX_SCAN_COUNT 15
+
+#define STMPE_KEYPAD_MAX_ROWS 8
+#define STMPE_KEYPAD_MAX_COLS 8
+#define STMPE_KEYPAD_ROW_SHIFT 3
+#define STMPE_KEYPAD_KEYMAP_SIZE \
+ (STMPE_KEYPAD_MAX_ROWS * STMPE_KEYPAD_MAX_COLS)
+
+/**
+ * struct stmpe_keypad_variant - model-specific attributes
+ * @auto_increment: whether the KPC_DATA_BYTE register address
+ * auto-increments on multiple read
+ * @num_data: number of data bytes
+ * @num_normal_data: number of normal keys' data bytes
+ * @max_cols: maximum number of columns supported
+ * @max_rows: maximum number of rows supported
+ * @col_gpios: bitmask of gpios which can be used for columns
+ * @row_gpios: bitmask of gpios which can be used for rows
+ */
+struct stmpe_keypad_variant {
+ bool auto_increment;
+ int num_data;
+ int num_normal_data;
+ int max_cols;
+ int max_rows;
+ unsigned int col_gpios;
+ unsigned int row_gpios;
+};
+
+static const struct stmpe_keypad_variant stmpe_keypad_variants[] = {
+ [STMPE1601] = {
+ .auto_increment = true,
+ .num_data = 5,
+ .num_normal_data = 3,
+ .max_cols = 8,
+ .max_rows = 8,
+ .col_gpios = 0x000ff, /* GPIO 0 - 7 */
+ .row_gpios = 0x0ff00, /* GPIO 8 - 15 */
+ },
+ [STMPE2401] = {
+ .auto_increment = false,
+ .num_data = 3,
+ .num_normal_data = 2,
+ .max_cols = 8,
+ .max_rows = 12,
+ .col_gpios = 0x0000ff, /* GPIO 0 - 7*/
+ .row_gpios = 0x1fef00, /* GPIO 8-14, 16-20 */
+ },
+ [STMPE2403] = {
+ .auto_increment = true,
+ .num_data = 5,
+ .num_normal_data = 3,
+ .max_cols = 8,
+ .max_rows = 12,
+ .col_gpios = 0x0000ff, /* GPIO 0 - 7*/
+ .row_gpios = 0x1fef00, /* GPIO 8-14, 16-20 */
+ },
+};
+
+struct stmpe_keypad {
+ struct stmpe *stmpe;
+ struct input_dev *input;
+ const struct stmpe_keypad_variant *variant;
+ const struct stmpe_keypad_platform_data *plat;
+
+ unsigned int rows;
+ unsigned int cols;
+
+ unsigned short keymap[STMPE_KEYPAD_KEYMAP_SIZE];
+};
+
+static int stmpe_keypad_read_data(struct stmpe_keypad *keypad, u8 *data)
+{
+ const struct stmpe_keypad_variant *variant = keypad->variant;
+ struct stmpe *stmpe = keypad->stmpe;
+ int ret;
+ int i;
+
+ if (variant->auto_increment)
+ return stmpe_block_read(stmpe, STMPE_KPC_DATA_BYTE0,
+ variant->num_data, data);
+
+ for (i = 0; i < variant->num_data; i++) {
+ ret = stmpe_reg_read(stmpe, STMPE_KPC_DATA_BYTE0 + i);
+ if (ret < 0)
+ return ret;
+
+ data[i] = ret;
+ }
+
+ return 0;
+}
+
+static irqreturn_t stmpe_keypad_irq(int irq, void *dev)
+{
+ struct stmpe_keypad *keypad = dev;
+ struct input_dev *input = keypad->input;
+ const struct stmpe_keypad_variant *variant = keypad->variant;
+ u8 fifo[variant->num_data];
+ int ret;
+ int i;
+
+ ret = stmpe_keypad_read_data(keypad, fifo);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ for (i = 0; i < variant->num_normal_data; i++) {
+ u8 data = fifo[i];
+ int row = (data & STMPE_KPC_DATA_ROW) >> 3;
+ int col = data & STMPE_KPC_DATA_COL;
+ int code = MATRIX_SCAN_CODE(row, col, STMPE_KEYPAD_ROW_SHIFT);
+ bool up = data & STMPE_KPC_DATA_UP;
+
+ if ((data & STMPE_KPC_DATA_NOKEY_MASK)
+ == STMPE_KPC_DATA_NOKEY_MASK)
+ continue;
+
+ input_event(input, EV_MSC, MSC_SCAN, code);
+ input_report_key(input, keypad->keymap[code], !up);
+ input_sync(input);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit stmpe_keypad_altfunc_init(struct stmpe_keypad *keypad)
+{
+ const struct stmpe_keypad_variant *variant = keypad->variant;
+ unsigned int col_gpios = variant->col_gpios;
+ unsigned int row_gpios = variant->row_gpios;
+ struct stmpe *stmpe = keypad->stmpe;
+ unsigned int pins = 0;
+ int i;
+
+ /*
+ * Figure out which pins need to be set to the keypad alternate
+ * function.
+ *
+ * {cols,rows}_gpios are bitmasks of which pins on the chip can be used
+ * for the keypad.
+ *
+ * keypad->{cols,rows} are a bitmask of which pins (of the ones useable
+ * for the keypad) are used on the board.
+ */
+
+ for (i = 0; i < variant->max_cols; i++) {
+ int num = __ffs(col_gpios);
+
+ if (keypad->cols & (1 << i))
+ pins |= 1 << num;
+
+ col_gpios &= ~(1 << num);
+ }
+
+ for (i = 0; i < variant->max_rows; i++) {
+ int num = __ffs(row_gpios);
+
+ if (keypad->rows & (1 << i))
+ pins |= 1 << num;
+
+ row_gpios &= ~(1 << num);
+ }
+
+ return stmpe_set_altfunc(stmpe, pins, STMPE_BLOCK_KEYPAD);
+}
+
+static int __devinit stmpe_keypad_chip_init(struct stmpe_keypad *keypad)
+{
+ const struct stmpe_keypad_platform_data *plat = keypad->plat;
+ const struct stmpe_keypad_variant *variant = keypad->variant;
+ struct stmpe *stmpe = keypad->stmpe;
+ int ret;
+
+ if (plat->debounce_ms > STMPE_KEYPAD_MAX_DEBOUNCE)
+ return -EINVAL;
+
+ if (plat->scan_count > STMPE_KEYPAD_MAX_SCAN_COUNT)
+ return -EINVAL;
+
+ ret = stmpe_enable(stmpe, STMPE_BLOCK_KEYPAD);
+ if (ret < 0)
+ return ret;
+
+ ret = stmpe_keypad_altfunc_init(keypad);
+ if (ret < 0)
+ return ret;
+
+ ret = stmpe_reg_write(stmpe, STMPE_KPC_COL, keypad->cols);
+ if (ret < 0)
+ return ret;
+
+ ret = stmpe_reg_write(stmpe, STMPE_KPC_ROW_LSB, keypad->rows);
+ if (ret < 0)
+ return ret;
+
+ if (variant->max_rows > 8) {
+ ret = stmpe_set_bits(stmpe, STMPE_KPC_ROW_MSB,
+ STMPE_KPC_ROW_MSB_ROWS,
+ keypad->rows >> 8);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_KPC_CTRL_MSB,
+ STMPE_KPC_CTRL_MSB_SCAN_COUNT,
+ plat->scan_count << 4);
+ if (ret < 0)
+ return ret;
+
+ return stmpe_set_bits(stmpe, STMPE_KPC_CTRL_LSB,
+ STMPE_KPC_CTRL_LSB_SCAN |
+ STMPE_KPC_CTRL_LSB_DEBOUNCE,
+ STMPE_KPC_CTRL_LSB_SCAN |
+ (plat->debounce_ms << 1));
+}
+
+static int __devinit stmpe_keypad_probe(struct platform_device *pdev)
+{
+ struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+ struct stmpe_keypad_platform_data *plat;
+ struct stmpe_keypad *keypad;
+ struct input_dev *input;
+ int ret;
+ int irq;
+ int i;
+
+ plat = stmpe->pdata->keypad;
+ if (!plat)
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ keypad = kzalloc(sizeof(struct stmpe_keypad), GFP_KERNEL);
+ if (!keypad)
+ return -ENOMEM;
+
+ input = input_allocate_device();
+ if (!input) {
+ ret = -ENOMEM;
+ goto out_freekeypad;
+ }
+
+ input->name = "STMPE keypad";
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &pdev->dev;
+
+ input_set_capability(input, EV_MSC, MSC_SCAN);
+
+ __set_bit(EV_KEY, input->evbit);
+ if (!plat->no_autorepeat)
+ __set_bit(EV_REP, input->evbit);
+
+ input->keycode = keypad->keymap;
+ input->keycodesize = sizeof(keypad->keymap[0]);
+ input->keycodemax = ARRAY_SIZE(keypad->keymap);
+
+ matrix_keypad_build_keymap(plat->keymap_data, STMPE_KEYPAD_ROW_SHIFT,
+ input->keycode, input->keybit);
+
+ for (i = 0; i < plat->keymap_data->keymap_size; i++) {
+ unsigned int key = plat->keymap_data->keymap[i];
+
+ keypad->cols |= 1 << KEY_COL(key);
+ keypad->rows |= 1 << KEY_ROW(key);
+ }
+
+ keypad->stmpe = stmpe;
+ keypad->plat = plat;
+ keypad->input = input;
+ keypad->variant = &stmpe_keypad_variants[stmpe->partnum];
+
+ ret = stmpe_keypad_chip_init(keypad);
+ if (ret < 0)
+ goto out_freeinput;
+
+ ret = input_register_device(input);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "unable to register input device: %d\n", ret);
+ goto out_freeinput;
+ }
+
+ ret = request_threaded_irq(irq, NULL, stmpe_keypad_irq, IRQF_ONESHOT,
+ "stmpe-keypad", keypad);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
+ goto out_unregisterinput;
+ }
+
+ platform_set_drvdata(pdev, keypad);
+
+ return 0;
+
+out_unregisterinput:
+ input_unregister_device(input);
+ input = NULL;
+out_freeinput:
+ input_free_device(input);
+out_freekeypad:
+ kfree(keypad);
+ return ret;
+}
+
+static int __devexit stmpe_keypad_remove(struct platform_device *pdev)
+{
+ struct stmpe_keypad *keypad = platform_get_drvdata(pdev);
+ struct stmpe *stmpe = keypad->stmpe;
+ int irq = platform_get_irq(pdev, 0);
+
+ stmpe_disable(stmpe, STMPE_BLOCK_KEYPAD);
+
+ free_irq(irq, keypad);
+ input_unregister_device(keypad->input);
+ platform_set_drvdata(pdev, NULL);
+ kfree(keypad);
+
+ return 0;
+}
+
+static struct platform_driver stmpe_keypad_driver = {
+ .driver.name = "stmpe-keypad",
+ .driver.owner = THIS_MODULE,
+ .probe = stmpe_keypad_probe,
+ .remove = __devexit_p(stmpe_keypad_remove),
+};
+
+static int __init stmpe_keypad_init(void)
+{
+ return platform_driver_register(&stmpe_keypad_driver);
+}
+module_init(stmpe_keypad_init);
+
+static void __exit stmpe_keypad_exit(void)
+{
+ platform_driver_unregister(&stmpe_keypad_driver);
+}
+module_exit(stmpe_keypad_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMPExxxx keypad driver");
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c
index f3bb92e9755f..8e130bf7d32b 100644
--- a/drivers/input/misc/sparcspkr.c
+++ b/drivers/input/misc/sparcspkr.c
@@ -173,7 +173,7 @@ static int __devinit sparcspkr_probe(struct device *dev)
return 0;
}
-static int sparcspkr_shutdown(struct of_device *dev)
+static int sparcspkr_shutdown(struct platform_device *dev)
{
struct sparcspkr_state *state = dev_get_drvdata(&dev->dev);
struct input_dev *input_dev = state->input_dev;
@@ -184,7 +184,7 @@ static int sparcspkr_shutdown(struct of_device *dev)
return 0;
}
-static int __devinit bbc_beep_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit bbc_beep_probe(struct platform_device *op, const struct of_device_id *match)
{
struct sparcspkr_state *state;
struct bbc_beep_info *info;
@@ -231,7 +231,7 @@ out_err:
return err;
}
-static int __devexit bbc_remove(struct of_device *op)
+static int __devexit bbc_remove(struct platform_device *op)
{
struct sparcspkr_state *state = dev_get_drvdata(&op->dev);
struct input_dev *input_dev = state->input_dev;
@@ -269,7 +269,7 @@ static struct of_platform_driver bbc_beep_driver = {
.shutdown = sparcspkr_shutdown,
};
-static int __devinit grover_beep_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit grover_beep_probe(struct platform_device *op, const struct of_device_id *match)
{
struct sparcspkr_state *state;
struct grover_beep_info *info;
@@ -312,7 +312,7 @@ out_err:
return err;
}
-static int __devexit grover_remove(struct of_device *op)
+static int __devexit grover_remove(struct platform_device *op)
{
struct sparcspkr_state *state = dev_get_drvdata(&op->dev);
struct grover_beep_info *info = &state->u.grover;
diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h
index cb2a24b94746..c5cc4508d6df 100644
--- a/drivers/input/serio/i8042-sparcio.h
+++ b/drivers/input/serio/i8042-sparcio.h
@@ -49,7 +49,7 @@ static inline void i8042_write_command(int val)
#define OBP_PS2MS_NAME1 "kdmouse"
#define OBP_PS2MS_NAME2 "mouse"
-static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit sparc_i8042_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -57,7 +57,7 @@ static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_dev
while (dp) {
if (!strcmp(dp->name, OBP_PS2KBD_NAME1) ||
!strcmp(dp->name, OBP_PS2KBD_NAME2)) {
- struct of_device *kbd = of_find_device_by_node(dp);
+ struct platform_device *kbd = of_find_device_by_node(dp);
unsigned int irq = kbd->archdata.irqs[0];
if (irq == 0xffffffff)
irq = op->archdata.irqs[0];
@@ -67,7 +67,7 @@ static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_dev
kbd_res = &kbd->resource[0];
} else if (!strcmp(dp->name, OBP_PS2MS_NAME1) ||
!strcmp(dp->name, OBP_PS2MS_NAME2)) {
- struct of_device *ms = of_find_device_by_node(dp);
+ struct platform_device *ms = of_find_device_by_node(dp);
unsigned int irq = ms->archdata.irqs[0];
if (irq == 0xffffffff)
irq = op->archdata.irqs[0];
@@ -80,7 +80,7 @@ static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_dev
return 0;
}
-static int __devexit sparc_i8042_remove(struct of_device *op)
+static int __devexit sparc_i8042_remove(struct platform_device *op)
{
of_iounmap(kbd_res, kbd_iobase, 8);
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 258b98b9d7c2..46e4ba0b9246 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -61,10 +61,6 @@ static bool i8042_noloop;
module_param_named(noloop, i8042_noloop, bool, 0);
MODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port");
-static unsigned int i8042_blink_frequency = 500;
-module_param_named(panicblink, i8042_blink_frequency, uint, 0600);
-MODULE_PARM_DESC(panicblink, "Frequency with which keyboard LEDs should blink when kernel panics");
-
#ifdef CONFIG_X86
static bool i8042_dritek;
module_param_named(dritek, i8042_dritek, bool, 0);
@@ -1030,8 +1026,8 @@ static void i8042_controller_reset(void)
/*
- * i8042_panic_blink() will flash the keyboard LEDs and is called when
- * kernel panics. Flashing LEDs is useful for users running X who may
+ * i8042_panic_blink() will turn the keyboard LEDs on or off and is called
+ * when kernel panics. Flashing LEDs is useful for users running X who may
* not see the console and will help distingushing panics from "real"
* lockups.
*
@@ -1041,22 +1037,12 @@ static void i8042_controller_reset(void)
#define DELAY do { mdelay(1); if (++delay > 10) return delay; } while(0)
-static long i8042_panic_blink(long count)
+static long i8042_panic_blink(int state)
{
long delay = 0;
- static long last_blink;
- static char led;
-
- /*
- * We expect frequency to be about 1/2s. KDB uses about 1s.
- * Make sure they are different.
- */
- if (!i8042_blink_frequency)
- return 0;
- if (count - last_blink < i8042_blink_frequency)
- return 0;
+ char led;
- led ^= 0x01 | 0x04;
+ led = (state) ? 0x01 | 0x04 : 0;
while (i8042_read_status() & I8042_STR_IBF)
DELAY;
dbg("%02x -> i8042 (panic blink)", 0xed);
@@ -1069,7 +1055,6 @@ static long i8042_panic_blink(long count)
dbg("%02x -> i8042 (panic blink)", led);
i8042_write_data(led);
DELAY;
- last_blink = count;
return delay;
}
diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c
index e2c028d2638f..bb14449fb022 100644
--- a/drivers/input/serio/xilinx_ps2.c
+++ b/drivers/input/serio/xilinx_ps2.c
@@ -232,7 +232,7 @@ static void sxps2_close(struct serio *pserio)
* It returns 0, if the driver is bound to the PS/2 device, or a negative
* value if there is an error.
*/
-static int __devinit xps2_of_probe(struct of_device *ofdev,
+static int __devinit xps2_of_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct resource r_irq; /* Interrupt resources */
@@ -332,7 +332,7 @@ failed1:
* if the driver module is being unloaded. It frees any resources allocated to
* the device.
*/
-static int __devexit xps2_of_remove(struct of_device *of_dev)
+static int __devexit xps2_of_remove(struct platform_device *of_dev)
{
struct device *dev = &of_dev->dev;
struct xps2data *drvdata = dev_get_drvdata(dev);
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 61f35184f76c..0069d9703fda 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -628,4 +628,14 @@ config TOUCHSCREEN_TPS6507X
To compile this driver as a module, choose M here: the
module will be called tps6507x_ts.
+config TOUCHSCREEN_STMPE
+ tristate "STMicroelectronics STMPE touchscreens"
+ depends on MFD_STMPE
+ help
+ Say Y here if you want support for STMicroelectronics
+ STMPE touchscreen controllers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stmpe-ts.
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index bd6f30b4ff70..28217e1dcafd 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
obj-$(CONFIG_TOUCHSCREEN_QT602240) += qt602240_ts.o
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
+obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c
new file mode 100644
index 000000000000..656148ec0027
--- /dev/null
+++ b/drivers/input/touchscreen/stmpe-ts.c
@@ -0,0 +1,397 @@
+/* STMicroelectronics STMPE811 Touchscreen Driver
+ *
+ * (C) 2010 Luotao Fu <l.fu@pengutronix.de>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/stmpe.h>
+
+/* Register layouts and functionalities are identical on all stmpexxx variants
+ * with touchscreen controller
+ */
+#define STMPE_REG_INT_STA 0x0B
+#define STMPE_REG_ADC_CTRL1 0x20
+#define STMPE_REG_ADC_CTRL2 0x21
+#define STMPE_REG_TSC_CTRL 0x40
+#define STMPE_REG_TSC_CFG 0x41
+#define STMPE_REG_FIFO_TH 0x4A
+#define STMPE_REG_FIFO_STA 0x4B
+#define STMPE_REG_FIFO_SIZE 0x4C
+#define STMPE_REG_TSC_DATA_XYZ 0x52
+#define STMPE_REG_TSC_FRACTION_Z 0x56
+#define STMPE_REG_TSC_I_DRIVE 0x58
+
+#define OP_MOD_XYZ 0
+
+#define STMPE_TSC_CTRL_TSC_EN (1<<0)
+
+#define STMPE_FIFO_STA_RESET (1<<0)
+
+#define STMPE_IRQ_TOUCH_DET 0
+
+#define SAMPLE_TIME(x) ((x & 0xf) << 4)
+#define MOD_12B(x) ((x & 0x1) << 3)
+#define REF_SEL(x) ((x & 0x1) << 1)
+#define ADC_FREQ(x) (x & 0x3)
+#define AVE_CTRL(x) ((x & 0x3) << 6)
+#define DET_DELAY(x) ((x & 0x7) << 3)
+#define SETTLING(x) (x & 0x7)
+#define FRACTION_Z(x) (x & 0x7)
+#define I_DRIVE(x) (x & 0x1)
+#define OP_MODE(x) ((x & 0x7) << 1)
+
+#define STMPE_TS_NAME "stmpe-ts"
+#define XY_MASK 0xfff
+
+struct stmpe_touch {
+ struct stmpe *stmpe;
+ struct input_dev *idev;
+ struct delayed_work work;
+ struct device *dev;
+ u8 sample_time;
+ u8 mod_12b;
+ u8 ref_sel;
+ u8 adc_freq;
+ u8 ave_ctrl;
+ u8 touch_det_delay;
+ u8 settling;
+ u8 fraction_z;
+ u8 i_drive;
+};
+
+static int __stmpe_reset_fifo(struct stmpe *stmpe)
+{
+ int ret;
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_FIFO_STA,
+ STMPE_FIFO_STA_RESET, STMPE_FIFO_STA_RESET);
+ if (ret)
+ return ret;
+
+ return stmpe_set_bits(stmpe, STMPE_REG_FIFO_STA,
+ STMPE_FIFO_STA_RESET, 0);
+}
+
+static void stmpe_work(struct work_struct *work)
+{
+ int int_sta;
+ u32 timeout = 40;
+
+ struct stmpe_touch *ts =
+ container_of(work, struct stmpe_touch, work.work);
+
+ int_sta = stmpe_reg_read(ts->stmpe, STMPE_REG_INT_STA);
+
+ /*
+ * touch_det sometimes get desasserted or just get stuck. This appears
+ * to be a silicon bug, We still have to clearify this with the
+ * manufacture. As a workaround We release the key anyway if the
+ * touch_det keeps coming in after 4ms, while the FIFO contains no value
+ * during the whole time.
+ */
+ while ((int_sta & (1 << STMPE_IRQ_TOUCH_DET)) && (timeout > 0)) {
+ timeout--;
+ int_sta = stmpe_reg_read(ts->stmpe, STMPE_REG_INT_STA);
+ udelay(100);
+ }
+
+ /* reset the FIFO before we report release event */
+ __stmpe_reset_fifo(ts->stmpe);
+
+ input_report_abs(ts->idev, ABS_PRESSURE, 0);
+ input_sync(ts->idev);
+}
+
+static irqreturn_t stmpe_ts_handler(int irq, void *data)
+{
+ u8 data_set[4];
+ int x, y, z;
+ struct stmpe_touch *ts = data;
+
+ /*
+ * Cancel scheduled polling for release if we have new value
+ * available. Wait if the polling is already running.
+ */
+ cancel_delayed_work_sync(&ts->work);
+
+ /*
+ * The FIFO sometimes just crashes and stops generating interrupts. This
+ * appears to be a silicon bug. We still have to clearify this with
+ * the manufacture. As a workaround we disable the TSC while we are
+ * collecting data and flush the FIFO after reading
+ */
+ stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+ STMPE_TSC_CTRL_TSC_EN, 0);
+
+ stmpe_block_read(ts->stmpe, STMPE_REG_TSC_DATA_XYZ, 4, data_set);
+
+ x = (data_set[0] << 4) | (data_set[1] >> 4);
+ y = ((data_set[1] & 0xf) << 8) | data_set[2];
+ z = data_set[3];
+
+ input_report_abs(ts->idev, ABS_X, x);
+ input_report_abs(ts->idev, ABS_Y, y);
+ input_report_abs(ts->idev, ABS_PRESSURE, z);
+ input_sync(ts->idev);
+
+ /* flush the FIFO after we have read out our values. */
+ __stmpe_reset_fifo(ts->stmpe);
+
+ /* reenable the tsc */
+ stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+ STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN);
+
+ /* start polling for touch_det to detect release */
+ schedule_delayed_work(&ts->work, HZ / 50);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit stmpe_init_hw(struct stmpe_touch *ts)
+{
+ int ret;
+ u8 adc_ctrl1, adc_ctrl1_mask, tsc_cfg, tsc_cfg_mask;
+ struct stmpe *stmpe = ts->stmpe;
+ struct device *dev = ts->dev;
+
+ ret = stmpe_enable(stmpe, STMPE_BLOCK_TOUCHSCREEN | STMPE_BLOCK_ADC);
+ if (ret) {
+ dev_err(dev, "Could not enable clock for ADC and TS\n");
+ return ret;
+ }
+
+ adc_ctrl1 = SAMPLE_TIME(ts->sample_time) | MOD_12B(ts->mod_12b) |
+ REF_SEL(ts->ref_sel);
+ adc_ctrl1_mask = SAMPLE_TIME(0xff) | MOD_12B(0xff) | REF_SEL(0xff);
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL1,
+ adc_ctrl1_mask, adc_ctrl1);
+ if (ret) {
+ dev_err(dev, "Could not setup ADC\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_ADC_CTRL2,
+ ADC_FREQ(0xff), ADC_FREQ(ts->adc_freq));
+ if (ret) {
+ dev_err(dev, "Could not setup ADC\n");
+ return ret;
+ }
+
+ tsc_cfg = AVE_CTRL(ts->ave_ctrl) | DET_DELAY(ts->touch_det_delay) |
+ SETTLING(ts->settling);
+ tsc_cfg_mask = AVE_CTRL(0xff) | DET_DELAY(0xff) | SETTLING(0xff);
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CFG, tsc_cfg_mask, tsc_cfg);
+ if (ret) {
+ dev_err(dev, "Could not config touch\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_FRACTION_Z,
+ FRACTION_Z(0xff), FRACTION_Z(ts->fraction_z));
+ if (ret) {
+ dev_err(dev, "Could not config touch\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_I_DRIVE,
+ I_DRIVE(0xff), I_DRIVE(ts->i_drive));
+ if (ret) {
+ dev_err(dev, "Could not config touch\n");
+ return ret;
+ }
+
+ /* set FIFO to 1 for single point reading */
+ ret = stmpe_reg_write(stmpe, STMPE_REG_FIFO_TH, 1);
+ if (ret) {
+ dev_err(dev, "Could not set FIFO\n");
+ return ret;
+ }
+
+ ret = stmpe_set_bits(stmpe, STMPE_REG_TSC_CTRL,
+ OP_MODE(0xff), OP_MODE(OP_MOD_XYZ));
+ if (ret) {
+ dev_err(dev, "Could not set mode\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int stmpe_ts_open(struct input_dev *dev)
+{
+ struct stmpe_touch *ts = input_get_drvdata(dev);
+ int ret = 0;
+
+ ret = __stmpe_reset_fifo(ts->stmpe);
+ if (ret)
+ return ret;
+
+ return stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+ STMPE_TSC_CTRL_TSC_EN, STMPE_TSC_CTRL_TSC_EN);
+}
+
+static void stmpe_ts_close(struct input_dev *dev)
+{
+ struct stmpe_touch *ts = input_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&ts->work);
+
+ stmpe_set_bits(ts->stmpe, STMPE_REG_TSC_CTRL,
+ STMPE_TSC_CTRL_TSC_EN, 0);
+}
+
+static int __devinit stmpe_input_probe(struct platform_device *pdev)
+{
+ struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+ struct stmpe_platform_data *pdata = stmpe->pdata;
+ struct stmpe_touch *ts;
+ struct input_dev *idev;
+ struct stmpe_ts_platform_data *ts_pdata = NULL;
+ int ret = 0;
+ int ts_irq;
+
+ ts_irq = platform_get_irq_byname(pdev, "FIFO_TH");
+ if (ts_irq < 0)
+ return ts_irq;
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ goto err_out;
+
+ idev = input_allocate_device();
+ if (!idev)
+ goto err_free_ts;
+
+ platform_set_drvdata(pdev, ts);
+ ts->stmpe = stmpe;
+ ts->idev = idev;
+ ts->dev = &pdev->dev;
+
+ if (pdata)
+ ts_pdata = pdata->ts;
+
+ if (ts_pdata) {
+ ts->sample_time = ts_pdata->sample_time;
+ ts->mod_12b = ts_pdata->mod_12b;
+ ts->ref_sel = ts_pdata->ref_sel;
+ ts->adc_freq = ts_pdata->adc_freq;
+ ts->ave_ctrl = ts_pdata->ave_ctrl;
+ ts->touch_det_delay = ts_pdata->touch_det_delay;
+ ts->settling = ts_pdata->settling;
+ ts->fraction_z = ts_pdata->fraction_z;
+ ts->i_drive = ts_pdata->i_drive;
+ }
+
+ INIT_DELAYED_WORK(&ts->work, stmpe_work);
+
+ ret = request_threaded_irq(ts_irq, NULL, stmpe_ts_handler,
+ IRQF_ONESHOT, STMPE_TS_NAME, ts);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request IRQ %d\n", ts_irq);
+ goto err_free_input;
+ }
+
+ ret = stmpe_init_hw(ts);
+ if (ret)
+ goto err_free_irq;
+
+ idev->name = STMPE_TS_NAME;
+ idev->id.bustype = BUS_I2C;
+ idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ idev->open = stmpe_ts_open;
+ idev->close = stmpe_ts_close;
+
+ input_set_drvdata(idev, ts);
+
+ input_set_abs_params(idev, ABS_X, 0, XY_MASK, 0, 0);
+ input_set_abs_params(idev, ABS_Y, 0, XY_MASK, 0, 0);
+ input_set_abs_params(idev, ABS_PRESSURE, 0x0, 0xff, 0, 0);
+
+ ret = input_register_device(idev);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register input device\n");
+ goto err_free_irq;
+ }
+
+ return ret;
+
+err_free_irq:
+ free_irq(ts_irq, ts);
+err_free_input:
+ input_free_device(idev);
+ platform_set_drvdata(pdev, NULL);
+err_free_ts:
+ kfree(ts);
+err_out:
+ return ret;
+}
+
+static int __devexit stmpe_ts_remove(struct platform_device *pdev)
+{
+ struct stmpe_touch *ts = platform_get_drvdata(pdev);
+ unsigned int ts_irq = platform_get_irq_byname(pdev, "FIFO_TH");
+
+ stmpe_disable(ts->stmpe, STMPE_BLOCK_TOUCHSCREEN);
+
+ free_irq(ts_irq, ts);
+
+ platform_set_drvdata(pdev, NULL);
+
+ input_unregister_device(ts->idev);
+ input_free_device(ts->idev);
+
+ kfree(ts);
+
+ return 0;
+}
+
+static struct platform_driver stmpe_ts_driver = {
+ .driver = {
+ .name = STMPE_TS_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = stmpe_input_probe,
+ .remove = __devexit_p(stmpe_ts_remove),
+};
+
+static int __init stmpe_ts_init(void)
+{
+ return platform_driver_register(&stmpe_ts_driver);
+}
+
+module_init(stmpe_ts_init);
+
+static void __exit stmpe_ts_exit(void)
+{
+ platform_driver_unregister(&stmpe_ts_driver);
+}
+
+module_exit(stmpe_ts_exit);
+
+MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
+MODULE_DESCRIPTION("STMPEXXX touchscreen driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" STMPE_TS_NAME);
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index cc22eeefa10b..ea57e05d08f3 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -224,7 +224,7 @@ struct gpio_led_of_platform_data {
struct gpio_led_data led_data[];
};
-static int __devinit of_gpio_leds_probe(struct of_device *ofdev,
+static int __devinit of_gpio_leds_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node, *child;
@@ -283,7 +283,7 @@ err:
return ret;
}
-static int __devexit of_gpio_leds_remove(struct of_device *ofdev)
+static int __devexit of_gpio_leds_remove(struct platform_device *ofdev)
{
struct gpio_led_of_platform_data *pdata = dev_get_drvdata(&ofdev->dev);
int i;
diff --git a/drivers/macintosh/macio_sysfs.c b/drivers/macintosh/macio_sysfs.c
index 6024038a5b9d..8eb40afbd0f5 100644
--- a/drivers/macintosh/macio_sysfs.c
+++ b/drivers/macintosh/macio_sysfs.c
@@ -15,7 +15,7 @@ field##_show (struct device *dev, struct device_attribute *attr, \
static ssize_t
compatible_show (struct device *dev, struct device_attribute *attr, char *buf)
{
- struct of_device *of;
+ struct platform_device *of;
const char *compat;
int cplen;
int length = 0;
@@ -52,9 +52,9 @@ static ssize_t modalias_show (struct device *dev, struct device_attribute *attr,
static ssize_t devspec_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct of_device *ofdev;
+ struct platform_device *ofdev;
- ofdev = to_of_device(dev);
+ ofdev = to_platform_device(dev);
return sprintf(buf, "%s\n", ofdev->dev.of_node->full_name);
}
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c
index 2506c957712e..e58c3d33e035 100644
--- a/drivers/macintosh/smu.c
+++ b/drivers/macintosh/smu.c
@@ -75,7 +75,7 @@ struct smu_cmd_buf {
struct smu_device {
spinlock_t lock;
struct device_node *of_node;
- struct of_device *of_dev;
+ struct platform_device *of_dev;
int doorbell; /* doorbell gpio */
u32 __iomem *db_buf; /* doorbell buffer */
struct device_node *db_node;
@@ -645,7 +645,7 @@ static void smu_expose_childs(struct work_struct *unused)
static DECLARE_WORK(smu_expose_childs_work, smu_expose_childs);
-static int smu_platform_probe(struct of_device* dev,
+static int smu_platform_probe(struct platform_device* dev,
const struct of_device_id *match)
{
if (!smu)
@@ -695,7 +695,7 @@ static int __init smu_init_sysfs(void)
device_initcall(smu_init_sysfs);
-struct of_device *smu_get_ofdev(void)
+struct platform_device *smu_get_ofdev(void)
{
if (!smu)
return NULL;
diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c
index c42eeb43042d..d0d221332db0 100644
--- a/drivers/macintosh/therm_adt746x.c
+++ b/drivers/macintosh/therm_adt746x.c
@@ -84,7 +84,7 @@ struct thermostat {
static enum {ADT7460, ADT7467} therm_type;
static int therm_bus, therm_address;
-static struct of_device * of_dev;
+static struct platform_device * of_dev;
static struct thermostat* thermostat;
static struct task_struct *thread_therm = NULL;
diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c
index e60605bd0ea9..44549272333c 100644
--- a/drivers/macintosh/therm_pm72.c
+++ b/drivers/macintosh/therm_pm72.c
@@ -148,7 +148,7 @@
* Driver statics
*/
-static struct of_device * of_dev;
+static struct platform_device * of_dev;
static struct i2c_adapter * u3_0;
static struct i2c_adapter * u3_1;
static struct i2c_adapter * k2;
@@ -2210,7 +2210,7 @@ static void fcu_lookup_fans(struct device_node *fcu_node)
}
}
-static int fcu_of_probe(struct of_device* dev, const struct of_device_id *match)
+static int fcu_of_probe(struct platform_device* dev, const struct of_device_id *match)
{
state = state_detached;
@@ -2221,7 +2221,7 @@ static int fcu_of_probe(struct of_device* dev, const struct of_device_id *match)
return i2c_add_driver(&therm_pm72_driver);
}
-static int fcu_of_remove(struct of_device* dev)
+static int fcu_of_remove(struct platform_device* dev)
{
i2c_del_driver(&therm_pm72_driver);
diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c
index 5c9367acf0cf..133f195de1fd 100644
--- a/drivers/macintosh/therm_windtunnel.c
+++ b/drivers/macintosh/therm_windtunnel.c
@@ -52,7 +52,7 @@ static struct {
struct task_struct *poll_task;
struct mutex lock;
- struct of_device *of_dev;
+ struct platform_device *of_dev;
struct i2c_client *thermostat;
struct i2c_client *fan;
@@ -444,13 +444,13 @@ static struct i2c_driver g4fan_driver = {
/************************************************************************/
static int
-therm_of_probe( struct of_device *dev, const struct of_device_id *match )
+therm_of_probe( struct platform_device *dev, const struct of_device_id *match )
{
return i2c_add_driver( &g4fan_driver );
}
static int
-therm_of_remove( struct of_device *dev )
+therm_of_remove( struct platform_device *dev )
{
i2c_del_driver( &g4fan_driver );
return 0;
diff --git a/drivers/media/video/fsl-viu.c b/drivers/media/video/fsl-viu.c
index 8f1c94f7e00c..43d208f1f586 100644
--- a/drivers/media/video/fsl-viu.c
+++ b/drivers/media/video/fsl-viu.c
@@ -1418,7 +1418,7 @@ static struct video_device viu_template = {
.current_norm = V4L2_STD_NTSC_M,
};
-static int __devinit viu_of_probe(struct of_device *op,
+static int __devinit viu_of_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct viu_dev *viu_dev;
@@ -1549,7 +1549,7 @@ err:
return ret;
}
-static int __devexit viu_of_remove(struct of_device *op)
+static int __devexit viu_of_remove(struct platform_device *op)
{
struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
@@ -1570,7 +1570,7 @@ static int __devexit viu_of_remove(struct of_device *op)
}
#ifdef CONFIG_PM
-static int viu_suspend(struct of_device *op, pm_message_t state)
+static int viu_suspend(struct platform_device *op, pm_message_t state)
{
struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
@@ -1579,7 +1579,7 @@ static int viu_suspend(struct of_device *op, pm_message_t state)
return 0;
}
-static int viu_resume(struct of_device *op)
+static int viu_resume(struct platform_device *op)
{
struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c
index eef78a068fd1..d3f1a087eced 100644
--- a/drivers/memstick/core/mspro_block.c
+++ b/drivers/memstick/core/mspro_block.c
@@ -1048,6 +1048,7 @@ static int mspro_block_read_attributes(struct memstick_dev *card)
snprintf(s_attr->name, sizeof(s_attr->name),
"attr_x%02x", attr->entries[cnt].id);
+ sysfs_attr_init(&s_attr->dev_attr.attr);
s_attr->dev_attr.attr.name = s_attr->name;
s_attr->dev_attr.attr.mode = S_IRUGO;
s_attr->dev_attr.show = mspro_block_attr_show(s_attr->id);
@@ -1338,13 +1339,14 @@ static void mspro_block_remove(struct memstick_dev *card)
struct mspro_block_data *msb = memstick_get_drvdata(card);
unsigned long flags;
- del_gendisk(msb->disk);
- dev_dbg(&card->dev, "mspro block remove\n");
spin_lock_irqsave(&msb->q_lock, flags);
msb->eject = 1;
blk_start_queue(msb->queue);
spin_unlock_irqrestore(&msb->q_lock, flags);
+ del_gendisk(msb->disk);
+ dev_dbg(&card->dev, "mspro block remove\n");
+
blk_cleanup_queue(msb->queue);
msb->queue = NULL;
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index d8c668d64418..b8f1719d7c02 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -50,6 +50,7 @@
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/init.h>
+#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/pci.h>
@@ -199,12 +200,9 @@ static int mpt_host_page_access_control(MPT_ADAPTER *ioc, u8 access_control_valu
static int mpt_host_page_alloc(MPT_ADAPTER *ioc, pIOCInit_t ioc_init);
#ifdef CONFIG_PROC_FS
-static int procmpt_summary_read(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
-static int procmpt_version_read(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
-static int procmpt_iocinfo_read(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
+static const struct file_operations mpt_summary_proc_fops;
+static const struct file_operations mpt_version_proc_fops;
+static const struct file_operations mpt_iocinfo_proc_fops;
#endif
static void mpt_get_fw_exp_ver(char *buf, MPT_ADAPTER *ioc);
@@ -1724,7 +1722,7 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
u8 pcixcmd;
static int mpt_ids = 0;
#ifdef CONFIG_PROC_FS
- struct proc_dir_entry *dent, *ent;
+ struct proc_dir_entry *dent;
#endif
ioc = kzalloc(sizeof(MPT_ADAPTER), GFP_ATOMIC);
@@ -1979,16 +1977,8 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
*/
dent = proc_mkdir(ioc->name, mpt_proc_root_dir);
if (dent) {
- ent = create_proc_entry("info", S_IFREG|S_IRUGO, dent);
- if (ent) {
- ent->read_proc = procmpt_iocinfo_read;
- ent->data = ioc;
- }
- ent = create_proc_entry("summary", S_IFREG|S_IRUGO, dent);
- if (ent) {
- ent->read_proc = procmpt_summary_read;
- ent->data = ioc;
- }
+ proc_create_data("info", S_IRUGO, dent, &mpt_iocinfo_proc_fops, ioc);
+ proc_create_data("summary", S_IRUGO, dent, &mpt_summary_proc_fops, ioc);
}
#endif
@@ -6545,20 +6535,12 @@ mpt_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
static int
procmpt_create(void)
{
- struct proc_dir_entry *ent;
-
mpt_proc_root_dir = proc_mkdir(MPT_PROCFS_MPTBASEDIR, NULL);
if (mpt_proc_root_dir == NULL)
return -ENOTDIR;
- ent = create_proc_entry("summary", S_IFREG|S_IRUGO, mpt_proc_root_dir);
- if (ent)
- ent->read_proc = procmpt_summary_read;
-
- ent = create_proc_entry("version", S_IFREG|S_IRUGO, mpt_proc_root_dir);
- if (ent)
- ent->read_proc = procmpt_version_read;
-
+ proc_create("summary", S_IRUGO, mpt_proc_root_dir, &mpt_summary_proc_fops);
+ proc_create("version", S_IRUGO, mpt_proc_root_dir, &mpt_version_proc_fops);
return 0;
}
@@ -6578,70 +6560,46 @@ procmpt_destroy(void)
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
- * procmpt_summary_read - Handle read request of a summary file
- * @buf: Pointer to area to write information
- * @start: Pointer to start pointer
- * @offset: Offset to start writing
- * @request: Amount of read data requested
- * @eof: Pointer to EOF integer
- * @data: Pointer
- *
* Handles read request from /proc/mpt/summary or /proc/mpt/iocN/summary.
- * Returns number of characters written to process performing the read.
*/
-static int
-procmpt_summary_read(char *buf, char **start, off_t offset, int request, int *eof, void *data)
-{
- MPT_ADAPTER *ioc;
- char *out = buf;
- int len;
+static void seq_mpt_print_ioc_summary(MPT_ADAPTER *ioc, struct seq_file *m, int showlan);
- if (data) {
- int more = 0;
-
- ioc = data;
- mpt_print_ioc_summary(ioc, out, &more, 0, 1);
+static int mpt_summary_proc_show(struct seq_file *m, void *v)
+{
+ MPT_ADAPTER *ioc = m->private;
- out += more;
+ if (ioc) {
+ seq_mpt_print_ioc_summary(ioc, m, 1);
} else {
list_for_each_entry(ioc, &ioc_list, list) {
- int more = 0;
-
- mpt_print_ioc_summary(ioc, out, &more, 0, 1);
-
- out += more;
- if ((out-buf) >= request)
- break;
+ seq_mpt_print_ioc_summary(ioc, m, 1);
}
}
- len = out - buf;
+ return 0;
+}
- MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len);
+static int mpt_summary_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mpt_summary_proc_show, PDE(inode)->data);
}
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * procmpt_version_read - Handle read request from /proc/mpt/version.
- * @buf: Pointer to area to write information
- * @start: Pointer to start pointer
- * @offset: Offset to start writing
- * @request: Amount of read data requested
- * @eof: Pointer to EOF integer
- * @data: Pointer
- *
- * Returns number of characters written to process performing the read.
- */
-static int
-procmpt_version_read(char *buf, char **start, off_t offset, int request, int *eof, void *data)
+static const struct file_operations mpt_summary_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = mpt_summary_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int mpt_version_proc_show(struct seq_file *m, void *v)
{
u8 cb_idx;
int scsi, fc, sas, lan, ctl, targ, dmp;
char *drvname;
- int len;
- len = sprintf(buf, "%s-%s\n", "mptlinux", MPT_LINUX_VERSION_COMMON);
- len += sprintf(buf+len, " Fusion MPT base driver\n");
+ seq_printf(m, "%s-%s\n", "mptlinux", MPT_LINUX_VERSION_COMMON);
+ seq_printf(m, " Fusion MPT base driver\n");
scsi = fc = sas = lan = ctl = targ = dmp = 0;
for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) {
@@ -6669,98 +6627,97 @@ procmpt_version_read(char *buf, char **start, off_t offset, int request, int *eo
}
if (drvname)
- len += sprintf(buf+len, " Fusion MPT %s driver\n", drvname);
+ seq_printf(m, " Fusion MPT %s driver\n", drvname);
}
}
- MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len);
+ return 0;
}
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * procmpt_iocinfo_read - Handle read request from /proc/mpt/iocN/info.
- * @buf: Pointer to area to write information
- * @start: Pointer to start pointer
- * @offset: Offset to start writing
- * @request: Amount of read data requested
- * @eof: Pointer to EOF integer
- * @data: Pointer
- *
- * Returns number of characters written to process performing the read.
- */
-static int
-procmpt_iocinfo_read(char *buf, char **start, off_t offset, int request, int *eof, void *data)
+static int mpt_version_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mpt_version_proc_show, NULL);
+}
+
+static const struct file_operations mpt_version_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = mpt_version_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int mpt_iocinfo_proc_show(struct seq_file *m, void *v)
{
- MPT_ADAPTER *ioc = data;
- int len;
+ MPT_ADAPTER *ioc = m->private;
char expVer[32];
int sz;
int p;
mpt_get_fw_exp_ver(expVer, ioc);
- len = sprintf(buf, "%s:", ioc->name);
+ seq_printf(m, "%s:", ioc->name);
if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT)
- len += sprintf(buf+len, " (f/w download boot flag set)");
+ seq_printf(m, " (f/w download boot flag set)");
// if (ioc->facts.IOCExceptions & MPI_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL)
-// len += sprintf(buf+len, " CONFIG_CHECKSUM_FAIL!");
+// seq_printf(m, " CONFIG_CHECKSUM_FAIL!");
- len += sprintf(buf+len, "\n ProductID = 0x%04x (%s)\n",
+ seq_printf(m, "\n ProductID = 0x%04x (%s)\n",
ioc->facts.ProductID,
ioc->prod_name);
- len += sprintf(buf+len, " FWVersion = 0x%08x%s", ioc->facts.FWVersion.Word, expVer);
+ seq_printf(m, " FWVersion = 0x%08x%s", ioc->facts.FWVersion.Word, expVer);
if (ioc->facts.FWImageSize)
- len += sprintf(buf+len, " (fw_size=%d)", ioc->facts.FWImageSize);
- len += sprintf(buf+len, "\n MsgVersion = 0x%04x\n", ioc->facts.MsgVersion);
- len += sprintf(buf+len, " FirstWhoInit = 0x%02x\n", ioc->FirstWhoInit);
- len += sprintf(buf+len, " EventState = 0x%02x\n", ioc->facts.EventState);
+ seq_printf(m, " (fw_size=%d)", ioc->facts.FWImageSize);
+ seq_printf(m, "\n MsgVersion = 0x%04x\n", ioc->facts.MsgVersion);
+ seq_printf(m, " FirstWhoInit = 0x%02x\n", ioc->FirstWhoInit);
+ seq_printf(m, " EventState = 0x%02x\n", ioc->facts.EventState);
- len += sprintf(buf+len, " CurrentHostMfaHighAddr = 0x%08x\n",
+ seq_printf(m, " CurrentHostMfaHighAddr = 0x%08x\n",
ioc->facts.CurrentHostMfaHighAddr);
- len += sprintf(buf+len, " CurrentSenseBufferHighAddr = 0x%08x\n",
+ seq_printf(m, " CurrentSenseBufferHighAddr = 0x%08x\n",
ioc->facts.CurrentSenseBufferHighAddr);
- len += sprintf(buf+len, " MaxChainDepth = 0x%02x frames\n", ioc->facts.MaxChainDepth);
- len += sprintf(buf+len, " MinBlockSize = 0x%02x bytes\n", 4*ioc->facts.BlockSize);
+ seq_printf(m, " MaxChainDepth = 0x%02x frames\n", ioc->facts.MaxChainDepth);
+ seq_printf(m, " MinBlockSize = 0x%02x bytes\n", 4*ioc->facts.BlockSize);
- len += sprintf(buf+len, " RequestFrames @ 0x%p (Dma @ 0x%p)\n",
+ seq_printf(m, " RequestFrames @ 0x%p (Dma @ 0x%p)\n",
(void *)ioc->req_frames, (void *)(ulong)ioc->req_frames_dma);
/*
* Rounding UP to nearest 4-kB boundary here...
*/
sz = (ioc->req_sz * ioc->req_depth) + 128;
sz = ((sz + 0x1000UL - 1UL) / 0x1000) * 0x1000;
- len += sprintf(buf+len, " {CurReqSz=%d} x {CurReqDepth=%d} = %d bytes ^= 0x%x\n",
+ seq_printf(m, " {CurReqSz=%d} x {CurReqDepth=%d} = %d bytes ^= 0x%x\n",
ioc->req_sz, ioc->req_depth, ioc->req_sz*ioc->req_depth, sz);
- len += sprintf(buf+len, " {MaxReqSz=%d} {MaxReqDepth=%d}\n",
+ seq_printf(m, " {MaxReqSz=%d} {MaxReqDepth=%d}\n",
4*ioc->facts.RequestFrameSize,
ioc->facts.GlobalCredits);
- len += sprintf(buf+len, " Frames @ 0x%p (Dma @ 0x%p)\n",
+ seq_printf(m, " Frames @ 0x%p (Dma @ 0x%p)\n",
(void *)ioc->alloc, (void *)(ulong)ioc->alloc_dma);
sz = (ioc->reply_sz * ioc->reply_depth) + 128;
- len += sprintf(buf+len, " {CurRepSz=%d} x {CurRepDepth=%d} = %d bytes ^= 0x%x\n",
+ seq_printf(m, " {CurRepSz=%d} x {CurRepDepth=%d} = %d bytes ^= 0x%x\n",
ioc->reply_sz, ioc->reply_depth, ioc->reply_sz*ioc->reply_depth, sz);
- len += sprintf(buf+len, " {MaxRepSz=%d} {MaxRepDepth=%d}\n",
+ seq_printf(m, " {MaxRepSz=%d} {MaxRepDepth=%d}\n",
ioc->facts.CurReplyFrameSize,
ioc->facts.ReplyQueueDepth);
- len += sprintf(buf+len, " MaxDevices = %d\n",
+ seq_printf(m, " MaxDevices = %d\n",
(ioc->facts.MaxDevices==0) ? 255 : ioc->facts.MaxDevices);
- len += sprintf(buf+len, " MaxBuses = %d\n", ioc->facts.MaxBuses);
+ seq_printf(m, " MaxBuses = %d\n", ioc->facts.MaxBuses);
/* per-port info */
for (p=0; p < ioc->facts.NumberOfPorts; p++) {
- len += sprintf(buf+len, " PortNumber = %d (of %d)\n",
+ seq_printf(m, " PortNumber = %d (of %d)\n",
p+1,
ioc->facts.NumberOfPorts);
if (ioc->bus_type == FC) {
if (ioc->pfacts[p].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) {
u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow;
- len += sprintf(buf+len, " LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
+ seq_printf(m, " LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
a[5], a[4], a[3], a[2], a[1], a[0]);
}
- len += sprintf(buf+len, " WWN = %08X%08X:%08X%08X\n",
+ seq_printf(m, " WWN = %08X%08X:%08X%08X\n",
ioc->fc_port_page0[p].WWNN.High,
ioc->fc_port_page0[p].WWNN.Low,
ioc->fc_port_page0[p].WWPN.High,
@@ -6768,9 +6725,21 @@ procmpt_iocinfo_read(char *buf, char **start, off_t offset, int request, int *eo
}
}
- MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len);
+ return 0;
}
+static int mpt_iocinfo_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mpt_iocinfo_proc_show, PDE(inode)->data);
+}
+
+static const struct file_operations mpt_iocinfo_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = mpt_iocinfo_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
#endif /* CONFIG_PROC_FS } */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -6836,6 +6805,39 @@ mpt_print_ioc_summary(MPT_ADAPTER *ioc, char *buffer, int *size, int len, int sh
*size = y;
}
+
+static void seq_mpt_print_ioc_summary(MPT_ADAPTER *ioc, struct seq_file *m, int showlan)
+{
+ char expVer[32];
+
+ mpt_get_fw_exp_ver(expVer, ioc);
+
+ /*
+ * Shorter summary of attached ioc's...
+ */
+ seq_printf(m, "%s: %s, %s%08xh%s, Ports=%d, MaxQ=%d",
+ ioc->name,
+ ioc->prod_name,
+ MPT_FW_REV_MAGIC_ID_STRING, /* "FwRev=" or somesuch */
+ ioc->facts.FWVersion.Word,
+ expVer,
+ ioc->facts.NumberOfPorts,
+ ioc->req_depth);
+
+ if (showlan && (ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN)) {
+ u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow;
+ seq_printf(m, ", LanAddr=%02X:%02X:%02X:%02X:%02X:%02X",
+ a[5], a[4], a[3], a[2], a[1], a[0]);
+ }
+
+ seq_printf(m, ", IRQ=%d", ioc->pci_irq);
+
+ if (!ioc->active)
+ seq_printf(m, " (disabled)");
+
+ seq_putc(m, '\n');
+}
+
/**
* mpt_set_taskmgmt_in_progress_flag - set flags associated with task management
* @ioc: Pointer to MPT_ADAPTER structure
@@ -6921,7 +6923,6 @@ EXPORT_SYMBOL(mpt_halt_firmware);
* mpt_SoftResetHandler - Issues a less expensive reset
* @ioc: Pointer to MPT_ADAPTER structure
* @sleepFlag: Indicates if sleep or schedule must be called.
-
*
* Returns 0 for SUCCESS or -1 if FAILED.
*
@@ -7066,7 +7067,6 @@ mpt_SoftResetHandler(MPT_ADAPTER *ioc, int sleepFlag)
* mpt_Soft_Hard_ResetHandler - Try less expensive reset
* @ioc: Pointer to MPT_ADAPTER structure
* @sleepFlag: Indicates if sleep or schedule must be called.
-
*
* Returns 0 for SUCCESS or -1 if FAILED.
* Try for softreset first, only if it fails go for expensive
diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h
index 23ed3dec72a5..f71f22948477 100644
--- a/drivers/message/fusion/mptbase.h
+++ b/drivers/message/fusion/mptbase.h
@@ -420,31 +420,6 @@ typedef struct _VirtDevice {
#define MPT_TARGET_FLAGS_LED_ON 0x80
/*
- * /proc/mpt interface
- */
-typedef struct {
- const char *name;
- mode_t mode;
- int pad;
- read_proc_t *read_proc;
- write_proc_t *write_proc;
-} mpt_proc_entry_t;
-
-#define MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len) \
-do { \
- len -= offset; \
- if (len < request) { \
- *eof = 1; \
- if (len <= 0) \
- return 0; \
- } else \
- len = request; \
- *start = buf + offset; \
- return len; \
-} while (0)
-
-
-/*
* IOCTL structure and associated defines
*/
diff --git a/drivers/message/i2o/exec-osm.c b/drivers/message/i2o/exec-osm.c
index 06c655c55587..a3970e56ae53 100644
--- a/drivers/message/i2o/exec-osm.c
+++ b/drivers/message/i2o/exec-osm.c
@@ -389,12 +389,16 @@ static int i2o_exec_lct_notify(struct i2o_controller *c, u32 change_ind)
dev = &c->pdev->dev;
if (i2o_dma_realloc(dev, &c->dlct,
- le32_to_cpu(sb->expected_lct_size)))
+ le32_to_cpu(sb->expected_lct_size))) {
+ mutex_unlock(&c->lct_lock);
return -ENOMEM;
+ }
msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
- if (IS_ERR(msg))
+ if (IS_ERR(msg)) {
+ mutex_unlock(&c->lct_lock);
return PTR_ERR(msg);
+ }
msg->u.head[0] = cpu_to_le32(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6);
msg->u.head[1] = cpu_to_le32(I2O_CMD_LCT_NOTIFY << 24 | HOST_TID << 12 |
diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c
index e6733bc99724..f0f1e667000f 100644
--- a/drivers/message/i2o/i2o_block.c
+++ b/drivers/message/i2o/i2o_block.c
@@ -727,7 +727,7 @@ static int i2o_block_transfer(struct request *req)
{
struct i2o_block_device *dev = req->rq_disk->private_data;
struct i2o_controller *c;
- u32 tid = dev->i2o_dev->lct_data.tid;
+ u32 tid;
struct i2o_message *msg;
u32 *mptr;
struct i2o_block_request *ireq = req->special;
@@ -743,6 +743,7 @@ static int i2o_block_transfer(struct request *req)
goto exit;
}
+ tid = dev->i2o_dev->lct_data.tid;
c = dev->i2o_dev->iop;
msg = i2o_msg_get(c);
diff --git a/drivers/message/i2o/i2o_config.c b/drivers/message/i2o/i2o_config.c
index c4b117f5fb70..068ba0785bb4 100644
--- a/drivers/message/i2o/i2o_config.c
+++ b/drivers/message/i2o/i2o_config.c
@@ -111,11 +111,11 @@ static int i2o_cfg_gethrt(unsigned long arg)
len = 8 + ((hrt->entry_len * hrt->num_entries) << 2);
- /* We did a get user...so assuming mem is ok...is this bad? */
- put_user(len, kcmd.reslen);
- if (len > reslen)
+ if (put_user(len, kcmd.reslen))
+ ret = -EFAULT;
+ else if (len > reslen)
ret = -ENOBUFS;
- if (copy_to_user(kcmd.resbuf, (void *)hrt, len))
+ else if (copy_to_user(kcmd.resbuf, (void *)hrt, len))
ret = -EFAULT;
return ret;
@@ -147,8 +147,9 @@ static int i2o_cfg_getlct(unsigned long arg)
lct = (i2o_lct *) c->lct;
len = (unsigned int)lct->table_size << 2;
- put_user(len, kcmd.reslen);
- if (len > reslen)
+ if (put_user(len, kcmd.reslen))
+ ret = -EFAULT;
+ else if (len > reslen)
ret = -ENOBUFS;
else if (copy_to_user(kcmd.resbuf, lct, len))
ret = -EFAULT;
@@ -208,8 +209,9 @@ static int i2o_cfg_parms(unsigned long arg, unsigned int type)
return -EAGAIN;
}
- put_user(len, kcmd.reslen);
- if (len > reslen)
+ if (put_user(len, kcmd.reslen))
+ ret = -EFAULT;
+ else if (len > reslen)
ret = -ENOBUFS;
else if (copy_to_user(kcmd.resbuf, res, len))
ret = -EFAULT;
diff --git a/drivers/message/i2o/i2o_scsi.c b/drivers/message/i2o/i2o_scsi.c
index 3d45817e6dcd..ea6b2197da8a 100644
--- a/drivers/message/i2o/i2o_scsi.c
+++ b/drivers/message/i2o/i2o_scsi.c
@@ -528,7 +528,6 @@ static int i2o_scsi_queuecommand(struct scsi_cmnd *SCpnt,
* Do the incoming paperwork
*/
i2o_dev = SCpnt->device->hostdata;
- c = i2o_dev->iop;
SCpnt->scsi_done = done;
@@ -538,7 +537,7 @@ static int i2o_scsi_queuecommand(struct scsi_cmnd *SCpnt,
done(SCpnt);
goto exit;
}
-
+ c = i2o_dev->iop;
tid = i2o_dev->lct_data.tid;
osm_debug("qcmd: Tid = %03x\n", tid);
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c
index 2c65a2c57294..07933f3f7e4c 100644
--- a/drivers/mfd/88pm860x-core.c
+++ b/drivers/mfd/88pm860x-core.c
@@ -74,12 +74,12 @@ static struct mfd_cell backlight_devs[] = {
}
static struct resource led_resources[] = {
- PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB2B),
- PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB2C),
- PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB2D),
- PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB1B),
- PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB1C),
- PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB1D),
+ PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB1B),
+ PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB1C),
+ PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB1D),
+ PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB2B),
+ PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB2C),
+ PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB2D),
};
#define PM8606_LED_DEVS(_i) \
@@ -428,52 +428,44 @@ static int __devinit device_gpadc_init(struct pm860x_chip *chip,
{
struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
: chip->companion;
- int use_gpadc = 0, data, ret;
+ int data;
+ int ret;
/* initialize GPADC without activating it */
- if (pdata && pdata->touch) {
- /* set GPADC MISC1 register */
- data = 0;
- data |= (pdata->touch->gpadc_prebias << 1)
- & PM8607_GPADC_PREBIAS_MASK;
- data |= (pdata->touch->slot_cycle << 3)
- & PM8607_GPADC_SLOT_CYCLE_MASK;
- data |= (pdata->touch->off_scale << 5)
- & PM8607_GPADC_OFF_SCALE_MASK;
- data |= (pdata->touch->sw_cal << 7)
- & PM8607_GPADC_SW_CAL_MASK;
- if (data) {
- ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
- if (ret < 0)
- goto out;
- }
- /* set tsi prebias time */
- if (pdata->touch->tsi_prebias) {
- data = pdata->touch->tsi_prebias;
- ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
- if (ret < 0)
- goto out;
- }
- /* set prebias & prechg time of pen detect */
- data = 0;
- data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
- data |= (pdata->touch->pen_prechg << 5)
- & PM8607_PD_PRECHG_MASK;
- if (data) {
- ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
- if (ret < 0)
- goto out;
- }
+ if (!pdata || !pdata->touch)
+ return -EINVAL;
- use_gpadc = 1;
+ /* set GPADC MISC1 register */
+ data = 0;
+ data |= (pdata->touch->gpadc_prebias << 1) & PM8607_GPADC_PREBIAS_MASK;
+ data |= (pdata->touch->slot_cycle << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
+ data |= (pdata->touch->off_scale << 5) & PM8607_GPADC_OFF_SCALE_MASK;
+ data |= (pdata->touch->sw_cal << 7) & PM8607_GPADC_SW_CAL_MASK;
+ if (data) {
+ ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
+ if (ret < 0)
+ goto out;
}
-
- /* turn on GPADC */
- if (use_gpadc) {
- ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
- PM8607_GPADC_EN, PM8607_GPADC_EN);
+ /* set tsi prebias time */
+ if (pdata->touch->tsi_prebias) {
+ data = pdata->touch->tsi_prebias;
+ ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
+ if (ret < 0)
+ goto out;
}
+ /* set prebias & prechg time of pen detect */
+ data = 0;
+ data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
+ data |= (pdata->touch->pen_prechg << 5) & PM8607_PD_PRECHG_MASK;
+ if (data) {
+ ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
+ if (ret < 0)
+ goto out;
+ }
+
+ ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
+ PM8607_GPADC_EN, PM8607_GPADC_EN);
out:
return ret;
}
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 9da0e504bbe9..db51ea1c6082 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -7,7 +7,16 @@ menuconfig MFD_SUPPORT
depends on HAS_IOMEM
default y
help
- Configure MFD device drivers.
+ Multifunction devices embed several functions (e.g. GPIOs,
+ touchscreens, keyboards, current regulators, power management chips,
+ etc...) in one single integrated circuit. They usually talk to the
+ main CPU through one or more IRQ lines and low speed data busses (SPI,
+ I2C, etc..). They appear as one single device to the main system
+ through the data bus and the MFD framework allows for sub devices
+ (a.k.a. functions) to appear as discrete platform devices.
+ MFDs are typically found on embedded platforms.
+
+ This option alone does not add any kernel code.
if MFD_SUPPORT
@@ -177,6 +186,38 @@ config TWL4030_CODEC
select MFD_CORE
default n
+config TWL6030_PWM
+ tristate "TWL6030 PWM (Pulse Width Modulator) Support"
+ depends on TWL4030_CORE
+ select HAVE_PWM
+ default n
+ help
+ Say yes here if you want support for TWL6030 PWM.
+ This is used to control charging LED brightness.
+
+config MFD_STMPE
+ bool "Support STMicroelectronics STMPE"
+ depends on I2C=y && GENERIC_HARDIRQS
+ select MFD_CORE
+ help
+ Support for the STMPE family of I/O Expanders from
+ STMicroelectronics.
+
+ Currently supported devices are:
+
+ STMPE811: GPIO, Touchscreen
+ STMPE1601: GPIO, Keypad
+ STMPE2401: GPIO, Keypad
+ STMPE2403: GPIO, Keypad
+
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the functionality
+ of the device. Currently available sub drivers are:
+
+ GPIO: stmpe-gpio
+ Keypad: stmpe-keypad
+ Touchscreen: stmpe-ts
+
config MFD_TC35892
bool "Support Toshiba TC35892"
depends on I2C=y && GENERIC_HARDIRQS
@@ -252,6 +293,16 @@ config MFD_MAX8925
accessing the device, additional drivers must be enabled in order
to use the functionality of the device.
+config MFD_MAX8998
+ bool "Maxim Semiconductor MAX8998 PMIC Support"
+ depends on I2C=y
+ select MFD_CORE
+ help
+ Say yes here to support for Maxim Semiconductor MAX8998. This is
+ a Power Management IC. This driver provies common support for
+ accessing the device, additional drivers must be enabled in order
+ to use the functionality of the device.
+
config MFD_WM8400
tristate "Support Wolfson Microelectronics WM8400"
select MFD_CORE
@@ -482,6 +533,28 @@ config MFD_JANZ_CMODIO
host many different types of MODULbus daughterboards, including
CAN and GPIO controllers.
+config MFD_JZ4740_ADC
+ tristate "Support for the JZ4740 SoC ADC core"
+ select MFD_CORE
+ depends on MACH_JZ4740
+ help
+ Say yes here if you want support for the ADC unit in the JZ4740 SoC.
+ This driver is necessary for jz4740-battery and jz4740-hwmon driver.
+
+config MFD_TPS6586X
+ tristate "TPS6586x Power Management chips"
+ depends on I2C && GPIOLIB
+ select MFD_CORE
+ help
+ If you say yes here you get support for the TPS6586X series of
+ Power Management chips.
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device.
+
+ This driver can also be built as a module. If so, the module
+ will be called tps6586x.
+
endif # MFD_SUPPORT
menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index fb503e77dc60..feaeeaeeddb7 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
+obj-$(CONFIG_MFD_STMPE) += stmpe.o
obj-$(CONFIG_MFD_TC35892) += tc35892.o
obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o
obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o
@@ -36,6 +37,7 @@ obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
+obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
obj-$(CONFIG_MFD_MC13783) += mc13783-core.o
@@ -56,6 +58,7 @@ obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o
obj-$(CONFIG_PMIC_DA903X) += da903x.o
max8925-objs := max8925-core.o max8925-i2c.o
obj-$(CONFIG_MFD_MAX8925) += max8925.o
+obj-$(CONFIG_MFD_MAX8998) += max8998.o
pcf50633-objs := pcf50633-core.o pcf50633-irq.o
obj-$(CONFIG_MFD_PCF50633) += pcf50633.o
@@ -71,3 +74,5 @@ obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
+obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o
+obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o
diff --git a/drivers/mfd/ab3100-otp.c b/drivers/mfd/ab3100-otp.c
index 63d2b727ddbb..8440010eb2b8 100644
--- a/drivers/mfd/ab3100-otp.c
+++ b/drivers/mfd/ab3100-otp.c
@@ -199,7 +199,7 @@ static int __init ab3100_otp_probe(struct platform_device *pdev)
err = ab3100_otp_read(otp);
if (err)
- return err;
+ goto err_otp_read;
dev_info(&pdev->dev, "AB3100 OTP readout registered\n");
@@ -208,21 +208,21 @@ static int __init ab3100_otp_probe(struct platform_device *pdev)
err = device_create_file(&pdev->dev,
&ab3100_otp_attrs[i]);
if (err)
- goto out_no_sysfs;
+ goto err_create_file;
}
/* debugfs entries */
err = ab3100_otp_init_debugfs(&pdev->dev, otp);
if (err)
- goto out_no_debugfs;
+ goto err_init_debugfs;
return 0;
-out_no_sysfs:
- for (i = 0; i < ARRAY_SIZE(ab3100_otp_attrs); i++)
- device_remove_file(&pdev->dev,
- &ab3100_otp_attrs[i]);
-out_no_debugfs:
+err_init_debugfs:
+err_create_file:
+ while (--i >= 0)
+ device_remove_file(&pdev->dev, &ab3100_otp_attrs[i]);
+err_otp_read:
kfree(otp);
return err;
}
diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c
index f54ab62e7bc6..8a98739e6d9c 100644
--- a/drivers/mfd/ab3550-core.c
+++ b/drivers/mfd/ab3550-core.c
@@ -589,16 +589,16 @@ static bool reg_read_allowed(const struct ab3550_reg_ranges *ranges, u8 reg)
}
/*
- * The exported register access functionality.
+ * The register access functionality.
*/
-int ab3550_get_chip_id(struct device *dev)
+static int ab3550_get_chip_id(struct device *dev)
{
struct ab3550 *ab = dev_get_drvdata(dev->parent);
return (int)ab->chip_id;
}
-int ab3550_mask_and_set_register_interruptible(struct device *dev, u8 bank,
- u8 reg, u8 bitmask, u8 bitvalues)
+static int ab3550_mask_and_set_register_interruptible(struct device *dev,
+ u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
{
struct ab3550 *ab;
struct platform_device *pdev = to_platform_device(dev);
@@ -612,15 +612,15 @@ int ab3550_mask_and_set_register_interruptible(struct device *dev, u8 bank,
bitmask, bitvalues);
}
-int ab3550_set_register_interruptible(struct device *dev, u8 bank, u8 reg,
- u8 value)
+static int ab3550_set_register_interruptible(struct device *dev, u8 bank,
+ u8 reg, u8 value)
{
return ab3550_mask_and_set_register_interruptible(dev, bank, reg, 0xFF,
value);
}
-int ab3550_get_register_interruptible(struct device *dev, u8 bank, u8 reg,
- u8 *value)
+static int ab3550_get_register_interruptible(struct device *dev, u8 bank,
+ u8 reg, u8 *value)
{
struct ab3550 *ab;
struct platform_device *pdev = to_platform_device(dev);
@@ -633,7 +633,7 @@ int ab3550_get_register_interruptible(struct device *dev, u8 bank, u8 reg,
return get_register_interruptible(ab, bank, reg, value);
}
-int ab3550_get_register_page_interruptible(struct device *dev, u8 bank,
+static int ab3550_get_register_page_interruptible(struct device *dev, u8 bank,
u8 first_reg, u8 *regvals, u8 numregs)
{
struct ab3550 *ab;
@@ -649,7 +649,8 @@ int ab3550_get_register_page_interruptible(struct device *dev, u8 bank,
numregs);
}
-int ab3550_event_registers_startup_state_get(struct device *dev, u8 *event)
+static int ab3550_event_registers_startup_state_get(struct device *dev,
+ u8 *event)
{
struct ab3550 *ab;
@@ -661,7 +662,7 @@ int ab3550_event_registers_startup_state_get(struct device *dev, u8 *event)
return 0;
}
-int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq)
+static int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq)
{
struct ab3550 *ab;
struct ab3550_platform_data *plf_data;
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index f3d26fa9c34d..defa786dee34 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/ab8500.h>
+#include <linux/regulator/ab8500.h>
/*
* Interrupt register offsets
@@ -352,6 +353,7 @@ static struct mfd_cell ab8500_devs[] = {
{ .name = "ab8500-audio", },
{ .name = "ab8500-usb", },
{ .name = "ab8500-pwm", },
+ { .name = "ab8500-regulator", },
};
int __devinit ab8500_init(struct ab8500 *ab8500)
@@ -411,7 +413,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
goto out_removeirq;
}
- ret = mfd_add_devices(ab8500->dev, -1, ab8500_devs,
+ ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
ARRAY_SIZE(ab8500_devs), NULL,
ab8500->irq_base);
if (ret)
diff --git a/drivers/mfd/ab8500-spi.c b/drivers/mfd/ab8500-spi.c
index b81d4f768ef6..e1c8b62b086d 100644
--- a/drivers/mfd/ab8500-spi.c
+++ b/drivers/mfd/ab8500-spi.c
@@ -68,7 +68,12 @@ static int ab8500_spi_read(struct ab8500 *ab8500, u16 addr)
ret = spi_sync(spi, &msg);
if (!ret)
- ret = ab8500->rx_buf[0];
+ /*
+ * Only the 8 lowermost bytes are
+ * defined with value, the rest may
+ * vary depending on chip/board noise.
+ */
+ ret = ab8500->rx_buf[0] & 0xFFU;
return ret;
}
diff --git a/drivers/mfd/abx500-core.c b/drivers/mfd/abx500-core.c
index 3b3b97ec32a7..f12720dbe126 100644
--- a/drivers/mfd/abx500-core.c
+++ b/drivers/mfd/abx500-core.c
@@ -36,7 +36,7 @@ int abx500_register_ops(struct device *dev, struct abx500_ops *ops)
struct abx500_device_entry *dev_entry;
dev_entry = kzalloc(sizeof(struct abx500_device_entry), GFP_KERNEL);
- if (IS_ERR(dev_entry)) {
+ if (!dev_entry) {
dev_err(dev, "register_ops kzalloc failed");
return -ENOMEM;
}
diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c
index 3e75f02e4778..33c923d215c7 100644
--- a/drivers/mfd/davinci_voicecodec.c
+++ b/drivers/mfd/davinci_voicecodec.c
@@ -94,7 +94,8 @@ static int __init davinci_vc_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!res) {
dev_err(&pdev->dev, "no DMA resource\n");
- return -ENXIO;
+ ret = -ENXIO;
+ goto fail4;
}
davinci_vc->davinci_vcif.dma_tx_channel = res->start;
@@ -104,7 +105,8 @@ static int __init davinci_vc_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!res) {
dev_err(&pdev->dev, "no DMA resource\n");
- return -ENXIO;
+ ret = -ENXIO;
+ goto fail4;
}
davinci_vc->davinci_vcif.dma_rx_channel = res->start;
diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c
index 9ed630799acc..36a166bcdb08 100644
--- a/drivers/mfd/janz-cmodio.c
+++ b/drivers/mfd/janz-cmodio.c
@@ -18,6 +18,7 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <linux/mfd/core.h>
#include <linux/mfd/janz.h>
diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c
new file mode 100644
index 000000000000..3ad492cb6c41
--- /dev/null
+++ b/drivers/mfd/jz4740-adc.c
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
+ * JZ4740 SoC ADC driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This driver synchronizes access to the JZ4740 ADC core between the
+ * JZ4740 battery and hwmon drivers.
+ */
+
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <linux/clk.h>
+#include <linux/mfd/core.h>
+
+#include <linux/jz4740-adc.h>
+
+
+#define JZ_REG_ADC_ENABLE 0x00
+#define JZ_REG_ADC_CFG 0x04
+#define JZ_REG_ADC_CTRL 0x08
+#define JZ_REG_ADC_STATUS 0x0c
+
+#define JZ_REG_ADC_TOUCHSCREEN_BASE 0x10
+#define JZ_REG_ADC_BATTERY_BASE 0x1c
+#define JZ_REG_ADC_HWMON_BASE 0x20
+
+#define JZ_ADC_ENABLE_TOUCH BIT(2)
+#define JZ_ADC_ENABLE_BATTERY BIT(1)
+#define JZ_ADC_ENABLE_ADCIN BIT(0)
+
+enum {
+ JZ_ADC_IRQ_ADCIN = 0,
+ JZ_ADC_IRQ_BATTERY,
+ JZ_ADC_IRQ_TOUCH,
+ JZ_ADC_IRQ_PENUP,
+ JZ_ADC_IRQ_PENDOWN,
+};
+
+struct jz4740_adc {
+ struct resource *mem;
+ void __iomem *base;
+
+ int irq;
+ int irq_base;
+
+ struct clk *clk;
+ atomic_t clk_ref;
+
+ spinlock_t lock;
+};
+
+static inline void jz4740_adc_irq_set_masked(struct jz4740_adc *adc, int irq,
+ bool masked)
+{
+ unsigned long flags;
+ uint8_t val;
+
+ irq -= adc->irq_base;
+
+ spin_lock_irqsave(&adc->lock, flags);
+
+ val = readb(adc->base + JZ_REG_ADC_CTRL);
+ if (masked)
+ val |= BIT(irq);
+ else
+ val &= ~BIT(irq);
+ writeb(val, adc->base + JZ_REG_ADC_CTRL);
+
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static void jz4740_adc_irq_mask(unsigned int irq)
+{
+ struct jz4740_adc *adc = get_irq_chip_data(irq);
+ jz4740_adc_irq_set_masked(adc, irq, true);
+}
+
+static void jz4740_adc_irq_unmask(unsigned int irq)
+{
+ struct jz4740_adc *adc = get_irq_chip_data(irq);
+ jz4740_adc_irq_set_masked(adc, irq, false);
+}
+
+static void jz4740_adc_irq_ack(unsigned int irq)
+{
+ struct jz4740_adc *adc = get_irq_chip_data(irq);
+
+ irq -= adc->irq_base;
+ writeb(BIT(irq), adc->base + JZ_REG_ADC_STATUS);
+}
+
+static struct irq_chip jz4740_adc_irq_chip = {
+ .name = "jz4740-adc",
+ .mask = jz4740_adc_irq_mask,
+ .unmask = jz4740_adc_irq_unmask,
+ .ack = jz4740_adc_irq_ack,
+};
+
+static void jz4740_adc_irq_demux(unsigned int irq, struct irq_desc *desc)
+{
+ struct jz4740_adc *adc = get_irq_desc_data(desc);
+ uint8_t status;
+ unsigned int i;
+
+ status = readb(adc->base + JZ_REG_ADC_STATUS);
+
+ for (i = 0; i < 5; ++i) {
+ if (status & BIT(i))
+ generic_handle_irq(adc->irq_base + i);
+ }
+}
+
+
+/* Refcounting for the ADC clock is done in here instead of in the clock
+ * framework, because it is the only clock which is shared between multiple
+ * devices and thus is the only clock which needs refcounting */
+static inline void jz4740_adc_clk_enable(struct jz4740_adc *adc)
+{
+ if (atomic_inc_return(&adc->clk_ref) == 1)
+ clk_enable(adc->clk);
+}
+
+static inline void jz4740_adc_clk_disable(struct jz4740_adc *adc)
+{
+ if (atomic_dec_return(&adc->clk_ref) == 0)
+ clk_disable(adc->clk);
+}
+
+static inline void jz4740_adc_set_enabled(struct jz4740_adc *adc, int engine,
+ bool enabled)
+{
+ unsigned long flags;
+ uint8_t val;
+
+ spin_lock_irqsave(&adc->lock, flags);
+
+ val = readb(adc->base + JZ_REG_ADC_ENABLE);
+ if (enabled)
+ val |= BIT(engine);
+ else
+ val &= BIT(engine);
+ writeb(val, adc->base + JZ_REG_ADC_ENABLE);
+
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static int jz4740_adc_cell_enable(struct platform_device *pdev)
+{
+ struct jz4740_adc *adc = dev_get_drvdata(pdev->dev.parent);
+
+ jz4740_adc_clk_enable(adc);
+ jz4740_adc_set_enabled(adc, pdev->id, true);
+
+ return 0;
+}
+
+static int jz4740_adc_cell_disable(struct platform_device *pdev)
+{
+ struct jz4740_adc *adc = dev_get_drvdata(pdev->dev.parent);
+
+ jz4740_adc_set_enabled(adc, pdev->id, false);
+ jz4740_adc_clk_disable(adc);
+
+ return 0;
+}
+
+int jz4740_adc_set_config(struct device *dev, uint32_t mask, uint32_t val)
+{
+ struct jz4740_adc *adc = dev_get_drvdata(dev);
+ unsigned long flags;
+ uint32_t cfg;
+
+ if (!adc)
+ return -ENODEV;
+
+ spin_lock_irqsave(&adc->lock, flags);
+
+ cfg = readl(adc->base + JZ_REG_ADC_CFG);
+
+ cfg &= ~mask;
+ cfg |= val;
+
+ writel(cfg, adc->base + JZ_REG_ADC_CFG);
+
+ spin_unlock_irqrestore(&adc->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(jz4740_adc_set_config);
+
+static struct resource jz4740_hwmon_resources[] = {
+ {
+ .start = JZ_ADC_IRQ_ADCIN,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = JZ_REG_ADC_HWMON_BASE,
+ .end = JZ_REG_ADC_HWMON_BASE + 3,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource jz4740_battery_resources[] = {
+ {
+ .start = JZ_ADC_IRQ_BATTERY,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = JZ_REG_ADC_BATTERY_BASE,
+ .end = JZ_REG_ADC_BATTERY_BASE + 3,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+const struct mfd_cell jz4740_adc_cells[] = {
+ {
+ .id = 0,
+ .name = "jz4740-hwmon",
+ .num_resources = ARRAY_SIZE(jz4740_hwmon_resources),
+ .resources = jz4740_hwmon_resources,
+ .platform_data = (void *)&jz4740_adc_cells[0],
+ .data_size = sizeof(struct mfd_cell),
+
+ .enable = jz4740_adc_cell_enable,
+ .disable = jz4740_adc_cell_disable,
+ },
+ {
+ .id = 1,
+ .name = "jz4740-battery",
+ .num_resources = ARRAY_SIZE(jz4740_battery_resources),
+ .resources = jz4740_battery_resources,
+ .platform_data = (void *)&jz4740_adc_cells[1],
+ .data_size = sizeof(struct mfd_cell),
+
+ .enable = jz4740_adc_cell_enable,
+ .disable = jz4740_adc_cell_disable,
+ },
+};
+
+static int __devinit jz4740_adc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct jz4740_adc *adc;
+ struct resource *mem_base;
+ int irq;
+
+ adc = kmalloc(sizeof(*adc), GFP_KERNEL);
+ if (!adc) {
+ dev_err(&pdev->dev, "Failed to allocate driver structure\n");
+ return -ENOMEM;
+ }
+
+ adc->irq = platform_get_irq(pdev, 0);
+ if (adc->irq < 0) {
+ ret = adc->irq;
+ dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
+ goto err_free;
+ }
+
+ adc->irq_base = platform_get_irq(pdev, 1);
+ if (adc->irq_base < 0) {
+ ret = adc->irq_base;
+ dev_err(&pdev->dev, "Failed to get irq base: %d\n", ret);
+ goto err_free;
+ }
+
+ mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem_base) {
+ ret = -ENOENT;
+ dev_err(&pdev->dev, "Failed to get platform mmio resource\n");
+ goto err_free;
+ }
+
+ /* Only request the shared registers for the MFD driver */
+ adc->mem = request_mem_region(mem_base->start, JZ_REG_ADC_STATUS,
+ pdev->name);
+ if (!adc->mem) {
+ ret = -EBUSY;
+ dev_err(&pdev->dev, "Failed to request mmio memory region\n");
+ goto err_free;
+ }
+
+ adc->base = ioremap_nocache(adc->mem->start, resource_size(adc->mem));
+ if (!adc->base) {
+ ret = -EBUSY;
+ dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
+ goto err_release_mem_region;
+ }
+
+ adc->clk = clk_get(&pdev->dev, "adc");
+ if (IS_ERR(adc->clk)) {
+ ret = PTR_ERR(adc->clk);
+ dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
+ goto err_iounmap;
+ }
+
+ spin_lock_init(&adc->lock);
+ atomic_set(&adc->clk_ref, 0);
+
+ platform_set_drvdata(pdev, adc);
+
+ for (irq = adc->irq_base; irq < adc->irq_base + 5; ++irq) {
+ set_irq_chip_data(irq, adc);
+ set_irq_chip_and_handler(irq, &jz4740_adc_irq_chip,
+ handle_level_irq);
+ }
+
+ set_irq_data(adc->irq, adc);
+ set_irq_chained_handler(adc->irq, jz4740_adc_irq_demux);
+
+ writeb(0x00, adc->base + JZ_REG_ADC_ENABLE);
+ writeb(0xff, adc->base + JZ_REG_ADC_CTRL);
+
+ ret = mfd_add_devices(&pdev->dev, 0, jz4740_adc_cells,
+ ARRAY_SIZE(jz4740_adc_cells), mem_base, adc->irq_base);
+ if (ret < 0)
+ goto err_clk_put;
+
+ return 0;
+
+err_clk_put:
+ clk_put(adc->clk);
+err_iounmap:
+ platform_set_drvdata(pdev, NULL);
+ iounmap(adc->base);
+err_release_mem_region:
+ release_mem_region(adc->mem->start, resource_size(adc->mem));
+err_free:
+ kfree(adc);
+
+ return ret;
+}
+
+static int __devexit jz4740_adc_remove(struct platform_device *pdev)
+{
+ struct jz4740_adc *adc = platform_get_drvdata(pdev);
+
+ mfd_remove_devices(&pdev->dev);
+
+ set_irq_data(adc->irq, NULL);
+ set_irq_chained_handler(adc->irq, NULL);
+
+ iounmap(adc->base);
+ release_mem_region(adc->mem->start, resource_size(adc->mem));
+
+ clk_put(adc->clk);
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(adc);
+
+ return 0;
+}
+
+struct platform_driver jz4740_adc_driver = {
+ .probe = jz4740_adc_probe,
+ .remove = __devexit_p(jz4740_adc_remove),
+ .driver = {
+ .name = "jz4740-adc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init jz4740_adc_init(void)
+{
+ return platform_driver_register(&jz4740_adc_driver);
+}
+module_init(jz4740_adc_init);
+
+static void __exit jz4740_adc_exit(void)
+{
+ platform_driver_unregister(&jz4740_adc_driver);
+}
+module_exit(jz4740_adc_exit);
+
+MODULE_DESCRIPTION("JZ4740 SoC ADC driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:jz4740-adc");
diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c
index f621bcea3d02..04028a9ee082 100644
--- a/drivers/mfd/max8925-core.c
+++ b/drivers/mfd/max8925-core.c
@@ -90,6 +90,24 @@ static struct mfd_cell rtc_devs[] = {
},
};
+static struct resource onkey_resources[] = {
+ {
+ .name = "max8925-onkey",
+ .start = MAX8925_IRQ_GPM_SW_3SEC,
+ .end = MAX8925_IRQ_GPM_SW_3SEC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell onkey_devs[] = {
+ {
+ .name = "max8925-onkey",
+ .num_resources = 1,
+ .resources = &onkey_resources[0],
+ .id = -1,
+ },
+};
+
#define MAX8925_REG_RESOURCE(_start, _end) \
{ \
.start = MAX8925_##_start, \
@@ -596,6 +614,15 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
dev_err(chip->dev, "Failed to add rtc subdev\n");
goto out;
}
+
+ ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
+ ARRAY_SIZE(onkey_devs),
+ &onkey_resources[0], 0);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to add onkey subdev\n");
+ goto out_dev;
+ }
+
if (pdata && pdata->regulator[0]) {
ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
ARRAY_SIZE(regulator_devs),
diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c
new file mode 100644
index 000000000000..73e6f5c4efc9
--- /dev/null
+++ b/drivers/mfd/max8998.c
@@ -0,0 +1,158 @@
+/*
+ * max8698.c - mfd core driver for the Maxim 8998
+ *
+ * Copyright (C) 2009-2010 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max8998.h>
+#include <linux/mfd/max8998-private.h>
+
+static struct mfd_cell max8998_devs[] = {
+ {
+ .name = "max8998-pmic",
+ }
+};
+
+static int max8998_i2c_device_read(struct max8998_dev *max8998, u8 reg, u8 *dest)
+{
+ struct i2c_client *client = max8998->i2c_client;
+ int ret;
+
+ mutex_lock(&max8998->iolock);
+ ret = i2c_smbus_read_byte_data(client, reg);
+ mutex_unlock(&max8998->iolock);
+ if (ret < 0)
+ return ret;
+
+ ret &= 0xff;
+ *dest = ret;
+ return 0;
+}
+
+static int max8998_i2c_device_write(struct max8998_dev *max8998, u8 reg, u8 value)
+{
+ struct i2c_client *client = max8998->i2c_client;
+ int ret;
+
+ mutex_lock(&max8998->iolock);
+ ret = i2c_smbus_write_byte_data(client, reg, value);
+ mutex_unlock(&max8998->iolock);
+ return ret;
+}
+
+static int max8998_i2c_device_update(struct max8998_dev *max8998, u8 reg,
+ u8 val, u8 mask)
+{
+ struct i2c_client *client = max8998->i2c_client;
+ int ret;
+
+ mutex_lock(&max8998->iolock);
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret >= 0) {
+ u8 old_val = ret & 0xff;
+ u8 new_val = (val & mask) | (old_val & (~mask));
+ ret = i2c_smbus_write_byte_data(client, reg, new_val);
+ if (ret >= 0)
+ ret = 0;
+ }
+ mutex_unlock(&max8998->iolock);
+ return ret;
+}
+
+static int max8998_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max8998_dev *max8998;
+ int ret = 0;
+
+ max8998 = kzalloc(sizeof(struct max8998_dev), GFP_KERNEL);
+ if (max8998 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max8998);
+ max8998->dev = &i2c->dev;
+ max8998->i2c_client = i2c;
+ max8998->dev_read = max8998_i2c_device_read;
+ max8998->dev_write = max8998_i2c_device_write;
+ max8998->dev_update = max8998_i2c_device_update;
+ mutex_init(&max8998->iolock);
+
+ ret = mfd_add_devices(max8998->dev, -1,
+ max8998_devs, ARRAY_SIZE(max8998_devs),
+ NULL, 0);
+ if (ret < 0)
+ goto err;
+
+ return ret;
+
+err:
+ mfd_remove_devices(max8998->dev);
+ kfree(max8998);
+ return ret;
+}
+
+static int max8998_i2c_remove(struct i2c_client *i2c)
+{
+ struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
+
+ mfd_remove_devices(max8998->dev);
+ kfree(max8998);
+
+ return 0;
+}
+
+static const struct i2c_device_id max8998_i2c_id[] = {
+ { "max8998", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max8998_i2c_id);
+
+static struct i2c_driver max8998_i2c_driver = {
+ .driver = {
+ .name = "max8998",
+ .owner = THIS_MODULE,
+ },
+ .probe = max8998_i2c_probe,
+ .remove = max8998_i2c_remove,
+ .id_table = max8998_i2c_id,
+};
+
+static int __init max8998_i2c_init(void)
+{
+ return i2c_add_driver(&max8998_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(max8998_i2c_init);
+
+static void __exit max8998_i2c_exit(void)
+{
+ i2c_del_driver(&max8998_i2c_driver);
+}
+module_exit(max8998_i2c_exit);
+
+MODULE_DESCRIPTION("MAXIM 8998 multi-function core driver");
+MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c
index fecf38a4f025..6df34989c1f6 100644
--- a/drivers/mfd/mc13783-core.c
+++ b/drivers/mfd/mc13783-core.c
@@ -11,9 +11,31 @@
*/
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
#include <linux/spi/spi.h>
#include <linux/mfd/core.h>
-#include <linux/mfd/mc13783-private.h>
+#include <linux/mfd/mc13783.h>
+
+struct mc13783 {
+ struct spi_device *spidev;
+ struct mutex lock;
+ int irq;
+ int flags;
+
+ irq_handler_t irqhandler[MC13783_NUM_IRQ];
+ void *irqdata[MC13783_NUM_IRQ];
+
+ /* XXX these should go as platformdata to the regulator subdevice */
+ struct mc13783_regulator_init_data *regulators;
+ int num_regulators;
+};
+
+#define MC13783_REG_REVISION 7
+#define MC13783_REG_ADC_0 43
+#define MC13783_REG_ADC_1 44
+#define MC13783_REG_ADC_2 45
#define MC13783_IRQSTAT0 0
#define MC13783_IRQSTAT0_ADCDONEI (1 << 0)
@@ -226,6 +248,12 @@ int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
}
EXPORT_SYMBOL(mc13783_reg_rmw);
+int mc13783_get_flags(struct mc13783 *mc13783)
+{
+ return mc13783->flags;
+}
+EXPORT_SYMBOL(mc13783_get_flags);
+
int mc13783_irq_mask(struct mc13783 *mc13783, int irq)
{
int ret;
diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c
index a3fb4bcb9889..4ba85bbdb4c1 100644
--- a/drivers/mfd/menelaus.c
+++ b/drivers/mfd/menelaus.c
@@ -128,6 +128,39 @@
#define MENELAUS_RESERVED14_IRQ 14 /* Reserved */
#define MENELAUS_RESERVED15_IRQ 15 /* Reserved */
+/* VCORE_CTRL1 register */
+#define VCORE_CTRL1_BYP_COMP (1 << 5)
+#define VCORE_CTRL1_HW_NSW (1 << 7)
+
+/* GPIO_CTRL register */
+#define GPIO_CTRL_SLOTSELEN (1 << 5)
+#define GPIO_CTRL_SLPCTLEN (1 << 6)
+#define GPIO1_DIR_INPUT (1 << 0)
+#define GPIO2_DIR_INPUT (1 << 1)
+#define GPIO3_DIR_INPUT (1 << 2)
+
+/* MCT_CTRL1 register */
+#define MCT_CTRL1_S1_CMD_OD (1 << 2)
+#define MCT_CTRL1_S2_CMD_OD (1 << 3)
+
+/* MCT_CTRL2 register */
+#define MCT_CTRL2_VS2_SEL_D0 (1 << 0)
+#define MCT_CTRL2_VS2_SEL_D1 (1 << 1)
+#define MCT_CTRL2_S1CD_BUFEN (1 << 4)
+#define MCT_CTRL2_S2CD_BUFEN (1 << 5)
+#define MCT_CTRL2_S1CD_DBEN (1 << 6)
+#define MCT_CTRL2_S2CD_BEN (1 << 7)
+
+/* MCT_CTRL3 register */
+#define MCT_CTRL3_SLOT1_EN (1 << 0)
+#define MCT_CTRL3_SLOT2_EN (1 << 1)
+#define MCT_CTRL3_S1_AUTO_EN (1 << 2)
+#define MCT_CTRL3_S2_AUTO_EN (1 << 3)
+
+/* MCT_PIN_ST register */
+#define MCT_PIN_ST_S1_CD_ST (1 << 0)
+#define MCT_PIN_ST_S2_CD_ST (1 << 1)
+
static void menelaus_work(struct work_struct *_menelaus);
struct menelaus_chip {
@@ -249,10 +282,10 @@ static void menelaus_mmc_cd_work(struct menelaus_chip *menelaus_hw)
return;
if (!(reg & 0x1))
- card_mask |= (1 << 0);
+ card_mask |= MCT_PIN_ST_S1_CD_ST;
if (!(reg & 0x2))
- card_mask |= (1 << 1);
+ card_mask |= MCT_PIN_ST_S2_CD_ST;
if (menelaus_hw->mmc_callback)
menelaus_hw->mmc_callback(menelaus_hw->mmc_callback_data,
@@ -277,14 +310,14 @@ int menelaus_set_mmc_opendrain(int slot, int enable)
val = ret;
if (slot == 1) {
if (enable)
- val |= 1 << 2;
+ val |= MCT_CTRL1_S1_CMD_OD;
else
- val &= ~(1 << 2);
+ val &= ~MCT_CTRL1_S1_CMD_OD;
} else {
if (enable)
- val |= 1 << 3;
+ val |= MCT_CTRL1_S2_CMD_OD;
else
- val &= ~(1 << 3);
+ val &= ~MCT_CTRL1_S2_CMD_OD;
}
ret = menelaus_write_reg(MENELAUS_MCT_CTRL1, val);
mutex_unlock(&the_menelaus->lock);
@@ -301,11 +334,11 @@ int menelaus_set_slot_sel(int enable)
ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
if (ret < 0)
goto out;
- ret |= 0x02;
+ ret |= GPIO2_DIR_INPUT;
if (enable)
- ret |= 1 << 5;
+ ret |= GPIO_CTRL_SLOTSELEN;
else
- ret &= ~(1 << 5);
+ ret &= ~GPIO_CTRL_SLOTSELEN;
ret = menelaus_write_reg(MENELAUS_GPIO_CTRL, ret);
out:
mutex_unlock(&the_menelaus->lock);
@@ -330,14 +363,14 @@ int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_en)
val = ret;
if (slot == 1) {
if (cd_en)
- val |= (1 << 4) | (1 << 6);
+ val |= MCT_CTRL2_S1CD_BUFEN | MCT_CTRL2_S1CD_DBEN;
else
- val &= ~((1 << 4) | (1 << 6));
+ val &= ~(MCT_CTRL2_S1CD_BUFEN | MCT_CTRL2_S1CD_DBEN);
} else {
if (cd_en)
- val |= (1 << 5) | (1 << 7);
+ val |= MCT_CTRL2_S2CD_BUFEN | MCT_CTRL2_S2CD_BEN;
else
- val &= ~((1 << 5) | (1 << 7));
+ val &= ~(MCT_CTRL2_S2CD_BUFEN | MCT_CTRL2_S2CD_BEN);
}
ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, val);
if (ret < 0)
@@ -349,25 +382,25 @@ int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_en)
val = ret;
if (slot == 1) {
if (enable)
- val |= 1 << 0;
+ val |= MCT_CTRL3_SLOT1_EN;
else
- val &= ~(1 << 0);
+ val &= ~MCT_CTRL3_SLOT1_EN;
} else {
int b;
if (enable)
- ret |= 1 << 1;
+ val |= MCT_CTRL3_SLOT2_EN;
else
- ret &= ~(1 << 1);
+ val &= ~MCT_CTRL3_SLOT2_EN;
b = menelaus_read_reg(MENELAUS_MCT_CTRL2);
- b &= ~0x03;
+ b &= ~(MCT_CTRL2_VS2_SEL_D0 | MCT_CTRL2_VS2_SEL_D1);
b |= power;
ret = menelaus_write_reg(MENELAUS_MCT_CTRL2, b);
if (ret < 0)
goto out;
}
/* Disable autonomous shutdown */
- val &= ~(0x03 << 2);
+ val &= ~(MCT_CTRL3_S1_AUTO_EN | MCT_CTRL3_S2_AUTO_EN);
ret = menelaus_write_reg(MENELAUS_MCT_CTRL3, val);
out:
mutex_unlock(&the_menelaus->lock);
@@ -552,7 +585,7 @@ int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV)
if (!the_menelaus->vcore_hw_mode) {
val = menelaus_read_reg(MENELAUS_VCORE_CTRL1);
/* HW mode, turn OFF byte comparator */
- val |= ((1 << 7) | (1 << 5));
+ val |= (VCORE_CTRL1_HW_NSW | VCORE_CTRL1_BYP_COMP);
ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val);
the_menelaus->vcore_hw_mode = 1;
}
@@ -749,7 +782,7 @@ int menelaus_set_regulator_sleep(int enable, u32 val)
ret = menelaus_read_reg(MENELAUS_GPIO_CTRL);
if (ret < 0)
goto out;
- t = ((1 << 6) | 0x04);
+ t = (GPIO_CTRL_SLPCTLEN | GPIO3_DIR_INPUT);
if (enable)
ret |= t;
else
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 7dd76bceaae8..1823a57b7d8f 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -70,7 +70,9 @@ static int mfd_add_device(struct device *parent, int id,
goto fail_res;
}
- platform_device_add_resources(pdev, res, cell->num_resources);
+ ret = platform_device_add_resources(pdev, res, cell->num_resources);
+ if (ret)
+ goto fail_res;
ret = platform_device_add(pdev);
if (ret)
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
new file mode 100644
index 000000000000..0754c5e91995
--- /dev/null
+++ b/drivers/mfd/stmpe.c
@@ -0,0 +1,985 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/stmpe.h>
+#include "stmpe.h"
+
+static int __stmpe_enable(struct stmpe *stmpe, unsigned int blocks)
+{
+ return stmpe->variant->enable(stmpe, blocks, true);
+}
+
+static int __stmpe_disable(struct stmpe *stmpe, unsigned int blocks)
+{
+ return stmpe->variant->enable(stmpe, blocks, false);
+}
+
+static int __stmpe_reg_read(struct stmpe *stmpe, u8 reg)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(stmpe->i2c, reg);
+ if (ret < 0)
+ dev_err(stmpe->dev, "failed to read reg %#x: %d\n",
+ reg, ret);
+
+ dev_vdbg(stmpe->dev, "rd: reg %#x => data %#x\n", reg, ret);
+
+ return ret;
+}
+
+static int __stmpe_reg_write(struct stmpe *stmpe, u8 reg, u8 val)
+{
+ int ret;
+
+ dev_vdbg(stmpe->dev, "wr: reg %#x <= %#x\n", reg, val);
+
+ ret = i2c_smbus_write_byte_data(stmpe->i2c, reg, val);
+ if (ret < 0)
+ dev_err(stmpe->dev, "failed to write reg %#x: %d\n",
+ reg, ret);
+
+ return ret;
+}
+
+static int __stmpe_set_bits(struct stmpe *stmpe, u8 reg, u8 mask, u8 val)
+{
+ int ret;
+
+ ret = __stmpe_reg_read(stmpe, reg);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~mask;
+ ret |= val;
+
+ return __stmpe_reg_write(stmpe, reg, ret);
+}
+
+static int __stmpe_block_read(struct stmpe *stmpe, u8 reg, u8 length,
+ u8 *values)
+{
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(stmpe->i2c, reg, length, values);
+ if (ret < 0)
+ dev_err(stmpe->dev, "failed to read regs %#x: %d\n",
+ reg, ret);
+
+ dev_vdbg(stmpe->dev, "rd: reg %#x (%d) => ret %#x\n", reg, length, ret);
+ stmpe_dump_bytes("stmpe rd: ", values, length);
+
+ return ret;
+}
+
+static int __stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length,
+ const u8 *values)
+{
+ int ret;
+
+ dev_vdbg(stmpe->dev, "wr: regs %#x (%d)\n", reg, length);
+ stmpe_dump_bytes("stmpe wr: ", values, length);
+
+ ret = i2c_smbus_write_i2c_block_data(stmpe->i2c, reg, length,
+ values);
+ if (ret < 0)
+ dev_err(stmpe->dev, "failed to write regs %#x: %d\n",
+ reg, ret);
+
+ return ret;
+}
+
+/**
+ * stmpe_enable - enable blocks on an STMPE device
+ * @stmpe: Device to work on
+ * @blocks: Mask of blocks (enum stmpe_block values) to enable
+ */
+int stmpe_enable(struct stmpe *stmpe, unsigned int blocks)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_enable(stmpe, blocks);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_enable);
+
+/**
+ * stmpe_disable - disable blocks on an STMPE device
+ * @stmpe: Device to work on
+ * @blocks: Mask of blocks (enum stmpe_block values) to enable
+ */
+int stmpe_disable(struct stmpe *stmpe, unsigned int blocks)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_disable(stmpe, blocks);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_disable);
+
+/**
+ * stmpe_reg_read() - read a single STMPE register
+ * @stmpe: Device to read from
+ * @reg: Register to read
+ */
+int stmpe_reg_read(struct stmpe *stmpe, u8 reg)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_reg_read(stmpe, reg);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_reg_read);
+
+/**
+ * stmpe_reg_write() - write a single STMPE register
+ * @stmpe: Device to write to
+ * @reg: Register to write
+ * @val: Value to write
+ */
+int stmpe_reg_write(struct stmpe *stmpe, u8 reg, u8 val)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_reg_write(stmpe, reg, val);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_reg_write);
+
+/**
+ * stmpe_set_bits() - set the value of a bitfield in a STMPE register
+ * @stmpe: Device to write to
+ * @reg: Register to write
+ * @mask: Mask of bits to set
+ * @val: Value to set
+ */
+int stmpe_set_bits(struct stmpe *stmpe, u8 reg, u8 mask, u8 val)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_set_bits(stmpe, reg, mask, val);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_set_bits);
+
+/**
+ * stmpe_block_read() - read multiple STMPE registers
+ * @stmpe: Device to read from
+ * @reg: First register
+ * @length: Number of registers
+ * @values: Buffer to write to
+ */
+int stmpe_block_read(struct stmpe *stmpe, u8 reg, u8 length, u8 *values)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_block_read(stmpe, reg, length, values);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_block_read);
+
+/**
+ * stmpe_block_write() - write multiple STMPE registers
+ * @stmpe: Device to write to
+ * @reg: First register
+ * @length: Number of registers
+ * @values: Values to write
+ */
+int stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length,
+ const u8 *values)
+{
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+ ret = __stmpe_block_write(stmpe, reg, length, values);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_block_write);
+
+/**
+ * stmpe_set_altfunc: set the alternate function for STMPE pins
+ * @stmpe: Device to configure
+ * @pins: Bitmask of pins to affect
+ * @block: block to enable alternate functions for
+ *
+ * @pins is assumed to have a bit set for each of the bits whose alternate
+ * function is to be changed, numbered according to the GPIOXY numbers.
+ *
+ * If the GPIO module is not enabled, this function automatically enables it in
+ * order to perform the change.
+ */
+int stmpe_set_altfunc(struct stmpe *stmpe, u32 pins, enum stmpe_block block)
+{
+ struct stmpe_variant_info *variant = stmpe->variant;
+ u8 regaddr = stmpe->regs[STMPE_IDX_GPAFR_U_MSB];
+ int af_bits = variant->af_bits;
+ int numregs = DIV_ROUND_UP(stmpe->num_gpios * af_bits, 8);
+ int afperreg = 8 / af_bits;
+ int mask = (1 << af_bits) - 1;
+ u8 regs[numregs];
+ int af;
+ int ret;
+
+ mutex_lock(&stmpe->lock);
+
+ ret = __stmpe_enable(stmpe, STMPE_BLOCK_GPIO);
+ if (ret < 0)
+ goto out;
+
+ ret = __stmpe_block_read(stmpe, regaddr, numregs, regs);
+ if (ret < 0)
+ goto out;
+
+ af = variant->get_altfunc(stmpe, block);
+
+ while (pins) {
+ int pin = __ffs(pins);
+ int regoffset = numregs - (pin / afperreg) - 1;
+ int pos = (pin % afperreg) * (8 / afperreg);
+
+ regs[regoffset] &= ~(mask << pos);
+ regs[regoffset] |= af << pos;
+
+ pins &= ~(1 << pin);
+ }
+
+ ret = __stmpe_block_write(stmpe, regaddr, numregs, regs);
+
+out:
+ mutex_unlock(&stmpe->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmpe_set_altfunc);
+
+/*
+ * GPIO (all variants)
+ */
+
+static struct resource stmpe_gpio_resources[] = {
+ /* Start and end filled dynamically */
+ {
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell stmpe_gpio_cell = {
+ .name = "stmpe-gpio",
+ .resources = stmpe_gpio_resources,
+ .num_resources = ARRAY_SIZE(stmpe_gpio_resources),
+};
+
+/*
+ * Keypad (1601, 2401, 2403)
+ */
+
+static struct resource stmpe_keypad_resources[] = {
+ {
+ .name = "KEYPAD",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "KEYPAD_OVER",
+ .start = 1,
+ .end = 1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell stmpe_keypad_cell = {
+ .name = "stmpe-keypad",
+ .resources = stmpe_keypad_resources,
+ .num_resources = ARRAY_SIZE(stmpe_keypad_resources),
+};
+
+/*
+ * Touchscreen (STMPE811)
+ */
+
+static struct resource stmpe_ts_resources[] = {
+ {
+ .name = "TOUCH_DET",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "FIFO_TH",
+ .start = 1,
+ .end = 1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell stmpe_ts_cell = {
+ .name = "stmpe-ts",
+ .resources = stmpe_ts_resources,
+ .num_resources = ARRAY_SIZE(stmpe_ts_resources),
+};
+
+/*
+ * STMPE811
+ */
+
+static const u8 stmpe811_regs[] = {
+ [STMPE_IDX_CHIP_ID] = STMPE811_REG_CHIP_ID,
+ [STMPE_IDX_ICR_LSB] = STMPE811_REG_INT_CTRL,
+ [STMPE_IDX_IER_LSB] = STMPE811_REG_INT_EN,
+ [STMPE_IDX_ISR_MSB] = STMPE811_REG_INT_STA,
+ [STMPE_IDX_GPMR_LSB] = STMPE811_REG_GPIO_MP_STA,
+ [STMPE_IDX_GPSR_LSB] = STMPE811_REG_GPIO_SET_PIN,
+ [STMPE_IDX_GPCR_LSB] = STMPE811_REG_GPIO_CLR_PIN,
+ [STMPE_IDX_GPDR_LSB] = STMPE811_REG_GPIO_DIR,
+ [STMPE_IDX_GPRER_LSB] = STMPE811_REG_GPIO_RE,
+ [STMPE_IDX_GPFER_LSB] = STMPE811_REG_GPIO_FE,
+ [STMPE_IDX_GPAFR_U_MSB] = STMPE811_REG_GPIO_AF,
+ [STMPE_IDX_IEGPIOR_LSB] = STMPE811_REG_GPIO_INT_EN,
+ [STMPE_IDX_ISGPIOR_MSB] = STMPE811_REG_GPIO_INT_STA,
+ [STMPE_IDX_GPEDR_MSB] = STMPE811_REG_GPIO_ED,
+};
+
+static struct stmpe_variant_block stmpe811_blocks[] = {
+ {
+ .cell = &stmpe_gpio_cell,
+ .irq = STMPE811_IRQ_GPIOC,
+ .block = STMPE_BLOCK_GPIO,
+ },
+ {
+ .cell = &stmpe_ts_cell,
+ .irq = STMPE811_IRQ_TOUCH_DET,
+ .block = STMPE_BLOCK_TOUCHSCREEN,
+ },
+};
+
+static int stmpe811_enable(struct stmpe *stmpe, unsigned int blocks,
+ bool enable)
+{
+ unsigned int mask = 0;
+
+ if (blocks & STMPE_BLOCK_GPIO)
+ mask |= STMPE811_SYS_CTRL2_GPIO_OFF;
+
+ if (blocks & STMPE_BLOCK_ADC)
+ mask |= STMPE811_SYS_CTRL2_ADC_OFF;
+
+ if (blocks & STMPE_BLOCK_TOUCHSCREEN)
+ mask |= STMPE811_SYS_CTRL2_TSC_OFF;
+
+ return __stmpe_set_bits(stmpe, STMPE811_REG_SYS_CTRL2, mask,
+ enable ? 0 : mask);
+}
+
+static int stmpe811_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
+{
+ /* 0 for touchscreen, 1 for GPIO */
+ return block != STMPE_BLOCK_TOUCHSCREEN;
+}
+
+static struct stmpe_variant_info stmpe811 = {
+ .name = "stmpe811",
+ .id_val = 0x0811,
+ .id_mask = 0xffff,
+ .num_gpios = 8,
+ .af_bits = 1,
+ .regs = stmpe811_regs,
+ .blocks = stmpe811_blocks,
+ .num_blocks = ARRAY_SIZE(stmpe811_blocks),
+ .num_irqs = STMPE811_NR_INTERNAL_IRQS,
+ .enable = stmpe811_enable,
+ .get_altfunc = stmpe811_get_altfunc,
+};
+
+/*
+ * STMPE1601
+ */
+
+static const u8 stmpe1601_regs[] = {
+ [STMPE_IDX_CHIP_ID] = STMPE1601_REG_CHIP_ID,
+ [STMPE_IDX_ICR_LSB] = STMPE1601_REG_ICR_LSB,
+ [STMPE_IDX_IER_LSB] = STMPE1601_REG_IER_LSB,
+ [STMPE_IDX_ISR_MSB] = STMPE1601_REG_ISR_MSB,
+ [STMPE_IDX_GPMR_LSB] = STMPE1601_REG_GPIO_MP_LSB,
+ [STMPE_IDX_GPSR_LSB] = STMPE1601_REG_GPIO_SET_LSB,
+ [STMPE_IDX_GPCR_LSB] = STMPE1601_REG_GPIO_CLR_LSB,
+ [STMPE_IDX_GPDR_LSB] = STMPE1601_REG_GPIO_SET_DIR_LSB,
+ [STMPE_IDX_GPRER_LSB] = STMPE1601_REG_GPIO_RE_LSB,
+ [STMPE_IDX_GPFER_LSB] = STMPE1601_REG_GPIO_FE_LSB,
+ [STMPE_IDX_GPAFR_U_MSB] = STMPE1601_REG_GPIO_AF_U_MSB,
+ [STMPE_IDX_IEGPIOR_LSB] = STMPE1601_REG_INT_EN_GPIO_MASK_LSB,
+ [STMPE_IDX_ISGPIOR_MSB] = STMPE1601_REG_INT_STA_GPIO_MSB,
+ [STMPE_IDX_GPEDR_MSB] = STMPE1601_REG_GPIO_ED_MSB,
+};
+
+static struct stmpe_variant_block stmpe1601_blocks[] = {
+ {
+ .cell = &stmpe_gpio_cell,
+ .irq = STMPE24XX_IRQ_GPIOC,
+ .block = STMPE_BLOCK_GPIO,
+ },
+ {
+ .cell = &stmpe_keypad_cell,
+ .irq = STMPE24XX_IRQ_KEYPAD,
+ .block = STMPE_BLOCK_KEYPAD,
+ },
+};
+
+/* supported autosleep timeout delay (in msecs) */
+static const int stmpe_autosleep_delay[] = {
+ 4, 16, 32, 64, 128, 256, 512, 1024,
+};
+
+static int stmpe_round_timeout(int timeout)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(stmpe_autosleep_delay); i++) {
+ if (stmpe_autosleep_delay[i] >= timeout)
+ return i;
+ }
+
+ /*
+ * requests for delays longer than supported should not return the
+ * longest supported delay
+ */
+ return -EINVAL;
+}
+
+static int stmpe_autosleep(struct stmpe *stmpe, int autosleep_timeout)
+{
+ int ret;
+
+ if (!stmpe->variant->enable_autosleep)
+ return -ENOSYS;
+
+ mutex_lock(&stmpe->lock);
+ ret = stmpe->variant->enable_autosleep(stmpe, autosleep_timeout);
+ mutex_unlock(&stmpe->lock);
+
+ return ret;
+}
+
+/*
+ * Both stmpe 1601/2403 support same layout for autosleep
+ */
+static int stmpe1601_autosleep(struct stmpe *stmpe,
+ int autosleep_timeout)
+{
+ int ret, timeout;
+
+ /* choose the best available timeout */
+ timeout = stmpe_round_timeout(autosleep_timeout);
+ if (timeout < 0) {
+ dev_err(stmpe->dev, "invalid timeout\n");
+ return timeout;
+ }
+
+ ret = __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL2,
+ STMPE1601_AUTOSLEEP_TIMEOUT_MASK,
+ timeout);
+ if (ret < 0)
+ return ret;
+
+ return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL2,
+ STPME1601_AUTOSLEEP_ENABLE,
+ STPME1601_AUTOSLEEP_ENABLE);
+}
+
+static int stmpe1601_enable(struct stmpe *stmpe, unsigned int blocks,
+ bool enable)
+{
+ unsigned int mask = 0;
+
+ if (blocks & STMPE_BLOCK_GPIO)
+ mask |= STMPE1601_SYS_CTRL_ENABLE_GPIO;
+
+ if (blocks & STMPE_BLOCK_KEYPAD)
+ mask |= STMPE1601_SYS_CTRL_ENABLE_KPC;
+
+ return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL, mask,
+ enable ? mask : 0);
+}
+
+static int stmpe1601_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
+{
+ switch (block) {
+ case STMPE_BLOCK_PWM:
+ return 2;
+
+ case STMPE_BLOCK_KEYPAD:
+ return 1;
+
+ case STMPE_BLOCK_GPIO:
+ default:
+ return 0;
+ }
+}
+
+static struct stmpe_variant_info stmpe1601 = {
+ .name = "stmpe1601",
+ .id_val = 0x0210,
+ .id_mask = 0xfff0, /* at least 0x0210 and 0x0212 */
+ .num_gpios = 16,
+ .af_bits = 2,
+ .regs = stmpe1601_regs,
+ .blocks = stmpe1601_blocks,
+ .num_blocks = ARRAY_SIZE(stmpe1601_blocks),
+ .num_irqs = STMPE1601_NR_INTERNAL_IRQS,
+ .enable = stmpe1601_enable,
+ .get_altfunc = stmpe1601_get_altfunc,
+ .enable_autosleep = stmpe1601_autosleep,
+};
+
+/*
+ * STMPE24XX
+ */
+
+static const u8 stmpe24xx_regs[] = {
+ [STMPE_IDX_CHIP_ID] = STMPE24XX_REG_CHIP_ID,
+ [STMPE_IDX_ICR_LSB] = STMPE24XX_REG_ICR_LSB,
+ [STMPE_IDX_IER_LSB] = STMPE24XX_REG_IER_LSB,
+ [STMPE_IDX_ISR_MSB] = STMPE24XX_REG_ISR_MSB,
+ [STMPE_IDX_GPMR_LSB] = STMPE24XX_REG_GPMR_LSB,
+ [STMPE_IDX_GPSR_LSB] = STMPE24XX_REG_GPSR_LSB,
+ [STMPE_IDX_GPCR_LSB] = STMPE24XX_REG_GPCR_LSB,
+ [STMPE_IDX_GPDR_LSB] = STMPE24XX_REG_GPDR_LSB,
+ [STMPE_IDX_GPRER_LSB] = STMPE24XX_REG_GPRER_LSB,
+ [STMPE_IDX_GPFER_LSB] = STMPE24XX_REG_GPFER_LSB,
+ [STMPE_IDX_GPAFR_U_MSB] = STMPE24XX_REG_GPAFR_U_MSB,
+ [STMPE_IDX_IEGPIOR_LSB] = STMPE24XX_REG_IEGPIOR_LSB,
+ [STMPE_IDX_ISGPIOR_MSB] = STMPE24XX_REG_ISGPIOR_MSB,
+ [STMPE_IDX_GPEDR_MSB] = STMPE24XX_REG_GPEDR_MSB,
+};
+
+static struct stmpe_variant_block stmpe24xx_blocks[] = {
+ {
+ .cell = &stmpe_gpio_cell,
+ .irq = STMPE24XX_IRQ_GPIOC,
+ .block = STMPE_BLOCK_GPIO,
+ },
+ {
+ .cell = &stmpe_keypad_cell,
+ .irq = STMPE24XX_IRQ_KEYPAD,
+ .block = STMPE_BLOCK_KEYPAD,
+ },
+};
+
+static int stmpe24xx_enable(struct stmpe *stmpe, unsigned int blocks,
+ bool enable)
+{
+ unsigned int mask = 0;
+
+ if (blocks & STMPE_BLOCK_GPIO)
+ mask |= STMPE24XX_SYS_CTRL_ENABLE_GPIO;
+
+ if (blocks & STMPE_BLOCK_KEYPAD)
+ mask |= STMPE24XX_SYS_CTRL_ENABLE_KPC;
+
+ return __stmpe_set_bits(stmpe, STMPE24XX_REG_SYS_CTRL, mask,
+ enable ? mask : 0);
+}
+
+static int stmpe24xx_get_altfunc(struct stmpe *stmpe, enum stmpe_block block)
+{
+ switch (block) {
+ case STMPE_BLOCK_ROTATOR:
+ return 2;
+
+ case STMPE_BLOCK_KEYPAD:
+ return 1;
+
+ case STMPE_BLOCK_GPIO:
+ default:
+ return 0;
+ }
+}
+
+static struct stmpe_variant_info stmpe2401 = {
+ .name = "stmpe2401",
+ .id_val = 0x0101,
+ .id_mask = 0xffff,
+ .num_gpios = 24,
+ .af_bits = 2,
+ .regs = stmpe24xx_regs,
+ .blocks = stmpe24xx_blocks,
+ .num_blocks = ARRAY_SIZE(stmpe24xx_blocks),
+ .num_irqs = STMPE24XX_NR_INTERNAL_IRQS,
+ .enable = stmpe24xx_enable,
+ .get_altfunc = stmpe24xx_get_altfunc,
+};
+
+static struct stmpe_variant_info stmpe2403 = {
+ .name = "stmpe2403",
+ .id_val = 0x0120,
+ .id_mask = 0xffff,
+ .num_gpios = 24,
+ .af_bits = 2,
+ .regs = stmpe24xx_regs,
+ .blocks = stmpe24xx_blocks,
+ .num_blocks = ARRAY_SIZE(stmpe24xx_blocks),
+ .num_irqs = STMPE24XX_NR_INTERNAL_IRQS,
+ .enable = stmpe24xx_enable,
+ .get_altfunc = stmpe24xx_get_altfunc,
+ .enable_autosleep = stmpe1601_autosleep, /* same as stmpe1601 */
+};
+
+static struct stmpe_variant_info *stmpe_variant_info[] = {
+ [STMPE811] = &stmpe811,
+ [STMPE1601] = &stmpe1601,
+ [STMPE2401] = &stmpe2401,
+ [STMPE2403] = &stmpe2403,
+};
+
+static irqreturn_t stmpe_irq(int irq, void *data)
+{
+ struct stmpe *stmpe = data;
+ struct stmpe_variant_info *variant = stmpe->variant;
+ int num = DIV_ROUND_UP(variant->num_irqs, 8);
+ u8 israddr = stmpe->regs[STMPE_IDX_ISR_MSB];
+ u8 isr[num];
+ int ret;
+ int i;
+
+ ret = stmpe_block_read(stmpe, israddr, num, isr);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ for (i = 0; i < num; i++) {
+ int bank = num - i - 1;
+ u8 status = isr[i];
+ u8 clear;
+
+ status &= stmpe->ier[bank];
+ if (!status)
+ continue;
+
+ clear = status;
+ while (status) {
+ int bit = __ffs(status);
+ int line = bank * 8 + bit;
+
+ handle_nested_irq(stmpe->irq_base + line);
+ status &= ~(1 << bit);
+ }
+
+ stmpe_reg_write(stmpe, israddr + i, clear);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void stmpe_irq_lock(unsigned int irq)
+{
+ struct stmpe *stmpe = get_irq_chip_data(irq);
+
+ mutex_lock(&stmpe->irq_lock);
+}
+
+static void stmpe_irq_sync_unlock(unsigned int irq)
+{
+ struct stmpe *stmpe = get_irq_chip_data(irq);
+ struct stmpe_variant_info *variant = stmpe->variant;
+ int num = DIV_ROUND_UP(variant->num_irqs, 8);
+ int i;
+
+ for (i = 0; i < num; i++) {
+ u8 new = stmpe->ier[i];
+ u8 old = stmpe->oldier[i];
+
+ if (new == old)
+ continue;
+
+ stmpe->oldier[i] = new;
+ stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_IER_LSB] - i, new);
+ }
+
+ mutex_unlock(&stmpe->irq_lock);
+}
+
+static void stmpe_irq_mask(unsigned int irq)
+{
+ struct stmpe *stmpe = get_irq_chip_data(irq);
+ int offset = irq - stmpe->irq_base;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ stmpe->ier[regoffset] &= ~mask;
+}
+
+static void stmpe_irq_unmask(unsigned int irq)
+{
+ struct stmpe *stmpe = get_irq_chip_data(irq);
+ int offset = irq - stmpe->irq_base;
+ int regoffset = offset / 8;
+ int mask = 1 << (offset % 8);
+
+ stmpe->ier[regoffset] |= mask;
+}
+
+static struct irq_chip stmpe_irq_chip = {
+ .name = "stmpe",
+ .bus_lock = stmpe_irq_lock,
+ .bus_sync_unlock = stmpe_irq_sync_unlock,
+ .mask = stmpe_irq_mask,
+ .unmask = stmpe_irq_unmask,
+};
+
+static int __devinit stmpe_irq_init(struct stmpe *stmpe)
+{
+ int num_irqs = stmpe->variant->num_irqs;
+ int base = stmpe->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + num_irqs; irq++) {
+ set_irq_chip_data(irq, stmpe);
+ set_irq_chip_and_handler(irq, &stmpe_irq_chip,
+ handle_edge_irq);
+ set_irq_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ set_irq_noprobe(irq);
+#endif
+ }
+
+ return 0;
+}
+
+static void stmpe_irq_remove(struct stmpe *stmpe)
+{
+ int num_irqs = stmpe->variant->num_irqs;
+ int base = stmpe->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + num_irqs; irq++) {
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#endif
+ set_irq_chip_and_handler(irq, NULL, NULL);
+ set_irq_chip_data(irq, NULL);
+ }
+}
+
+static int __devinit stmpe_chip_init(struct stmpe *stmpe)
+{
+ unsigned int irq_trigger = stmpe->pdata->irq_trigger;
+ int autosleep_timeout = stmpe->pdata->autosleep_timeout;
+ struct stmpe_variant_info *variant = stmpe->variant;
+ u8 icr = STMPE_ICR_LSB_GIM;
+ unsigned int id;
+ u8 data[2];
+ int ret;
+
+ ret = stmpe_block_read(stmpe, stmpe->regs[STMPE_IDX_CHIP_ID],
+ ARRAY_SIZE(data), data);
+ if (ret < 0)
+ return ret;
+
+ id = (data[0] << 8) | data[1];
+ if ((id & variant->id_mask) != variant->id_val) {
+ dev_err(stmpe->dev, "unknown chip id: %#x\n", id);
+ return -EINVAL;
+ }
+
+ dev_info(stmpe->dev, "%s detected, chip id: %#x\n", variant->name, id);
+
+ /* Disable all modules -- subdrivers should enable what they need. */
+ ret = stmpe_disable(stmpe, ~0);
+ if (ret)
+ return ret;
+
+ if (irq_trigger == IRQF_TRIGGER_FALLING ||
+ irq_trigger == IRQF_TRIGGER_RISING)
+ icr |= STMPE_ICR_LSB_EDGE;
+
+ if (irq_trigger == IRQF_TRIGGER_RISING ||
+ irq_trigger == IRQF_TRIGGER_HIGH)
+ icr |= STMPE_ICR_LSB_HIGH;
+
+ if (stmpe->pdata->irq_invert_polarity)
+ icr ^= STMPE_ICR_LSB_HIGH;
+
+ if (stmpe->pdata->autosleep) {
+ ret = stmpe_autosleep(stmpe, autosleep_timeout);
+ if (ret)
+ return ret;
+ }
+
+ return stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_ICR_LSB], icr);
+}
+
+static int __devinit stmpe_add_device(struct stmpe *stmpe,
+ struct mfd_cell *cell, int irq)
+{
+ return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1,
+ NULL, stmpe->irq_base + irq);
+}
+
+static int __devinit stmpe_devices_init(struct stmpe *stmpe)
+{
+ struct stmpe_variant_info *variant = stmpe->variant;
+ unsigned int platform_blocks = stmpe->pdata->blocks;
+ int ret = -EINVAL;
+ int i;
+
+ for (i = 0; i < variant->num_blocks; i++) {
+ struct stmpe_variant_block *block = &variant->blocks[i];
+
+ if (!(platform_blocks & block->block))
+ continue;
+
+ platform_blocks &= ~block->block;
+ ret = stmpe_add_device(stmpe, block->cell, block->irq);
+ if (ret)
+ return ret;
+ }
+
+ if (platform_blocks)
+ dev_warn(stmpe->dev,
+ "platform wants blocks (%#x) not present on variant",
+ platform_blocks);
+
+ return ret;
+}
+
+static int __devinit stmpe_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct stmpe_platform_data *pdata = i2c->dev.platform_data;
+ struct stmpe *stmpe;
+ int ret;
+
+ if (!pdata)
+ return -EINVAL;
+
+ stmpe = kzalloc(sizeof(struct stmpe), GFP_KERNEL);
+ if (!stmpe)
+ return -ENOMEM;
+
+ mutex_init(&stmpe->irq_lock);
+ mutex_init(&stmpe->lock);
+
+ stmpe->dev = &i2c->dev;
+ stmpe->i2c = i2c;
+
+ stmpe->pdata = pdata;
+ stmpe->irq_base = pdata->irq_base;
+
+ stmpe->partnum = id->driver_data;
+ stmpe->variant = stmpe_variant_info[stmpe->partnum];
+ stmpe->regs = stmpe->variant->regs;
+ stmpe->num_gpios = stmpe->variant->num_gpios;
+
+ i2c_set_clientdata(i2c, stmpe);
+
+ ret = stmpe_chip_init(stmpe);
+ if (ret)
+ goto out_free;
+
+ ret = stmpe_irq_init(stmpe);
+ if (ret)
+ goto out_free;
+
+ ret = request_threaded_irq(stmpe->i2c->irq, NULL, stmpe_irq,
+ pdata->irq_trigger | IRQF_ONESHOT,
+ "stmpe", stmpe);
+ if (ret) {
+ dev_err(stmpe->dev, "failed to request IRQ: %d\n", ret);
+ goto out_removeirq;
+ }
+
+ ret = stmpe_devices_init(stmpe);
+ if (ret) {
+ dev_err(stmpe->dev, "failed to add children\n");
+ goto out_removedevs;
+ }
+
+ return 0;
+
+out_removedevs:
+ mfd_remove_devices(stmpe->dev);
+ free_irq(stmpe->i2c->irq, stmpe);
+out_removeirq:
+ stmpe_irq_remove(stmpe);
+out_free:
+ kfree(stmpe);
+ return ret;
+}
+
+static int __devexit stmpe_remove(struct i2c_client *client)
+{
+ struct stmpe *stmpe = i2c_get_clientdata(client);
+
+ mfd_remove_devices(stmpe->dev);
+
+ free_irq(stmpe->i2c->irq, stmpe);
+ stmpe_irq_remove(stmpe);
+
+ kfree(stmpe);
+
+ return 0;
+}
+
+static const struct i2c_device_id stmpe_id[] = {
+ { "stmpe811", STMPE811 },
+ { "stmpe1601", STMPE1601 },
+ { "stmpe2401", STMPE2401 },
+ { "stmpe2403", STMPE2403 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, stmpe_id);
+
+static struct i2c_driver stmpe_driver = {
+ .driver.name = "stmpe",
+ .driver.owner = THIS_MODULE,
+ .probe = stmpe_probe,
+ .remove = __devexit_p(stmpe_remove),
+ .id_table = stmpe_id,
+};
+
+static int __init stmpe_init(void)
+{
+ return i2c_add_driver(&stmpe_driver);
+}
+subsys_initcall(stmpe_init);
+
+static void __exit stmpe_exit(void)
+{
+ i2c_del_driver(&stmpe_driver);
+}
+module_exit(stmpe_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMPE MFD core driver");
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h
new file mode 100644
index 000000000000..0dbdc4e8cd77
--- /dev/null
+++ b/drivers/mfd/stmpe.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#ifndef __STMPE_H
+#define __STMPE_H
+
+#ifdef STMPE_DUMP_BYTES
+static inline void stmpe_dump_bytes(const char *str, const void *buf,
+ size_t len)
+{
+ print_hex_dump_bytes(str, DUMP_PREFIX_OFFSET, buf, len);
+}
+#else
+static inline void stmpe_dump_bytes(const char *str, const void *buf,
+ size_t len)
+{
+}
+#endif
+
+/**
+ * struct stmpe_variant_block - information about block
+ * @cell: base mfd cell
+ * @irq: interrupt number to be added to each IORESOURCE_IRQ
+ * in the cell
+ * @block: block id; used for identification with platform data and for
+ * enable and altfunc callbacks
+ */
+struct stmpe_variant_block {
+ struct mfd_cell *cell;
+ int irq;
+ enum stmpe_block block;
+};
+
+/**
+ * struct stmpe_variant_info - variant-specific information
+ * @name: part name
+ * @id_val: content of CHIPID register
+ * @id_mask: bits valid in CHIPID register for comparison with id_val
+ * @num_gpios: number of GPIOS
+ * @af_bits: number of bits used to specify the alternate function
+ * @blocks: list of blocks present on this device
+ * @num_blocks: number of blocks present on this device
+ * @num_irqs: number of internal IRQs available on this device
+ * @enable: callback to enable the specified blocks.
+ * Called with the I/O lock held.
+ * @get_altfunc: callback to get the alternate function number for the
+ * specific block
+ * @enable_autosleep: callback to configure autosleep with specified timeout
+ */
+struct stmpe_variant_info {
+ const char *name;
+ u16 id_val;
+ u16 id_mask;
+ int num_gpios;
+ int af_bits;
+ const u8 *regs;
+ struct stmpe_variant_block *blocks;
+ int num_blocks;
+ int num_irqs;
+ int (*enable)(struct stmpe *stmpe, unsigned int blocks, bool enable);
+ int (*get_altfunc)(struct stmpe *stmpe, enum stmpe_block block);
+ int (*enable_autosleep)(struct stmpe *stmpe, int autosleep_timeout);
+};
+
+#define STMPE_ICR_LSB_HIGH (1 << 2)
+#define STMPE_ICR_LSB_EDGE (1 << 1)
+#define STMPE_ICR_LSB_GIM (1 << 0)
+
+/*
+ * STMPE811
+ */
+
+#define STMPE811_IRQ_TOUCH_DET 0
+#define STMPE811_IRQ_FIFO_TH 1
+#define STMPE811_IRQ_FIFO_OFLOW 2
+#define STMPE811_IRQ_FIFO_FULL 3
+#define STMPE811_IRQ_FIFO_EMPTY 4
+#define STMPE811_IRQ_TEMP_SENS 5
+#define STMPE811_IRQ_ADC 6
+#define STMPE811_IRQ_GPIOC 7
+#define STMPE811_NR_INTERNAL_IRQS 8
+
+#define STMPE811_REG_CHIP_ID 0x00
+#define STMPE811_REG_SYS_CTRL2 0x04
+#define STMPE811_REG_INT_CTRL 0x09
+#define STMPE811_REG_INT_EN 0x0A
+#define STMPE811_REG_INT_STA 0x0B
+#define STMPE811_REG_GPIO_INT_EN 0x0C
+#define STMPE811_REG_GPIO_INT_STA 0x0D
+#define STMPE811_REG_GPIO_SET_PIN 0x10
+#define STMPE811_REG_GPIO_CLR_PIN 0x11
+#define STMPE811_REG_GPIO_MP_STA 0x12
+#define STMPE811_REG_GPIO_DIR 0x13
+#define STMPE811_REG_GPIO_ED 0x14
+#define STMPE811_REG_GPIO_RE 0x15
+#define STMPE811_REG_GPIO_FE 0x16
+#define STMPE811_REG_GPIO_AF 0x17
+
+#define STMPE811_SYS_CTRL2_ADC_OFF (1 << 0)
+#define STMPE811_SYS_CTRL2_TSC_OFF (1 << 1)
+#define STMPE811_SYS_CTRL2_GPIO_OFF (1 << 2)
+#define STMPE811_SYS_CTRL2_TS_OFF (1 << 3)
+
+/*
+ * STMPE1601
+ */
+
+#define STMPE1601_IRQ_GPIOC 8
+#define STMPE1601_IRQ_PWM3 7
+#define STMPE1601_IRQ_PWM2 6
+#define STMPE1601_IRQ_PWM1 5
+#define STMPE1601_IRQ_PWM0 4
+#define STMPE1601_IRQ_KEYPAD_OVER 2
+#define STMPE1601_IRQ_KEYPAD 1
+#define STMPE1601_IRQ_WAKEUP 0
+#define STMPE1601_NR_INTERNAL_IRQS 9
+
+#define STMPE1601_REG_SYS_CTRL 0x02
+#define STMPE1601_REG_SYS_CTRL2 0x03
+#define STMPE1601_REG_ICR_LSB 0x11
+#define STMPE1601_REG_IER_LSB 0x13
+#define STMPE1601_REG_ISR_MSB 0x14
+#define STMPE1601_REG_CHIP_ID 0x80
+#define STMPE1601_REG_INT_EN_GPIO_MASK_LSB 0x17
+#define STMPE1601_REG_INT_STA_GPIO_MSB 0x18
+#define STMPE1601_REG_GPIO_MP_LSB 0x87
+#define STMPE1601_REG_GPIO_SET_LSB 0x83
+#define STMPE1601_REG_GPIO_CLR_LSB 0x85
+#define STMPE1601_REG_GPIO_SET_DIR_LSB 0x89
+#define STMPE1601_REG_GPIO_ED_MSB 0x8A
+#define STMPE1601_REG_GPIO_RE_LSB 0x8D
+#define STMPE1601_REG_GPIO_FE_LSB 0x8F
+#define STMPE1601_REG_GPIO_AF_U_MSB 0x92
+
+#define STMPE1601_SYS_CTRL_ENABLE_GPIO (1 << 3)
+#define STMPE1601_SYS_CTRL_ENABLE_KPC (1 << 1)
+#define STMPE1601_SYSCON_ENABLE_SPWM (1 << 0)
+
+/* The 1601/2403 share the same masks */
+#define STMPE1601_AUTOSLEEP_TIMEOUT_MASK (0x7)
+#define STPME1601_AUTOSLEEP_ENABLE (1 << 3)
+
+/*
+ * STMPE24xx
+ */
+
+#define STMPE24XX_IRQ_GPIOC 8
+#define STMPE24XX_IRQ_PWM2 7
+#define STMPE24XX_IRQ_PWM1 6
+#define STMPE24XX_IRQ_PWM0 5
+#define STMPE24XX_IRQ_ROT_OVER 4
+#define STMPE24XX_IRQ_ROT 3
+#define STMPE24XX_IRQ_KEYPAD_OVER 2
+#define STMPE24XX_IRQ_KEYPAD 1
+#define STMPE24XX_IRQ_WAKEUP 0
+#define STMPE24XX_NR_INTERNAL_IRQS 9
+
+#define STMPE24XX_REG_SYS_CTRL 0x02
+#define STMPE24XX_REG_ICR_LSB 0x11
+#define STMPE24XX_REG_IER_LSB 0x13
+#define STMPE24XX_REG_ISR_MSB 0x14
+#define STMPE24XX_REG_CHIP_ID 0x80
+#define STMPE24XX_REG_IEGPIOR_LSB 0x18
+#define STMPE24XX_REG_ISGPIOR_MSB 0x19
+#define STMPE24XX_REG_GPMR_LSB 0xA5
+#define STMPE24XX_REG_GPSR_LSB 0x85
+#define STMPE24XX_REG_GPCR_LSB 0x88
+#define STMPE24XX_REG_GPDR_LSB 0x8B
+#define STMPE24XX_REG_GPEDR_MSB 0x8C
+#define STMPE24XX_REG_GPRER_LSB 0x91
+#define STMPE24XX_REG_GPFER_LSB 0x94
+#define STMPE24XX_REG_GPAFR_U_MSB 0x9B
+
+#define STMPE24XX_SYS_CTRL_ENABLE_GPIO (1 << 3)
+#define STMPE24XX_SYSCON_ENABLE_PWM (1 << 2)
+#define STMPE24XX_SYS_CTRL_ENABLE_KPC (1 << 1)
+#define STMPE24XX_SYSCON_ENABLE_ROT (1 << 0)
+
+#endif
diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c
index 5041d33adf0b..006c121f3f0d 100644
--- a/drivers/mfd/t7l66xb.c
+++ b/drivers/mfd/t7l66xb.c
@@ -350,7 +350,6 @@ static int t7l66xb_probe(struct platform_device *dev)
t7l66xb->clk48m = clk_get(&dev->dev, "CLK_CK48M");
if (IS_ERR(t7l66xb->clk48m)) {
ret = PTR_ERR(t7l66xb->clk48m);
- clk_put(t7l66xb->clk32k);
goto err_clk48m_get;
}
@@ -425,6 +424,8 @@ static int t7l66xb_remove(struct platform_device *dev)
ret = pdata->disable(dev);
clk_disable(t7l66xb->clk48m);
clk_put(t7l66xb->clk48m);
+ clk_disable(t7l66xb->clk32k);
+ clk_put(t7l66xb->clk32k);
t7l66xb_detach_irq(dev);
iounmap(t7l66xb->scr);
release_resource(&t7l66xb->rscr);
diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c
index 517f9bcdeaac..6315f63f017d 100644
--- a/drivers/mfd/tc6387xb.c
+++ b/drivers/mfd/tc6387xb.c
@@ -137,7 +137,7 @@ static struct mfd_cell tc6387xb_cells[] = {
},
};
-static int tc6387xb_probe(struct platform_device *dev)
+static int __devinit tc6387xb_probe(struct platform_device *dev)
{
struct tc6387xb_platform_data *pdata = dev->dev.platform_data;
struct resource *iomem, *rscr;
@@ -201,6 +201,7 @@ static int tc6387xb_probe(struct platform_device *dev)
if (!ret)
return 0;
+ iounmap(tc6387xb->scr);
err_ioremap:
release_resource(&tc6387xb->rscr);
err_resource:
@@ -211,14 +212,17 @@ err_no_irq:
return ret;
}
-static int tc6387xb_remove(struct platform_device *dev)
+static int __devexit tc6387xb_remove(struct platform_device *dev)
{
- struct clk *clk32k = platform_get_drvdata(dev);
+ struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
mfd_remove_devices(&dev->dev);
- clk_disable(clk32k);
- clk_put(clk32k);
+ iounmap(tc6387xb->scr);
+ release_resource(&tc6387xb->rscr);
+ clk_disable(tc6387xb->clk32k);
+ clk_put(tc6387xb->clk32k);
platform_set_drvdata(dev, NULL);
+ kfree(tc6387xb);
return 0;
}
@@ -229,7 +233,7 @@ static struct platform_driver tc6387xb_platform_driver = {
.name = "tc6387xb",
},
.probe = tc6387xb_probe,
- .remove = tc6387xb_remove,
+ .remove = __devexit_p(tc6387xb_remove),
.suspend = tc6387xb_suspend,
.resume = tc6387xb_resume,
};
diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c
index fcf9068810fb..ef6c42c8917a 100644
--- a/drivers/mfd/tc6393xb.c
+++ b/drivers/mfd/tc6393xb.c
@@ -732,9 +732,9 @@ err_gpio_add:
if (tc6393xb->gpio.base != -1)
temp = gpiochip_remove(&tc6393xb->gpio);
tcpd->disable(dev);
-err_clk_enable:
- clk_disable(tc6393xb->clk);
err_enable:
+ clk_disable(tc6393xb->clk);
+err_clk_enable:
iounmap(tc6393xb->scr);
err_ioremap:
release_resource(&tc6393xb->rscr);
diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c
index d859dffed39f..fc0197649281 100644
--- a/drivers/mfd/tps6507x.c
+++ b/drivers/mfd/tps6507x.c
@@ -89,10 +89,8 @@ static int tps6507x_i2c_probe(struct i2c_client *i2c,
int ret = 0;
tps6507x = kzalloc(sizeof(struct tps6507x_dev), GFP_KERNEL);
- if (tps6507x == NULL) {
- kfree(i2c);
+ if (tps6507x == NULL)
return -ENOMEM;
- }
i2c_set_clientdata(i2c, tps6507x);
tps6507x->dev = &i2c->dev;
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
new file mode 100644
index 000000000000..4cde31e6a252
--- /dev/null
+++ b/drivers/mfd/tps6586x.c
@@ -0,0 +1,375 @@
+/*
+ * Core driver for TI TPS6586x PMIC family
+ *
+ * Copyright (c) 2010 CompuLab Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * Based on da903x.c.
+ * Copyright (C) 2008 Compulab, Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * Eric Miao <eric.miao@marvell.com>
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps6586x.h>
+
+/* GPIO control registers */
+#define TPS6586X_GPIOSET1 0x5d
+#define TPS6586X_GPIOSET2 0x5e
+
+/* device id */
+#define TPS6586X_VERSIONCRC 0xcd
+#define TPS658621A_VERSIONCRC 0x15
+
+struct tps6586x {
+ struct mutex lock;
+ struct device *dev;
+ struct i2c_client *client;
+
+ struct gpio_chip gpio;
+};
+
+static inline int __tps6586x_read(struct i2c_client *client,
+ int reg, uint8_t *val)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
+ return ret;
+ }
+
+ *val = (uint8_t)ret;
+
+ return 0;
+}
+
+static inline int __tps6586x_reads(struct i2c_client *client, int reg,
+ int len, uint8_t *val)
+{
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(client, reg, len, val);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed reading from 0x%02x\n", reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int __tps6586x_write(struct i2c_client *client,
+ int reg, uint8_t val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
+ val, reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int __tps6586x_writes(struct i2c_client *client, int reg,
+ int len, uint8_t *val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_i2c_block_data(client, reg, len, val);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed writings to 0x%02x\n", reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+int tps6586x_write(struct device *dev, int reg, uint8_t val)
+{
+ return __tps6586x_write(to_i2c_client(dev), reg, val);
+}
+EXPORT_SYMBOL_GPL(tps6586x_write);
+
+int tps6586x_writes(struct device *dev, int reg, int len, uint8_t *val)
+{
+ return __tps6586x_writes(to_i2c_client(dev), reg, len, val);
+}
+EXPORT_SYMBOL_GPL(tps6586x_writes);
+
+int tps6586x_read(struct device *dev, int reg, uint8_t *val)
+{
+ return __tps6586x_read(to_i2c_client(dev), reg, val);
+}
+EXPORT_SYMBOL_GPL(tps6586x_read);
+
+int tps6586x_reads(struct device *dev, int reg, int len, uint8_t *val)
+{
+ return __tps6586x_reads(to_i2c_client(dev), reg, len, val);
+}
+EXPORT_SYMBOL_GPL(tps6586x_reads);
+
+int tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask)
+{
+ struct tps6586x *tps6586x = dev_get_drvdata(dev);
+ uint8_t reg_val;
+ int ret = 0;
+
+ mutex_lock(&tps6586x->lock);
+
+ ret = __tps6586x_read(to_i2c_client(dev), reg, &reg_val);
+ if (ret)
+ goto out;
+
+ if ((reg_val & bit_mask) == 0) {
+ reg_val |= bit_mask;
+ ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val);
+ }
+out:
+ mutex_unlock(&tps6586x->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tps6586x_set_bits);
+
+int tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
+{
+ struct tps6586x *tps6586x = dev_get_drvdata(dev);
+ uint8_t reg_val;
+ int ret = 0;
+
+ mutex_lock(&tps6586x->lock);
+
+ ret = __tps6586x_read(to_i2c_client(dev), reg, &reg_val);
+ if (ret)
+ goto out;
+
+ if (reg_val & bit_mask) {
+ reg_val &= ~bit_mask;
+ ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val);
+ }
+out:
+ mutex_unlock(&tps6586x->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tps6586x_clr_bits);
+
+int tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask)
+{
+ struct tps6586x *tps6586x = dev_get_drvdata(dev);
+ uint8_t reg_val;
+ int ret = 0;
+
+ mutex_lock(&tps6586x->lock);
+
+ ret = __tps6586x_read(tps6586x->client, reg, &reg_val);
+ if (ret)
+ goto out;
+
+ if ((reg_val & mask) != val) {
+ reg_val = (reg_val & ~mask) | val;
+ ret = __tps6586x_write(tps6586x->client, reg, reg_val);
+ }
+out:
+ mutex_unlock(&tps6586x->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tps6586x_update);
+
+static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
+ uint8_t val;
+ int ret;
+
+ ret = __tps6586x_read(tps6586x->client, TPS6586X_GPIOSET2, &val);
+ if (ret)
+ return ret;
+
+ return !!(val & (1 << offset));
+}
+
+
+static void tps6586x_gpio_set(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct tps6586x *tps6586x = container_of(chip, struct tps6586x, gpio);
+
+ __tps6586x_write(tps6586x->client, TPS6586X_GPIOSET2,
+ value << offset);
+}
+
+static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
+ uint8_t val, mask;
+
+ tps6586x_gpio_set(gc, offset, value);
+
+ val = 0x1 << (offset * 2);
+ mask = 0x3 << (offset * 2);
+
+ return tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET1, val, mask);
+}
+
+static void tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base)
+{
+ int ret;
+
+ if (!gpio_base)
+ return;
+
+ tps6586x->gpio.owner = THIS_MODULE;
+ tps6586x->gpio.label = tps6586x->client->name;
+ tps6586x->gpio.dev = tps6586x->dev;
+ tps6586x->gpio.base = gpio_base;
+ tps6586x->gpio.ngpio = 4;
+ tps6586x->gpio.can_sleep = 1;
+
+ /* FIXME: add handling of GPIOs as dedicated inputs */
+ tps6586x->gpio.direction_output = tps6586x_gpio_output;
+ tps6586x->gpio.set = tps6586x_gpio_set;
+ tps6586x->gpio.get = tps6586x_gpio_get;
+
+ ret = gpiochip_add(&tps6586x->gpio);
+ if (ret)
+ dev_warn(tps6586x->dev, "GPIO registration failed: %d\n", ret);
+}
+
+static int __remove_subdev(struct device *dev, void *unused)
+{
+ platform_device_unregister(to_platform_device(dev));
+ return 0;
+}
+
+static int tps6586x_remove_subdevs(struct tps6586x *tps6586x)
+{
+ return device_for_each_child(tps6586x->dev, NULL, __remove_subdev);
+}
+
+static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x,
+ struct tps6586x_platform_data *pdata)
+{
+ struct tps6586x_subdev_info *subdev;
+ struct platform_device *pdev;
+ int i, ret = 0;
+
+ for (i = 0; i < pdata->num_subdevs; i++) {
+ subdev = &pdata->subdevs[i];
+
+ pdev = platform_device_alloc(subdev->name, subdev->id);
+
+ pdev->dev.parent = tps6586x->dev;
+ pdev->dev.platform_data = subdev->platform_data;
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto failed;
+ }
+ return 0;
+
+failed:
+ tps6586x_remove_subdevs(tps6586x);
+ return ret;
+}
+
+static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tps6586x_platform_data *pdata = client->dev.platform_data;
+ struct tps6586x *tps6586x;
+ int ret;
+
+ if (!pdata) {
+ dev_err(&client->dev, "tps6586x requires platform data\n");
+ return -ENOTSUPP;
+ }
+
+ ret = i2c_smbus_read_byte_data(client, TPS6586X_VERSIONCRC);
+ if (ret < 0) {
+ dev_err(&client->dev, "Chip ID read failed: %d\n", ret);
+ return -EIO;
+ }
+
+ if (ret != TPS658621A_VERSIONCRC) {
+ dev_err(&client->dev, "Unsupported chip ID: %x\n", ret);
+ return -ENODEV;
+ }
+
+ tps6586x = kzalloc(sizeof(struct tps6586x), GFP_KERNEL);
+ if (tps6586x == NULL)
+ return -ENOMEM;
+
+ tps6586x->client = client;
+ tps6586x->dev = &client->dev;
+ i2c_set_clientdata(client, tps6586x);
+
+ mutex_init(&tps6586x->lock);
+
+ ret = tps6586x_add_subdevs(tps6586x, pdata);
+ if (ret) {
+ dev_err(&client->dev, "add devices failed: %d\n", ret);
+ goto err_add_devs;
+ }
+
+ tps6586x_gpio_init(tps6586x, pdata->gpio_base);
+
+ return 0;
+
+err_add_devs:
+ kfree(tps6586x);
+ return ret;
+}
+
+static int __devexit tps6586x_i2c_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id tps6586x_id_table[] = {
+ { "tps6586x", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, tps6586x_id_table);
+
+static struct i2c_driver tps6586x_driver = {
+ .driver = {
+ .name = "tps6586x",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps6586x_i2c_probe,
+ .remove = __devexit_p(tps6586x_i2c_remove),
+ .id_table = tps6586x_id_table,
+};
+
+static int __init tps6586x_init(void)
+{
+ return i2c_add_driver(&tps6586x_driver);
+}
+subsys_initcall(tps6586x_init);
+
+static void __exit tps6586x_exit(void)
+{
+ i2c_del_driver(&tps6586x_driver);
+}
+module_exit(tps6586x_exit);
+
+MODULE_DESCRIPTION("TPS6586X core driver");
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/mfd/twl6030-pwm.c b/drivers/mfd/twl6030-pwm.c
new file mode 100644
index 000000000000..5d25bdc78424
--- /dev/null
+++ b/drivers/mfd/twl6030-pwm.c
@@ -0,0 +1,163 @@
+/*
+ * twl6030_pwm.c
+ * Driver for PHOENIX (TWL6030) Pulse Width Modulator
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Hemanth V <hemanthv@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl.h>
+#include <linux/slab.h>
+
+#define LED_PWM_CTRL1 0xF4
+#define LED_PWM_CTRL2 0xF5
+
+/* Max value for CTRL1 register */
+#define PWM_CTRL1_MAX 255
+
+/* Pull down disable */
+#define PWM_CTRL2_DIS_PD (1 << 6)
+
+/* Current control 2.5 milli Amps */
+#define PWM_CTRL2_CURR_02 (2 << 4)
+
+/* LED supply source */
+#define PWM_CTRL2_SRC_VAC (1 << 2)
+
+/* LED modes */
+#define PWM_CTRL2_MODE_HW (0 << 0)
+#define PWM_CTRL2_MODE_SW (1 << 0)
+#define PWM_CTRL2_MODE_DIS (2 << 0)
+
+#define PWM_CTRL2_MODE_MASK 0x3
+
+struct pwm_device {
+ const char *label;
+ unsigned int pwm_id;
+};
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+ u8 duty_cycle;
+ int ret;
+
+ if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+ return -EINVAL;
+
+ duty_cycle = (duty_ns * PWM_CTRL1_MAX) / period_ns;
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, duty_cycle, LED_PWM_CTRL1);
+
+ if (ret < 0) {
+ pr_err("%s: Failed to configure PWM, Error %d\n",
+ pwm->label, ret);
+ return ret;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+ u8 val;
+ int ret;
+
+ ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
+ if (ret < 0) {
+ pr_err("%s: Failed to enable PWM, Error %d\n", pwm->label, ret);
+ return ret;
+ }
+
+ /* Change mode to software control */
+ val &= ~PWM_CTRL2_MODE_MASK;
+ val |= PWM_CTRL2_MODE_SW;
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
+ if (ret < 0) {
+ pr_err("%s: Failed to enable PWM, Error %d\n", pwm->label, ret);
+ return ret;
+ }
+
+ twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
+ return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+ u8 val;
+ int ret;
+
+ ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2);
+ if (ret < 0) {
+ pr_err("%s: Failed to disable PWM, Error %d\n",
+ pwm->label, ret);
+ return;
+ }
+
+ val &= ~PWM_CTRL2_MODE_MASK;
+ val |= PWM_CTRL2_MODE_HW;
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
+ if (ret < 0) {
+ pr_err("%s: Failed to disable PWM, Error %d\n",
+ pwm->label, ret);
+ return;
+ }
+ return;
+}
+EXPORT_SYMBOL(pwm_disable);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+ u8 val;
+ int ret;
+ struct pwm_device *pwm;
+
+ pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
+ if (pwm == NULL) {
+ pr_err("%s: failed to allocate memory\n", label);
+ return NULL;
+ }
+
+ pwm->label = label;
+ pwm->pwm_id = pwm_id;
+
+ /* Configure PWM */
+ val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VAC |
+ PWM_CTRL2_MODE_HW;
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2);
+
+ if (ret < 0) {
+ pr_err("%s: Failed to configure PWM, Error %d\n",
+ pwm->label, ret);
+
+ kfree(pwm);
+ return NULL;
+ }
+
+ return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+ pwm_disable(pwm);
+ kfree(pwm);
+}
+EXPORT_SYMBOL(pwm_free);
diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c
index dbe280153f9e..d73f84ba0f08 100644
--- a/drivers/mfd/ucb1400_core.c
+++ b/drivers/mfd/ucb1400_core.c
@@ -114,7 +114,7 @@ static int ucb1400_core_probe(struct device *dev)
err3:
platform_device_put(ucb->ucb1400_ts);
err2:
- platform_device_unregister(ucb->ucb1400_gpio);
+ platform_device_del(ucb->ucb1400_gpio);
err1:
platform_device_put(ucb->ucb1400_gpio);
err0:
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c
index 1a968f34d679..1e7aaaf6cc6f 100644
--- a/drivers/mfd/wm831x-core.c
+++ b/drivers/mfd/wm831x-core.c
@@ -95,6 +95,7 @@ enum wm831x_parent {
WM8311 = 0x8311,
WM8312 = 0x8312,
WM8320 = 0x8320,
+ WM8321 = 0x8321,
};
static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg)
@@ -1533,6 +1534,12 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
dev_info(wm831x->dev, "WM8320 revision %c\n", 'A' + rev);
break;
+ case WM8321:
+ parent = WM8321;
+ wm831x->num_gpio = 12;
+ dev_info(wm831x->dev, "WM8321 revision %c\n", 'A' + rev);
+ break;
+
default:
dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
ret = -EINVAL;
@@ -1607,6 +1614,12 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
NULL, 0);
break;
+ case WM8321:
+ ret = mfd_add_devices(wm831x->dev, -1,
+ wm8320_devs, ARRAY_SIZE(wm8320_devs),
+ NULL, 0);
+ break;
+
default:
/* If this happens the bus probe function is buggy */
BUG();
@@ -1744,10 +1757,8 @@ static int wm831x_i2c_probe(struct i2c_client *i2c,
struct wm831x *wm831x;
wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
- if (wm831x == NULL) {
- kfree(i2c);
+ if (wm831x == NULL)
return -ENOMEM;
- }
i2c_set_clientdata(i2c, wm831x);
wm831x->dev = &i2c->dev;
@@ -1779,6 +1790,7 @@ static const struct i2c_device_id wm831x_i2c_id[] = {
{ "wm8311", WM8311 },
{ "wm8312", WM8312 },
{ "wm8320", WM8320 },
+ { "wm8321", WM8321 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index b5807484b4c9..e81cc31e4202 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -536,6 +536,7 @@ static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode)
}
out:
+ kfree(wm8350->reg_cache);
return ret;
}
@@ -700,7 +701,7 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
ret = wm8350_irq_init(wm8350, irq, pdata);
if (ret < 0)
- goto err;
+ goto err_free;
if (wm8350->irq_base) {
ret = request_threaded_irq(wm8350->irq_base +
@@ -738,8 +739,9 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
err_irq:
wm8350_irq_exit(wm8350);
-err:
+err_free:
kfree(wm8350->reg_cache);
+err:
return ret;
}
EXPORT_SYMBOL_GPL(wm8350_device_init);
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index ec71c9368906..b3b2aaf89dbe 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -326,8 +326,10 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq)
wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
ARRAY_SIZE(wm8994_main_supplies),
GFP_KERNEL);
- if (!wm8994->supplies)
+ if (!wm8994->supplies) {
+ ret = -ENOMEM;
goto err;
+ }
for (i = 0; i < ARRAY_SIZE(wm8994_main_supplies); i++)
wm8994->supplies[i].supply = wm8994_main_supplies[i];
@@ -495,10 +497,8 @@ static int wm8994_i2c_probe(struct i2c_client *i2c,
struct wm8994 *wm8994;
wm8994 = kzalloc(sizeof(struct wm8994), GFP_KERNEL);
- if (wm8994 == NULL) {
- kfree(i2c);
+ if (wm8994 == NULL)
return -ENOMEM;
- }
i2c_set_clientdata(i2c, wm8994);
wm8994->dev = &i2c->dev;
diff --git a/drivers/misc/cs5535-mfgpt.c b/drivers/misc/cs5535-mfgpt.c
index 2d44b3300104..6f6218061b0d 100644
--- a/drivers/misc/cs5535-mfgpt.c
+++ b/drivers/misc/cs5535-mfgpt.c
@@ -211,6 +211,17 @@ EXPORT_SYMBOL_GPL(cs5535_mfgpt_alloc_timer);
*/
void cs5535_mfgpt_free_timer(struct cs5535_mfgpt_timer *timer)
{
+ unsigned long flags;
+ uint16_t val;
+
+ /* timer can be made available again only if never set up */
+ val = cs5535_mfgpt_read(timer, MFGPT_REG_SETUP);
+ if (!(val & MFGPT_SETUP_SETUP)) {
+ spin_lock_irqsave(&timer->chip->lock, flags);
+ __set_bit(timer->nr, timer->chip->avail);
+ spin_unlock_irqrestore(&timer->chip->lock, flags);
+ }
+
kfree(timer);
}
EXPORT_SYMBOL_GPL(cs5535_mfgpt_free_timer);
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 8433cde29c8b..d545f79f6000 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -247,7 +247,76 @@ static u32 get_card_status(struct mmc_card *card, struct request *req)
return cmd.resp[0];
}
-static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
+static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
+{
+ struct mmc_blk_data *md = mq->data;
+ struct mmc_card *card = md->queue.card;
+ unsigned int from, nr, arg;
+ int err = 0;
+
+ mmc_claim_host(card->host);
+
+ if (!mmc_can_erase(card)) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ from = blk_rq_pos(req);
+ nr = blk_rq_sectors(req);
+
+ if (mmc_can_trim(card))
+ arg = MMC_TRIM_ARG;
+ else
+ arg = MMC_ERASE_ARG;
+
+ err = mmc_erase(card, from, nr, arg);
+out:
+ spin_lock_irq(&md->lock);
+ __blk_end_request(req, err, blk_rq_bytes(req));
+ spin_unlock_irq(&md->lock);
+
+ mmc_release_host(card->host);
+
+ return err ? 0 : 1;
+}
+
+static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
+ struct request *req)
+{
+ struct mmc_blk_data *md = mq->data;
+ struct mmc_card *card = md->queue.card;
+ unsigned int from, nr, arg;
+ int err = 0;
+
+ mmc_claim_host(card->host);
+
+ if (!mmc_can_secure_erase_trim(card)) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ from = blk_rq_pos(req);
+ nr = blk_rq_sectors(req);
+
+ if (mmc_can_trim(card) && !mmc_erase_group_aligned(card, from, nr))
+ arg = MMC_SECURE_TRIM1_ARG;
+ else
+ arg = MMC_SECURE_ERASE_ARG;
+
+ err = mmc_erase(card, from, nr, arg);
+ if (!err && arg == MMC_SECURE_TRIM1_ARG)
+ err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG);
+out:
+ spin_lock_irq(&md->lock);
+ __blk_end_request(req, err, blk_rq_bytes(req));
+ spin_unlock_irq(&md->lock);
+
+ mmc_release_host(card->host);
+
+ return err ? 0 : 1;
+}
+
+static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
{
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
@@ -475,6 +544,17 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
return 0;
}
+static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
+{
+ if (req->cmd_flags & REQ_DISCARD) {
+ if (req->cmd_flags & REQ_SECURE)
+ return mmc_blk_issue_secdiscard_rq(mq, req);
+ else
+ return mmc_blk_issue_discard_rq(mq, req);
+ } else {
+ return mmc_blk_issue_rw_rq(mq, req);
+ }
+}
static inline int mmc_blk_readonly(struct mmc_card *card)
{
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index 445d7db2277e..5dd8576b5c18 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/scatterlist.h>
+#include <linux/swap.h> /* For nr_free_buffer_pages() */
#define RESULT_OK 0
#define RESULT_FAIL 1
@@ -25,6 +26,60 @@
#define BUFFER_ORDER 2
#define BUFFER_SIZE (PAGE_SIZE << BUFFER_ORDER)
+/*
+ * Limit the test area size to the maximum MMC HC erase group size. Note that
+ * the maximum SD allocation unit size is just 4MiB.
+ */
+#define TEST_AREA_MAX_SIZE (128 * 1024 * 1024)
+
+/**
+ * struct mmc_test_pages - pages allocated by 'alloc_pages()'.
+ * @page: first page in the allocation
+ * @order: order of the number of pages allocated
+ */
+struct mmc_test_pages {
+ struct page *page;
+ unsigned int order;
+};
+
+/**
+ * struct mmc_test_mem - allocated memory.
+ * @arr: array of allocations
+ * @cnt: number of allocations
+ */
+struct mmc_test_mem {
+ struct mmc_test_pages *arr;
+ unsigned int cnt;
+};
+
+/**
+ * struct mmc_test_area - information for performance tests.
+ * @max_sz: test area size (in bytes)
+ * @dev_addr: address on card at which to do performance tests
+ * @max_segs: maximum segments in scatterlist @sg
+ * @blocks: number of (512 byte) blocks currently mapped by @sg
+ * @sg_len: length of currently mapped scatterlist @sg
+ * @mem: allocated memory
+ * @sg: scatterlist
+ */
+struct mmc_test_area {
+ unsigned long max_sz;
+ unsigned int dev_addr;
+ unsigned int max_segs;
+ unsigned int blocks;
+ unsigned int sg_len;
+ struct mmc_test_mem *mem;
+ struct scatterlist *sg;
+};
+
+/**
+ * struct mmc_test_card - test information.
+ * @card: card under test
+ * @scratch: transfer buffer
+ * @buffer: transfer buffer
+ * @highmem: buffer for highmem tests
+ * @area: information for performance tests
+ */
struct mmc_test_card {
struct mmc_card *card;
@@ -33,6 +88,7 @@ struct mmc_test_card {
#ifdef CONFIG_HIGHMEM
struct page *highmem;
#endif
+ struct mmc_test_area area;
};
/*******************************************************************/
@@ -97,6 +153,12 @@ static void mmc_test_prepare_mrq(struct mmc_test_card *test,
mmc_set_data_timeout(mrq->data, test->card);
}
+static int mmc_test_busy(struct mmc_command *cmd)
+{
+ return !(cmd->resp[0] & R1_READY_FOR_DATA) ||
+ (R1_CURRENT_STATE(cmd->resp[0]) == 7);
+}
+
/*
* Wait for the card to finish the busy state
*/
@@ -117,13 +179,13 @@ static int mmc_test_wait_busy(struct mmc_test_card *test)
if (ret)
break;
- if (!busy && !(cmd.resp[0] & R1_READY_FOR_DATA)) {
+ if (!busy && mmc_test_busy(&cmd)) {
busy = 1;
printk(KERN_INFO "%s: Warning: Host did not "
"wait for busy state to end.\n",
mmc_hostname(test->card->host));
}
- } while (!(cmd.resp[0] & R1_READY_FOR_DATA));
+ } while (mmc_test_busy(&cmd));
return ret;
}
@@ -170,6 +232,248 @@ static int mmc_test_buffer_transfer(struct mmc_test_card *test,
return 0;
}
+static void mmc_test_free_mem(struct mmc_test_mem *mem)
+{
+ if (!mem)
+ return;
+ while (mem->cnt--)
+ __free_pages(mem->arr[mem->cnt].page,
+ mem->arr[mem->cnt].order);
+ kfree(mem->arr);
+ kfree(mem);
+}
+
+/*
+ * Allocate a lot of memory, preferrably max_sz but at least min_sz. In case
+ * there isn't much memory do not exceed 1/16th total lowmem pages.
+ */
+static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz,
+ unsigned long max_sz)
+{
+ unsigned long max_page_cnt = DIV_ROUND_UP(max_sz, PAGE_SIZE);
+ unsigned long min_page_cnt = DIV_ROUND_UP(min_sz, PAGE_SIZE);
+ unsigned long page_cnt = 0;
+ unsigned long limit = nr_free_buffer_pages() >> 4;
+ struct mmc_test_mem *mem;
+
+ if (max_page_cnt > limit)
+ max_page_cnt = limit;
+ if (max_page_cnt < min_page_cnt)
+ max_page_cnt = min_page_cnt;
+
+ mem = kzalloc(sizeof(struct mmc_test_mem), GFP_KERNEL);
+ if (!mem)
+ return NULL;
+
+ mem->arr = kzalloc(sizeof(struct mmc_test_pages) * max_page_cnt,
+ GFP_KERNEL);
+ if (!mem->arr)
+ goto out_free;
+
+ while (max_page_cnt) {
+ struct page *page;
+ unsigned int order;
+ gfp_t flags = GFP_KERNEL | GFP_DMA | __GFP_NOWARN |
+ __GFP_NORETRY;
+
+ order = get_order(max_page_cnt << PAGE_SHIFT);
+ while (1) {
+ page = alloc_pages(flags, order);
+ if (page || !order)
+ break;
+ order -= 1;
+ }
+ if (!page) {
+ if (page_cnt < min_page_cnt)
+ goto out_free;
+ break;
+ }
+ mem->arr[mem->cnt].page = page;
+ mem->arr[mem->cnt].order = order;
+ mem->cnt += 1;
+ if (max_page_cnt <= (1UL << order))
+ break;
+ max_page_cnt -= 1UL << order;
+ page_cnt += 1UL << order;
+ }
+
+ return mem;
+
+out_free:
+ mmc_test_free_mem(mem);
+ return NULL;
+}
+
+/*
+ * Map memory into a scatterlist. Optionally allow the same memory to be
+ * mapped more than once.
+ */
+static int mmc_test_map_sg(struct mmc_test_mem *mem, unsigned long sz,
+ struct scatterlist *sglist, int repeat,
+ unsigned int max_segs, unsigned int *sg_len)
+{
+ struct scatterlist *sg = NULL;
+ unsigned int i;
+
+ sg_init_table(sglist, max_segs);
+
+ *sg_len = 0;
+ do {
+ for (i = 0; i < mem->cnt; i++) {
+ unsigned long len = PAGE_SIZE << mem->arr[i].order;
+
+ if (sz < len)
+ len = sz;
+ if (sg)
+ sg = sg_next(sg);
+ else
+ sg = sglist;
+ if (!sg)
+ return -EINVAL;
+ sg_set_page(sg, mem->arr[i].page, len, 0);
+ sz -= len;
+ *sg_len += 1;
+ if (!sz)
+ break;
+ }
+ } while (sz && repeat);
+
+ if (sz)
+ return -EINVAL;
+
+ if (sg)
+ sg_mark_end(sg);
+
+ return 0;
+}
+
+/*
+ * Map memory into a scatterlist so that no pages are contiguous. Allow the
+ * same memory to be mapped more than once.
+ */
+static int mmc_test_map_sg_max_scatter(struct mmc_test_mem *mem,
+ unsigned long sz,
+ struct scatterlist *sglist,
+ unsigned int max_segs,
+ unsigned int *sg_len)
+{
+ struct scatterlist *sg = NULL;
+ unsigned int i = mem->cnt, cnt;
+ unsigned long len;
+ void *base, *addr, *last_addr = NULL;
+
+ sg_init_table(sglist, max_segs);
+
+ *sg_len = 0;
+ while (sz && i) {
+ base = page_address(mem->arr[--i].page);
+ cnt = 1 << mem->arr[i].order;
+ while (sz && cnt) {
+ addr = base + PAGE_SIZE * --cnt;
+ if (last_addr && last_addr + PAGE_SIZE == addr)
+ continue;
+ last_addr = addr;
+ len = PAGE_SIZE;
+ if (sz < len)
+ len = sz;
+ if (sg)
+ sg = sg_next(sg);
+ else
+ sg = sglist;
+ if (!sg)
+ return -EINVAL;
+ sg_set_page(sg, virt_to_page(addr), len, 0);
+ sz -= len;
+ *sg_len += 1;
+ }
+ }
+
+ if (sg)
+ sg_mark_end(sg);
+
+ return 0;
+}
+
+/*
+ * Calculate transfer rate in bytes per second.
+ */
+static unsigned int mmc_test_rate(uint64_t bytes, struct timespec *ts)
+{
+ uint64_t ns;
+
+ ns = ts->tv_sec;
+ ns *= 1000000000;
+ ns += ts->tv_nsec;
+
+ bytes *= 1000000000;
+
+ while (ns > UINT_MAX) {
+ bytes >>= 1;
+ ns >>= 1;
+ }
+
+ if (!ns)
+ return 0;
+
+ do_div(bytes, (uint32_t)ns);
+
+ return bytes;
+}
+
+/*
+ * Print the transfer rate.
+ */
+static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes,
+ struct timespec *ts1, struct timespec *ts2)
+{
+ unsigned int rate, sectors = bytes >> 9;
+ struct timespec ts;
+
+ ts = timespec_sub(*ts2, *ts1);
+
+ rate = mmc_test_rate(bytes, &ts);
+
+ printk(KERN_INFO "%s: Transfer of %u sectors (%u%s KiB) took %lu.%09lu "
+ "seconds (%u kB/s, %u KiB/s)\n",
+ mmc_hostname(test->card->host), sectors, sectors >> 1,
+ (sectors == 1 ? ".5" : ""), (unsigned long)ts.tv_sec,
+ (unsigned long)ts.tv_nsec, rate / 1000, rate / 1024);
+}
+
+/*
+ * Print the average transfer rate.
+ */
+static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes,
+ unsigned int count, struct timespec *ts1,
+ struct timespec *ts2)
+{
+ unsigned int rate, sectors = bytes >> 9;
+ uint64_t tot = bytes * count;
+ struct timespec ts;
+
+ ts = timespec_sub(*ts2, *ts1);
+
+ rate = mmc_test_rate(tot, &ts);
+
+ printk(KERN_INFO "%s: Transfer of %u x %u sectors (%u x %u%s KiB) took "
+ "%lu.%09lu seconds (%u kB/s, %u KiB/s)\n",
+ mmc_hostname(test->card->host), count, sectors, count,
+ sectors >> 1, (sectors == 1 ? ".5" : ""),
+ (unsigned long)ts.tv_sec, (unsigned long)ts.tv_nsec,
+ rate / 1000, rate / 1024);
+}
+
+/*
+ * Return the card size in sectors.
+ */
+static unsigned int mmc_test_capacity(struct mmc_card *card)
+{
+ if (!mmc_card_sd(card) && mmc_card_blockaddr(card))
+ return card->ext_csd.sectors;
+ else
+ return card->csd.capacity << (card->csd.read_blkbits - 9);
+}
+
/*******************************************************************/
/* Test preparation and cleanup */
/*******************************************************************/
@@ -893,8 +1197,419 @@ static int mmc_test_multi_read_high(struct mmc_test_card *test)
return 0;
}
+#else
+
+static int mmc_test_no_highmem(struct mmc_test_card *test)
+{
+ printk(KERN_INFO "%s: Highmem not configured - test skipped\n",
+ mmc_hostname(test->card->host));
+ return 0;
+}
+
#endif /* CONFIG_HIGHMEM */
+/*
+ * Map sz bytes so that it can be transferred.
+ */
+static int mmc_test_area_map(struct mmc_test_card *test, unsigned long sz,
+ int max_scatter)
+{
+ struct mmc_test_area *t = &test->area;
+
+ t->blocks = sz >> 9;
+
+ if (max_scatter) {
+ return mmc_test_map_sg_max_scatter(t->mem, sz, t->sg,
+ t->max_segs, &t->sg_len);
+ } else {
+ return mmc_test_map_sg(t->mem, sz, t->sg, 1, t->max_segs,
+ &t->sg_len);
+ }
+}
+
+/*
+ * Transfer bytes mapped by mmc_test_area_map().
+ */
+static int mmc_test_area_transfer(struct mmc_test_card *test,
+ unsigned int dev_addr, int write)
+{
+ struct mmc_test_area *t = &test->area;
+
+ return mmc_test_simple_transfer(test, t->sg, t->sg_len, dev_addr,
+ t->blocks, 512, write);
+}
+
+/*
+ * Map and transfer bytes.
+ */
+static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz,
+ unsigned int dev_addr, int write, int max_scatter,
+ int timed)
+{
+ struct timespec ts1, ts2;
+ int ret;
+
+ ret = mmc_test_area_map(test, sz, max_scatter);
+ if (ret)
+ return ret;
+
+ if (timed)
+ getnstimeofday(&ts1);
+
+ ret = mmc_test_area_transfer(test, dev_addr, write);
+ if (ret)
+ return ret;
+
+ if (timed)
+ getnstimeofday(&ts2);
+
+ if (timed)
+ mmc_test_print_rate(test, sz, &ts1, &ts2);
+
+ return 0;
+}
+
+/*
+ * Write the test area entirely.
+ */
+static int mmc_test_area_fill(struct mmc_test_card *test)
+{
+ return mmc_test_area_io(test, test->area.max_sz, test->area.dev_addr,
+ 1, 0, 0);
+}
+
+/*
+ * Erase the test area entirely.
+ */
+static int mmc_test_area_erase(struct mmc_test_card *test)
+{
+ struct mmc_test_area *t = &test->area;
+
+ if (!mmc_can_erase(test->card))
+ return 0;
+
+ return mmc_erase(test->card, t->dev_addr, test->area.max_sz >> 9,
+ MMC_ERASE_ARG);
+}
+
+/*
+ * Cleanup struct mmc_test_area.
+ */
+static int mmc_test_area_cleanup(struct mmc_test_card *test)
+{
+ struct mmc_test_area *t = &test->area;
+
+ kfree(t->sg);
+ mmc_test_free_mem(t->mem);
+
+ return 0;
+}
+
+/*
+ * Initialize an area for testing large transfers. The size of the area is the
+ * preferred erase size which is a good size for optimal transfer speed. Note
+ * that is typically 4MiB for modern cards. The test area is set to the middle
+ * of the card because cards may have different charateristics at the front
+ * (for FAT file system optimization). Optionally, the area is erased (if the
+ * card supports it) which may improve write performance. Optionally, the area
+ * is filled with data for subsequent read tests.
+ */
+static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
+{
+ struct mmc_test_area *t = &test->area;
+ unsigned long min_sz = 64 * 1024;
+ int ret;
+
+ ret = mmc_test_set_blksize(test, 512);
+ if (ret)
+ return ret;
+
+ if (test->card->pref_erase > TEST_AREA_MAX_SIZE >> 9)
+ t->max_sz = TEST_AREA_MAX_SIZE;
+ else
+ t->max_sz = (unsigned long)test->card->pref_erase << 9;
+ /*
+ * Try to allocate enough memory for the whole area. Less is OK
+ * because the same memory can be mapped into the scatterlist more than
+ * once.
+ */
+ t->mem = mmc_test_alloc_mem(min_sz, t->max_sz);
+ if (!t->mem)
+ return -ENOMEM;
+
+ t->max_segs = DIV_ROUND_UP(t->max_sz, PAGE_SIZE);
+ t->sg = kmalloc(sizeof(struct scatterlist) * t->max_segs, GFP_KERNEL);
+ if (!t->sg) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ t->dev_addr = mmc_test_capacity(test->card) / 2;
+ t->dev_addr -= t->dev_addr % (t->max_sz >> 9);
+
+ if (erase) {
+ ret = mmc_test_area_erase(test);
+ if (ret)
+ goto out_free;
+ }
+
+ if (fill) {
+ ret = mmc_test_area_fill(test);
+ if (ret)
+ goto out_free;
+ }
+
+ return 0;
+
+out_free:
+ mmc_test_area_cleanup(test);
+ return ret;
+}
+
+/*
+ * Prepare for large transfers. Do not erase the test area.
+ */
+static int mmc_test_area_prepare(struct mmc_test_card *test)
+{
+ return mmc_test_area_init(test, 0, 0);
+}
+
+/*
+ * Prepare for large transfers. Do erase the test area.
+ */
+static int mmc_test_area_prepare_erase(struct mmc_test_card *test)
+{
+ return mmc_test_area_init(test, 1, 0);
+}
+
+/*
+ * Prepare for large transfers. Erase and fill the test area.
+ */
+static int mmc_test_area_prepare_fill(struct mmc_test_card *test)
+{
+ return mmc_test_area_init(test, 1, 1);
+}
+
+/*
+ * Test best-case performance. Best-case performance is expected from
+ * a single large transfer.
+ *
+ * An additional option (max_scatter) allows the measurement of the same
+ * transfer but with no contiguous pages in the scatter list. This tests
+ * the efficiency of DMA to handle scattered pages.
+ */
+static int mmc_test_best_performance(struct mmc_test_card *test, int write,
+ int max_scatter)
+{
+ return mmc_test_area_io(test, test->area.max_sz, test->area.dev_addr,
+ write, max_scatter, 1);
+}
+
+/*
+ * Best-case read performance.
+ */
+static int mmc_test_best_read_performance(struct mmc_test_card *test)
+{
+ return mmc_test_best_performance(test, 0, 0);
+}
+
+/*
+ * Best-case write performance.
+ */
+static int mmc_test_best_write_performance(struct mmc_test_card *test)
+{
+ return mmc_test_best_performance(test, 1, 0);
+}
+
+/*
+ * Best-case read performance into scattered pages.
+ */
+static int mmc_test_best_read_perf_max_scatter(struct mmc_test_card *test)
+{
+ return mmc_test_best_performance(test, 0, 1);
+}
+
+/*
+ * Best-case write performance from scattered pages.
+ */
+static int mmc_test_best_write_perf_max_scatter(struct mmc_test_card *test)
+{
+ return mmc_test_best_performance(test, 1, 1);
+}
+
+/*
+ * Single read performance by transfer size.
+ */
+static int mmc_test_profile_read_perf(struct mmc_test_card *test)
+{
+ unsigned long sz;
+ unsigned int dev_addr;
+ int ret;
+
+ for (sz = 512; sz < test->area.max_sz; sz <<= 1) {
+ dev_addr = test->area.dev_addr + (sz >> 9);
+ ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 1);
+ if (ret)
+ return ret;
+ }
+ dev_addr = test->area.dev_addr;
+ return mmc_test_area_io(test, sz, dev_addr, 0, 0, 1);
+}
+
+/*
+ * Single write performance by transfer size.
+ */
+static int mmc_test_profile_write_perf(struct mmc_test_card *test)
+{
+ unsigned long sz;
+ unsigned int dev_addr;
+ int ret;
+
+ ret = mmc_test_area_erase(test);
+ if (ret)
+ return ret;
+ for (sz = 512; sz < test->area.max_sz; sz <<= 1) {
+ dev_addr = test->area.dev_addr + (sz >> 9);
+ ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 1);
+ if (ret)
+ return ret;
+ }
+ ret = mmc_test_area_erase(test);
+ if (ret)
+ return ret;
+ dev_addr = test->area.dev_addr;
+ return mmc_test_area_io(test, sz, dev_addr, 1, 0, 1);
+}
+
+/*
+ * Single trim performance by transfer size.
+ */
+static int mmc_test_profile_trim_perf(struct mmc_test_card *test)
+{
+ unsigned long sz;
+ unsigned int dev_addr;
+ struct timespec ts1, ts2;
+ int ret;
+
+ if (!mmc_can_trim(test->card))
+ return RESULT_UNSUP_CARD;
+
+ if (!mmc_can_erase(test->card))
+ return RESULT_UNSUP_HOST;
+
+ for (sz = 512; sz < test->area.max_sz; sz <<= 1) {
+ dev_addr = test->area.dev_addr + (sz >> 9);
+ getnstimeofday(&ts1);
+ ret = mmc_erase(test->card, dev_addr, sz >> 9, MMC_TRIM_ARG);
+ if (ret)
+ return ret;
+ getnstimeofday(&ts2);
+ mmc_test_print_rate(test, sz, &ts1, &ts2);
+ }
+ dev_addr = test->area.dev_addr;
+ getnstimeofday(&ts1);
+ ret = mmc_erase(test->card, dev_addr, sz >> 9, MMC_TRIM_ARG);
+ if (ret)
+ return ret;
+ getnstimeofday(&ts2);
+ mmc_test_print_rate(test, sz, &ts1, &ts2);
+ return 0;
+}
+
+/*
+ * Consecutive read performance by transfer size.
+ */
+static int mmc_test_profile_seq_read_perf(struct mmc_test_card *test)
+{
+ unsigned long sz;
+ unsigned int dev_addr, i, cnt;
+ struct timespec ts1, ts2;
+ int ret;
+
+ for (sz = 512; sz <= test->area.max_sz; sz <<= 1) {
+ cnt = test->area.max_sz / sz;
+ dev_addr = test->area.dev_addr;
+ getnstimeofday(&ts1);
+ for (i = 0; i < cnt; i++) {
+ ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 0);
+ if (ret)
+ return ret;
+ dev_addr += (sz >> 9);
+ }
+ getnstimeofday(&ts2);
+ mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
+ }
+ return 0;
+}
+
+/*
+ * Consecutive write performance by transfer size.
+ */
+static int mmc_test_profile_seq_write_perf(struct mmc_test_card *test)
+{
+ unsigned long sz;
+ unsigned int dev_addr, i, cnt;
+ struct timespec ts1, ts2;
+ int ret;
+
+ for (sz = 512; sz <= test->area.max_sz; sz <<= 1) {
+ ret = mmc_test_area_erase(test);
+ if (ret)
+ return ret;
+ cnt = test->area.max_sz / sz;
+ dev_addr = test->area.dev_addr;
+ getnstimeofday(&ts1);
+ for (i = 0; i < cnt; i++) {
+ ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 0);
+ if (ret)
+ return ret;
+ dev_addr += (sz >> 9);
+ }
+ getnstimeofday(&ts2);
+ mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
+ }
+ return 0;
+}
+
+/*
+ * Consecutive trim performance by transfer size.
+ */
+static int mmc_test_profile_seq_trim_perf(struct mmc_test_card *test)
+{
+ unsigned long sz;
+ unsigned int dev_addr, i, cnt;
+ struct timespec ts1, ts2;
+ int ret;
+
+ if (!mmc_can_trim(test->card))
+ return RESULT_UNSUP_CARD;
+
+ if (!mmc_can_erase(test->card))
+ return RESULT_UNSUP_HOST;
+
+ for (sz = 512; sz <= test->area.max_sz; sz <<= 1) {
+ ret = mmc_test_area_erase(test);
+ if (ret)
+ return ret;
+ ret = mmc_test_area_fill(test);
+ if (ret)
+ return ret;
+ cnt = test->area.max_sz / sz;
+ dev_addr = test->area.dev_addr;
+ getnstimeofday(&ts1);
+ for (i = 0; i < cnt; i++) {
+ ret = mmc_erase(test->card, dev_addr, sz >> 9,
+ MMC_TRIM_ARG);
+ if (ret)
+ return ret;
+ dev_addr += (sz >> 9);
+ }
+ getnstimeofday(&ts2);
+ mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2);
+ }
+ return 0;
+}
+
static const struct mmc_test_case mmc_test_cases[] = {
{
.name = "Basic write (no data verification)",
@@ -1040,8 +1755,100 @@ static const struct mmc_test_case mmc_test_cases[] = {
.cleanup = mmc_test_cleanup,
},
+#else
+
+ {
+ .name = "Highmem write",
+ .run = mmc_test_no_highmem,
+ },
+
+ {
+ .name = "Highmem read",
+ .run = mmc_test_no_highmem,
+ },
+
+ {
+ .name = "Multi-block highmem write",
+ .run = mmc_test_no_highmem,
+ },
+
+ {
+ .name = "Multi-block highmem read",
+ .run = mmc_test_no_highmem,
+ },
+
#endif /* CONFIG_HIGHMEM */
+ {
+ .name = "Best-case read performance",
+ .prepare = mmc_test_area_prepare_fill,
+ .run = mmc_test_best_read_performance,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Best-case write performance",
+ .prepare = mmc_test_area_prepare_erase,
+ .run = mmc_test_best_write_performance,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Best-case read performance into scattered pages",
+ .prepare = mmc_test_area_prepare_fill,
+ .run = mmc_test_best_read_perf_max_scatter,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Best-case write performance from scattered pages",
+ .prepare = mmc_test_area_prepare_erase,
+ .run = mmc_test_best_write_perf_max_scatter,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Single read performance by transfer size",
+ .prepare = mmc_test_area_prepare_fill,
+ .run = mmc_test_profile_read_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Single write performance by transfer size",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_profile_write_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Single trim performance by transfer size",
+ .prepare = mmc_test_area_prepare_fill,
+ .run = mmc_test_profile_trim_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Consecutive read performance by transfer size",
+ .prepare = mmc_test_area_prepare_fill,
+ .run = mmc_test_profile_seq_read_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Consecutive write performance by transfer size",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_profile_seq_write_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
+ {
+ .name = "Consecutive trim performance by transfer size",
+ .prepare = mmc_test_area_prepare,
+ .run = mmc_test_profile_seq_trim_perf,
+ .cleanup = mmc_test_area_cleanup,
+ },
+
};
static DEFINE_MUTEX(mmc_test_lock);
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index c77eb49eda0e..e876678176be 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -30,9 +30,9 @@
static int mmc_prep_request(struct request_queue *q, struct request *req)
{
/*
- * We only like normal block requests.
+ * We only like normal block requests and discards.
*/
- if (req->cmd_type != REQ_TYPE_FS) {
+ if (req->cmd_type != REQ_TYPE_FS && !(req->cmd_flags & REQ_DISCARD)) {
blk_dump_rq_flags(req, "MMC bad request");
return BLKPREP_KILL;
}
@@ -130,6 +130,21 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
blk_queue_prep_rq(mq->queue, mmc_prep_request);
blk_queue_ordered(mq->queue, QUEUE_ORDERED_DRAIN);
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
+ if (mmc_can_erase(card)) {
+ queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mq->queue);
+ mq->queue->limits.max_discard_sectors = UINT_MAX;
+ if (card->erased_byte == 0)
+ mq->queue->limits.discard_zeroes_data = 1;
+ if (!mmc_can_trim(card) && is_power_of_2(card->erase_size)) {
+ mq->queue->limits.discard_granularity =
+ card->erase_size << 9;
+ mq->queue->limits.discard_alignment =
+ card->erase_size << 9;
+ }
+ if (mmc_can_secure_erase_trim(card))
+ queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD,
+ mq->queue);
+ }
#ifdef CONFIG_MMC_BLOCK_BOUNCE
if (host->max_hw_segs == 1) {
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 49d9dcaeca49..7cd9749dc21d 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -37,6 +37,8 @@ static ssize_t mmc_type_show(struct device *dev,
return sprintf(buf, "SD\n");
case MMC_TYPE_SDIO:
return sprintf(buf, "SDIO\n");
+ case MMC_TYPE_SD_COMBO:
+ return sprintf(buf, "SDcombo\n");
default:
return -EFAULT;
}
@@ -74,6 +76,9 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
case MMC_TYPE_SDIO:
type = "SDIO";
break;
+ case MMC_TYPE_SD_COMBO:
+ type = "SDcombo";
+ break;
default:
type = NULL;
}
@@ -239,6 +244,10 @@ int mmc_add_card(struct mmc_card *card)
case MMC_TYPE_SDIO:
type = "SDIO";
break;
+ case MMC_TYPE_SD_COMBO:
+ type = "SD-combo";
+ if (mmc_card_blockaddr(card))
+ type = "SDHC-combo";
default:
type = "?";
break;
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 569e94da844c..5db49b124ffa 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1050,6 +1050,352 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay)
EXPORT_SYMBOL(mmc_detect_change);
+void mmc_init_erase(struct mmc_card *card)
+{
+ unsigned int sz;
+
+ if (is_power_of_2(card->erase_size))
+ card->erase_shift = ffs(card->erase_size) - 1;
+ else
+ card->erase_shift = 0;
+
+ /*
+ * It is possible to erase an arbitrarily large area of an SD or MMC
+ * card. That is not desirable because it can take a long time
+ * (minutes) potentially delaying more important I/O, and also the
+ * timeout calculations become increasingly hugely over-estimated.
+ * Consequently, 'pref_erase' is defined as a guide to limit erases
+ * to that size and alignment.
+ *
+ * For SD cards that define Allocation Unit size, limit erases to one
+ * Allocation Unit at a time. For MMC cards that define High Capacity
+ * Erase Size, whether it is switched on or not, limit to that size.
+ * Otherwise just have a stab at a good value. For modern cards it
+ * will end up being 4MiB. Note that if the value is too small, it
+ * can end up taking longer to erase.
+ */
+ if (mmc_card_sd(card) && card->ssr.au) {
+ card->pref_erase = card->ssr.au;
+ card->erase_shift = ffs(card->ssr.au) - 1;
+ } else if (card->ext_csd.hc_erase_size) {
+ card->pref_erase = card->ext_csd.hc_erase_size;
+ } else {
+ sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >> 11;
+ if (sz < 128)
+ card->pref_erase = 512 * 1024 / 512;
+ else if (sz < 512)
+ card->pref_erase = 1024 * 1024 / 512;
+ else if (sz < 1024)
+ card->pref_erase = 2 * 1024 * 1024 / 512;
+ else
+ card->pref_erase = 4 * 1024 * 1024 / 512;
+ if (card->pref_erase < card->erase_size)
+ card->pref_erase = card->erase_size;
+ else {
+ sz = card->pref_erase % card->erase_size;
+ if (sz)
+ card->pref_erase += card->erase_size - sz;
+ }
+ }
+}
+
+static void mmc_set_mmc_erase_timeout(struct mmc_card *card,
+ struct mmc_command *cmd,
+ unsigned int arg, unsigned int qty)
+{
+ unsigned int erase_timeout;
+
+ if (card->ext_csd.erase_group_def & 1) {
+ /* High Capacity Erase Group Size uses HC timeouts */
+ if (arg == MMC_TRIM_ARG)
+ erase_timeout = card->ext_csd.trim_timeout;
+ else
+ erase_timeout = card->ext_csd.hc_erase_timeout;
+ } else {
+ /* CSD Erase Group Size uses write timeout */
+ unsigned int mult = (10 << card->csd.r2w_factor);
+ unsigned int timeout_clks = card->csd.tacc_clks * mult;
+ unsigned int timeout_us;
+
+ /* Avoid overflow: e.g. tacc_ns=80000000 mult=1280 */
+ if (card->csd.tacc_ns < 1000000)
+ timeout_us = (card->csd.tacc_ns * mult) / 1000;
+ else
+ timeout_us = (card->csd.tacc_ns / 1000) * mult;
+
+ /*
+ * ios.clock is only a target. The real clock rate might be
+ * less but not that much less, so fudge it by multiplying by 2.
+ */
+ timeout_clks <<= 1;
+ timeout_us += (timeout_clks * 1000) /
+ (card->host->ios.clock / 1000);
+
+ erase_timeout = timeout_us / 1000;
+
+ /*
+ * Theoretically, the calculation could underflow so round up
+ * to 1ms in that case.
+ */
+ if (!erase_timeout)
+ erase_timeout = 1;
+ }
+
+ /* Multiplier for secure operations */
+ if (arg & MMC_SECURE_ARGS) {
+ if (arg == MMC_SECURE_ERASE_ARG)
+ erase_timeout *= card->ext_csd.sec_erase_mult;
+ else
+ erase_timeout *= card->ext_csd.sec_trim_mult;
+ }
+
+ erase_timeout *= qty;
+
+ /*
+ * Ensure at least a 1 second timeout for SPI as per
+ * 'mmc_set_data_timeout()'
+ */
+ if (mmc_host_is_spi(card->host) && erase_timeout < 1000)
+ erase_timeout = 1000;
+
+ cmd->erase_timeout = erase_timeout;
+}
+
+static void mmc_set_sd_erase_timeout(struct mmc_card *card,
+ struct mmc_command *cmd, unsigned int arg,
+ unsigned int qty)
+{
+ if (card->ssr.erase_timeout) {
+ /* Erase timeout specified in SD Status Register (SSR) */
+ cmd->erase_timeout = card->ssr.erase_timeout * qty +
+ card->ssr.erase_offset;
+ } else {
+ /*
+ * Erase timeout not specified in SD Status Register (SSR) so
+ * use 250ms per write block.
+ */
+ cmd->erase_timeout = 250 * qty;
+ }
+
+ /* Must not be less than 1 second */
+ if (cmd->erase_timeout < 1000)
+ cmd->erase_timeout = 1000;
+}
+
+static void mmc_set_erase_timeout(struct mmc_card *card,
+ struct mmc_command *cmd, unsigned int arg,
+ unsigned int qty)
+{
+ if (mmc_card_sd(card))
+ mmc_set_sd_erase_timeout(card, cmd, arg, qty);
+ else
+ mmc_set_mmc_erase_timeout(card, cmd, arg, qty);
+}
+
+static int mmc_do_erase(struct mmc_card *card, unsigned int from,
+ unsigned int to, unsigned int arg)
+{
+ struct mmc_command cmd;
+ unsigned int qty = 0;
+ int err;
+
+ /*
+ * qty is used to calculate the erase timeout which depends on how many
+ * erase groups (or allocation units in SD terminology) are affected.
+ * We count erasing part of an erase group as one erase group.
+ * For SD, the allocation units are always a power of 2. For MMC, the
+ * erase group size is almost certainly also power of 2, but it does not
+ * seem to insist on that in the JEDEC standard, so we fall back to
+ * division in that case. SD may not specify an allocation unit size,
+ * in which case the timeout is based on the number of write blocks.
+ *
+ * Note that the timeout for secure trim 2 will only be correct if the
+ * number of erase groups specified is the same as the total of all
+ * preceding secure trim 1 commands. Since the power may have been
+ * lost since the secure trim 1 commands occurred, it is generally
+ * impossible to calculate the secure trim 2 timeout correctly.
+ */
+ if (card->erase_shift)
+ qty += ((to >> card->erase_shift) -
+ (from >> card->erase_shift)) + 1;
+ else if (mmc_card_sd(card))
+ qty += to - from + 1;
+ else
+ qty += ((to / card->erase_size) -
+ (from / card->erase_size)) + 1;
+
+ if (!mmc_card_blockaddr(card)) {
+ from <<= 9;
+ to <<= 9;
+ }
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ if (mmc_card_sd(card))
+ cmd.opcode = SD_ERASE_WR_BLK_START;
+ else
+ cmd.opcode = MMC_ERASE_GROUP_START;
+ cmd.arg = from;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err) {
+ printk(KERN_ERR "mmc_erase: group start error %d, "
+ "status %#x\n", err, cmd.resp[0]);
+ err = -EINVAL;
+ goto out;
+ }
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ if (mmc_card_sd(card))
+ cmd.opcode = SD_ERASE_WR_BLK_END;
+ else
+ cmd.opcode = MMC_ERASE_GROUP_END;
+ cmd.arg = to;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err) {
+ printk(KERN_ERR "mmc_erase: group end error %d, status %#x\n",
+ err, cmd.resp[0]);
+ err = -EINVAL;
+ goto out;
+ }
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = MMC_ERASE;
+ cmd.arg = arg;
+ cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+ mmc_set_erase_timeout(card, &cmd, arg, qty);
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err) {
+ printk(KERN_ERR "mmc_erase: erase error %d, status %#x\n",
+ err, cmd.resp[0]);
+ err = -EIO;
+ goto out;
+ }
+
+ if (mmc_host_is_spi(card->host))
+ goto out;
+
+ do {
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = MMC_SEND_STATUS;
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ /* Do not retry else we can't see errors */
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err || (cmd.resp[0] & 0xFDF92000)) {
+ printk(KERN_ERR "error %d requesting status %#x\n",
+ err, cmd.resp[0]);
+ err = -EIO;
+ goto out;
+ }
+ } while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
+ R1_CURRENT_STATE(cmd.resp[0]) == 7);
+out:
+ return err;
+}
+
+/**
+ * mmc_erase - erase sectors.
+ * @card: card to erase
+ * @from: first sector to erase
+ * @nr: number of sectors to erase
+ * @arg: erase command argument (SD supports only %MMC_ERASE_ARG)
+ *
+ * Caller must claim host before calling this function.
+ */
+int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
+ unsigned int arg)
+{
+ unsigned int rem, to = from + nr;
+
+ if (!(card->host->caps & MMC_CAP_ERASE) ||
+ !(card->csd.cmdclass & CCC_ERASE))
+ return -EOPNOTSUPP;
+
+ if (!card->erase_size)
+ return -EOPNOTSUPP;
+
+ if (mmc_card_sd(card) && arg != MMC_ERASE_ARG)
+ return -EOPNOTSUPP;
+
+ if ((arg & MMC_SECURE_ARGS) &&
+ !(card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN))
+ return -EOPNOTSUPP;
+
+ if ((arg & MMC_TRIM_ARGS) &&
+ !(card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN))
+ return -EOPNOTSUPP;
+
+ if (arg == MMC_SECURE_ERASE_ARG) {
+ if (from % card->erase_size || nr % card->erase_size)
+ return -EINVAL;
+ }
+
+ if (arg == MMC_ERASE_ARG) {
+ rem = from % card->erase_size;
+ if (rem) {
+ rem = card->erase_size - rem;
+ from += rem;
+ if (nr > rem)
+ nr -= rem;
+ else
+ return 0;
+ }
+ rem = nr % card->erase_size;
+ if (rem)
+ nr -= rem;
+ }
+
+ if (nr == 0)
+ return 0;
+
+ to = from + nr;
+
+ if (to <= from)
+ return -EINVAL;
+
+ /* 'from' and 'to' are inclusive */
+ to -= 1;
+
+ return mmc_do_erase(card, from, to, arg);
+}
+EXPORT_SYMBOL(mmc_erase);
+
+int mmc_can_erase(struct mmc_card *card)
+{
+ if ((card->host->caps & MMC_CAP_ERASE) &&
+ (card->csd.cmdclass & CCC_ERASE) && card->erase_size)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(mmc_can_erase);
+
+int mmc_can_trim(struct mmc_card *card)
+{
+ if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(mmc_can_trim);
+
+int mmc_can_secure_erase_trim(struct mmc_card *card)
+{
+ if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(mmc_can_secure_erase_trim);
+
+int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from,
+ unsigned int nr)
+{
+ if (!card->erase_size)
+ return 0;
+ if (from % card->erase_size || nr % card->erase_size)
+ return 0;
+ return 1;
+}
+EXPORT_SYMBOL(mmc_erase_group_aligned);
void mmc_rescan(struct work_struct *work)
{
@@ -1057,6 +1403,17 @@ void mmc_rescan(struct work_struct *work)
container_of(work, struct mmc_host, detect.work);
u32 ocr;
int err;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (host->rescan_disable) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
mmc_bus_get(host);
@@ -1099,8 +1456,15 @@ void mmc_rescan(struct work_struct *work)
*/
err = mmc_send_io_op_cond(host, 0, &ocr);
if (!err) {
- if (mmc_attach_sdio(host, ocr))
- mmc_power_off(host);
+ if (mmc_attach_sdio(host, ocr)) {
+ mmc_claim_host(host);
+ /* try SDMEM (but not MMC) even if SDIO is broken */
+ if (mmc_send_app_op_cond(host, 0, &ocr))
+ goto out_fail;
+
+ if (mmc_attach_sd(host, ocr))
+ mmc_power_off(host);
+ }
goto out;
}
@@ -1124,6 +1488,7 @@ void mmc_rescan(struct work_struct *work)
goto out;
}
+out_fail:
mmc_release_host(host);
mmc_power_off(host);
@@ -1266,19 +1631,6 @@ int mmc_suspend_host(struct mmc_host *host)
if (host->bus_ops && !host->bus_dead) {
if (host->bus_ops->suspend)
err = host->bus_ops->suspend(host);
- if (err == -ENOSYS || !host->bus_ops->resume) {
- /*
- * We simply "remove" the card in this case.
- * It will be redetected on resume.
- */
- if (host->bus_ops->remove)
- host->bus_ops->remove(host);
- mmc_claim_host(host);
- mmc_detach_bus(host);
- mmc_release_host(host);
- host->pm_flags = 0;
- err = 0;
- }
}
mmc_bus_put(host);
@@ -1310,28 +1662,61 @@ int mmc_resume_host(struct mmc_host *host)
printk(KERN_WARNING "%s: error %d during resume "
"(card was removed?)\n",
mmc_hostname(host), err);
- if (host->bus_ops->remove)
- host->bus_ops->remove(host);
- mmc_claim_host(host);
- mmc_detach_bus(host);
- mmc_release_host(host);
- /* no need to bother upper layers */
err = 0;
}
}
mmc_bus_put(host);
- /*
- * We add a slight delay here so that resume can progress
- * in parallel.
- */
- mmc_detect_change(host, 1);
-
return err;
}
-
EXPORT_SYMBOL(mmc_resume_host);
+/* Do the card removal on suspend if card is assumed removeable
+ * Do that in pm notifier while userspace isn't yet frozen, so we will be able
+ to sync the card.
+*/
+int mmc_pm_notify(struct notifier_block *notify_block,
+ unsigned long mode, void *unused)
+{
+ struct mmc_host *host = container_of(
+ notify_block, struct mmc_host, pm_notify);
+ unsigned long flags;
+
+
+ switch (mode) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->rescan_disable = 1;
+ spin_unlock_irqrestore(&host->lock, flags);
+ cancel_delayed_work_sync(&host->detect);
+
+ if (!host->bus_ops || host->bus_ops->suspend)
+ break;
+
+ mmc_claim_host(host);
+
+ if (host->bus_ops->remove)
+ host->bus_ops->remove(host);
+
+ mmc_detach_bus(host);
+ mmc_release_host(host);
+ host->pm_flags = 0;
+ break;
+
+ case PM_POST_SUSPEND:
+ case PM_POST_HIBERNATION:
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->rescan_disable = 0;
+ spin_unlock_irqrestore(&host->lock, flags);
+ mmc_detect_change(host, 0);
+
+ }
+
+ return 0;
+}
#endif
static int __init mmc_init(void)
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index a811c52a1659..9d9eef50e5d1 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -29,6 +29,8 @@ struct mmc_bus_ops {
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
void mmc_detach_bus(struct mmc_host *host);
+void mmc_init_erase(struct mmc_card *card);
+
void mmc_set_chip_select(struct mmc_host *host, int mode);
void mmc_set_clock(struct mmc_host *host, unsigned int hz);
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 47353909e345..0efe631e50ca 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -17,6 +17,7 @@
#include <linux/pagemap.h>
#include <linux/leds.h>
#include <linux/slab.h>
+#include <linux/suspend.h>
#include <linux/mmc/host.h>
@@ -85,6 +86,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable);
+ host->pm_notify.notifier_call = mmc_pm_notify;
/*
* By default, hosts do not support SGIO or large requests.
@@ -133,6 +135,7 @@ int mmc_add_host(struct mmc_host *host)
#endif
mmc_start_host(host);
+ register_pm_notifier(&host->pm_notify);
return 0;
}
@@ -149,6 +152,7 @@ EXPORT_SYMBOL(mmc_add_host);
*/
void mmc_remove_host(struct mmc_host *host)
{
+ unregister_pm_notifier(&host->pm_notify);
mmc_stop_host(host);
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 89f7a25b7ac1..6909a54c39be 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -108,23 +108,34 @@ static int mmc_decode_cid(struct mmc_card *card)
return 0;
}
+static void mmc_set_erase_size(struct mmc_card *card)
+{
+ if (card->ext_csd.erase_group_def & 1)
+ card->erase_size = card->ext_csd.hc_erase_size;
+ else
+ card->erase_size = card->csd.erase_size;
+
+ mmc_init_erase(card);
+}
+
/*
* Given a 128-bit response, decode to our card CSD structure.
*/
static int mmc_decode_csd(struct mmc_card *card)
{
struct mmc_csd *csd = &card->csd;
- unsigned int e, m, csd_struct;
+ unsigned int e, m, a, b;
u32 *resp = card->raw_csd;
/*
* We only understand CSD structure v1.1 and v1.2.
* v1.2 has extra information in bits 15, 11 and 10.
+ * We also support eMMC v4.4 & v4.41.
*/
- csd_struct = UNSTUFF_BITS(resp, 126, 2);
- if (csd_struct != 1 && csd_struct != 2) {
+ csd->structure = UNSTUFF_BITS(resp, 126, 2);
+ if (csd->structure == 0) {
printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
- mmc_hostname(card->host), csd_struct);
+ mmc_hostname(card->host), csd->structure);
return -EINVAL;
}
@@ -151,6 +162,13 @@ static int mmc_decode_csd(struct mmc_card *card)
csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
+ if (csd->write_blkbits >= 9) {
+ a = UNSTUFF_BITS(resp, 42, 5);
+ b = UNSTUFF_BITS(resp, 37, 5);
+ csd->erase_size = (a + 1) * (b + 1);
+ csd->erase_size <<= csd->write_blkbits - 9;
+ }
+
return 0;
}
@@ -207,11 +225,22 @@ static int mmc_read_ext_csd(struct mmc_card *card)
goto out;
}
+ /* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */
+ if (card->csd.structure == 3) {
+ int ext_csd_struct = ext_csd[EXT_CSD_STRUCTURE];
+ if (ext_csd_struct > 2) {
+ printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
+ "version %d\n", mmc_hostname(card->host),
+ ext_csd_struct);
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
if (card->ext_csd.rev > 5) {
- printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
- "version %d\n", mmc_hostname(card->host),
- card->ext_csd.rev);
+ printk(KERN_ERR "%s: unrecognised EXT_CSD revision %d\n",
+ mmc_hostname(card->host), card->ext_csd.rev);
err = -EINVAL;
goto out;
}
@@ -222,7 +251,9 @@ static int mmc_read_ext_csd(struct mmc_card *card)
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
- if (card->ext_csd.sectors)
+
+ /* Cards with density > 2GiB are sector addressed */
+ if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512)
mmc_card_set_blockaddr(card);
}
@@ -247,8 +278,30 @@ static int mmc_read_ext_csd(struct mmc_card *card)
if (sa_shift > 0 && sa_shift <= 0x17)
card->ext_csd.sa_timeout =
1 << ext_csd[EXT_CSD_S_A_TIMEOUT];
+ card->ext_csd.erase_group_def =
+ ext_csd[EXT_CSD_ERASE_GROUP_DEF];
+ card->ext_csd.hc_erase_timeout = 300 *
+ ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT];
+ card->ext_csd.hc_erase_size =
+ ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] << 10;
}
+ if (card->ext_csd.rev >= 4) {
+ card->ext_csd.sec_trim_mult =
+ ext_csd[EXT_CSD_SEC_TRIM_MULT];
+ card->ext_csd.sec_erase_mult =
+ ext_csd[EXT_CSD_SEC_ERASE_MULT];
+ card->ext_csd.sec_feature_support =
+ ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT];
+ card->ext_csd.trim_timeout = 300 *
+ ext_csd[EXT_CSD_TRIM_MULT];
+ }
+
+ if (ext_csd[EXT_CSD_ERASED_MEM_CONT])
+ card->erased_byte = 0xFF;
+ else
+ card->erased_byte = 0x0;
+
out:
kfree(ext_csd);
@@ -260,6 +313,8 @@ MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
card->raw_csd[2], card->raw_csd[3]);
MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year);
+MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9);
+MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9);
MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev);
MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
@@ -271,6 +326,8 @@ static struct attribute *mmc_std_attrs[] = {
&dev_attr_cid.attr,
&dev_attr_csd.attr,
&dev_attr_date.attr,
+ &dev_attr_erase_size.attr,
+ &dev_attr_preferred_erase_size.attr,
&dev_attr_fwrev.attr,
&dev_attr_hwrev.attr,
&dev_attr_manfid.attr,
@@ -407,6 +464,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
err = mmc_read_ext_csd(card);
if (err)
goto free_card;
+ /* Erase size depends on CSD and Extended CSD */
+ mmc_set_erase_size(card);
}
/*
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 5eac21df4809..0f5241085557 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -59,7 +59,7 @@ static const unsigned int tacc_mant[] = {
/*
* Given the decoded CSD structure, decode the raw CID to our CID structure.
*/
-static void mmc_decode_cid(struct mmc_card *card)
+void mmc_decode_cid(struct mmc_card *card)
{
u32 *resp = card->raw_cid;
@@ -119,6 +119,13 @@ static int mmc_decode_csd(struct mmc_card *card)
csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
+
+ if (UNSTUFF_BITS(resp, 46, 1)) {
+ csd->erase_size = 1;
+ } else if (csd->write_blkbits >= 9) {
+ csd->erase_size = UNSTUFF_BITS(resp, 39, 7) + 1;
+ csd->erase_size <<= csd->write_blkbits - 9;
+ }
break;
case 1:
/*
@@ -147,6 +154,7 @@ static int mmc_decode_csd(struct mmc_card *card)
csd->r2w_factor = 4; /* Unused */
csd->write_blkbits = 9;
csd->write_partial = 0;
+ csd->erase_size = 1;
break;
default:
printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
@@ -154,6 +162,8 @@ static int mmc_decode_csd(struct mmc_card *card)
return -EINVAL;
}
+ card->erase_size = csd->erase_size;
+
return 0;
}
@@ -179,10 +189,68 @@ static int mmc_decode_scr(struct mmc_card *card)
scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4);
scr->bus_widths = UNSTUFF_BITS(resp, 48, 4);
+ if (UNSTUFF_BITS(resp, 55, 1))
+ card->erased_byte = 0xFF;
+ else
+ card->erased_byte = 0x0;
+
return 0;
}
/*
+ * Fetch and process SD Status register.
+ */
+static int mmc_read_ssr(struct mmc_card *card)
+{
+ unsigned int au, es, et, eo;
+ int err, i;
+ u32 *ssr;
+
+ if (!(card->csd.cmdclass & CCC_APP_SPEC)) {
+ printk(KERN_WARNING "%s: card lacks mandatory SD Status "
+ "function.\n", mmc_hostname(card->host));
+ return 0;
+ }
+
+ ssr = kmalloc(64, GFP_KERNEL);
+ if (!ssr)
+ return -ENOMEM;
+
+ err = mmc_app_sd_status(card, ssr);
+ if (err) {
+ printk(KERN_WARNING "%s: problem reading SD Status "
+ "register.\n", mmc_hostname(card->host));
+ err = 0;
+ goto out;
+ }
+
+ for (i = 0; i < 16; i++)
+ ssr[i] = be32_to_cpu(ssr[i]);
+
+ /*
+ * UNSTUFF_BITS only works with four u32s so we have to offset the
+ * bitfield positions accordingly.
+ */
+ au = UNSTUFF_BITS(ssr, 428 - 384, 4);
+ if (au > 0 || au <= 9) {
+ card->ssr.au = 1 << (au + 4);
+ es = UNSTUFF_BITS(ssr, 408 - 384, 16);
+ et = UNSTUFF_BITS(ssr, 402 - 384, 6);
+ eo = UNSTUFF_BITS(ssr, 400 - 384, 2);
+ if (es && et) {
+ card->ssr.erase_timeout = (et * 1000) / es;
+ card->ssr.erase_offset = eo * 1000;
+ }
+ } else {
+ printk(KERN_WARNING "%s: SD Status: Invalid Allocation Unit "
+ "size.\n", mmc_hostname(card->host));
+ }
+out:
+ kfree(ssr);
+ return err;
+}
+
+/*
* Fetches and decodes switch information
*/
static int mmc_read_switch(struct mmc_card *card)
@@ -238,7 +306,7 @@ out:
/*
* Test if the card supports high-speed mode and, if so, switch to it.
*/
-static int mmc_switch_hs(struct mmc_card *card)
+int mmc_sd_switch_hs(struct mmc_card *card)
{
int err;
u8 *status;
@@ -272,9 +340,9 @@ static int mmc_switch_hs(struct mmc_card *card)
printk(KERN_WARNING "%s: Problem switching card "
"into high-speed mode!\n",
mmc_hostname(card->host));
+ err = 0;
} else {
- mmc_card_set_highspeed(card);
- mmc_set_timing(card->host, MMC_TIMING_SD_HS);
+ err = 1;
}
out:
@@ -289,6 +357,8 @@ MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
card->raw_csd[2], card->raw_csd[3]);
MMC_DEV_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]);
MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year);
+MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9);
+MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9);
MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev);
MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
@@ -302,6 +372,8 @@ static struct attribute *sd_std_attrs[] = {
&dev_attr_csd.attr,
&dev_attr_scr.attr,
&dev_attr_date.attr,
+ &dev_attr_erase_size.attr,
+ &dev_attr_preferred_erase_size.attr,
&dev_attr_fwrev.attr,
&dev_attr_hwrev.attr,
&dev_attr_manfid.attr,
@@ -320,26 +392,16 @@ static const struct attribute_group *sd_attr_groups[] = {
NULL,
};
-static struct device_type sd_type = {
+struct device_type sd_type = {
.groups = sd_attr_groups,
};
/*
- * Handle the detection and initialisation of a card.
- *
- * In the case of a resume, "oldcard" will contain the card
- * we're trying to reinitialise.
+ * Fetch CID from card.
*/
-static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
- struct mmc_card *oldcard)
+int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
{
- struct mmc_card *card;
int err;
- u32 cid[4];
- unsigned int max_dtr;
-
- BUG_ON(!host);
- WARN_ON(!host->claimed);
/*
* Since we're changing the OCR value, we seem to
@@ -361,92 +423,67 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
err = mmc_send_app_op_cond(host, ocr, NULL);
if (err)
- goto err;
+ return err;
- /*
- * Fetch CID from card.
- */
if (mmc_host_is_spi(host))
err = mmc_send_cid(host, cid);
else
err = mmc_all_send_cid(host, cid);
- if (err)
- goto err;
- if (oldcard) {
- if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
- err = -ENOENT;
- goto err;
- }
-
- card = oldcard;
- } else {
- /*
- * Allocate card structure.
- */
- card = mmc_alloc_card(host, &sd_type);
- if (IS_ERR(card)) {
- err = PTR_ERR(card);
- goto err;
- }
+ return err;
+}
- card->type = MMC_TYPE_SD;
- memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
- }
+int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card)
+{
+ int err;
/*
- * For native busses: get card RCA and quit open drain mode.
+ * Fetch CSD from card.
*/
- if (!mmc_host_is_spi(host)) {
- err = mmc_send_relative_addr(host, &card->rca);
- if (err)
- goto free_card;
+ err = mmc_send_csd(card, card->raw_csd);
+ if (err)
+ return err;
- mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
- }
+ err = mmc_decode_csd(card);
+ if (err)
+ return err;
- if (!oldcard) {
+ return 0;
+}
+
+int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
+ bool reinit)
+{
+ int err;
+
+ if (!reinit) {
/*
- * Fetch CSD from card.
+ * Fetch SCR from card.
*/
- err = mmc_send_csd(card, card->raw_csd);
- if (err)
- goto free_card;
-
- err = mmc_decode_csd(card);
+ err = mmc_app_send_scr(card, card->raw_scr);
if (err)
- goto free_card;
-
- mmc_decode_cid(card);
- }
+ return err;
- /*
- * Select card, as all following commands rely on that.
- */
- if (!mmc_host_is_spi(host)) {
- err = mmc_select_card(card);
+ err = mmc_decode_scr(card);
if (err)
- goto free_card;
- }
+ return err;
- if (!oldcard) {
/*
- * Fetch SCR from card.
+ * Fetch and process SD Status register.
*/
- err = mmc_app_send_scr(card, card->raw_scr);
+ err = mmc_read_ssr(card);
if (err)
- goto free_card;
+ return err;
- err = mmc_decode_scr(card);
- if (err < 0)
- goto free_card;
+ /* Erase init depends on CSD and SSR */
+ mmc_init_erase(card);
/*
* Fetch switch information from card.
*/
err = mmc_read_switch(card);
if (err)
- goto free_card;
+ return err;
}
/*
@@ -458,20 +495,34 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
if (mmc_host_is_spi(host)) {
err = mmc_spi_set_crc(host, use_spi_crc);
if (err)
- goto free_card;
+ return err;
}
/*
- * Attempt to change to high-speed (if supported)
+ * Check if read-only switch is active.
*/
- err = mmc_switch_hs(card);
- if (err)
- goto free_card;
+ if (!reinit) {
+ int ro = -1;
- /*
- * Compute bus speed.
- */
- max_dtr = (unsigned int)-1;
+ if (host->ops->get_ro)
+ ro = host->ops->get_ro(host);
+
+ if (ro < 0) {
+ printk(KERN_WARNING "%s: host does not "
+ "support reading read-only "
+ "switch. assuming write-enable.\n",
+ mmc_hostname(host));
+ } else if (ro > 0) {
+ mmc_card_set_readonly(card);
+ }
+ }
+
+ return 0;
+}
+
+unsigned mmc_sd_get_max_clock(struct mmc_card *card)
+{
+ unsigned max_dtr = (unsigned int)-1;
if (mmc_card_highspeed(card)) {
if (max_dtr > card->sw_caps.hs_max_dtr)
@@ -480,7 +531,97 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
max_dtr = card->csd.max_dtr;
}
- mmc_set_clock(host, max_dtr);
+ return max_dtr;
+}
+
+void mmc_sd_go_highspeed(struct mmc_card *card)
+{
+ mmc_card_set_highspeed(card);
+ mmc_set_timing(card->host, MMC_TIMING_SD_HS);
+}
+
+/*
+ * Handle the detection and initialisation of a card.
+ *
+ * In the case of a resume, "oldcard" will contain the card
+ * we're trying to reinitialise.
+ */
+static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *oldcard)
+{
+ struct mmc_card *card;
+ int err;
+ u32 cid[4];
+
+ BUG_ON(!host);
+ WARN_ON(!host->claimed);
+
+ err = mmc_sd_get_cid(host, ocr, cid);
+ if (err)
+ return err;
+
+ if (oldcard) {
+ if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
+ return -ENOENT;
+
+ card = oldcard;
+ } else {
+ /*
+ * Allocate card structure.
+ */
+ card = mmc_alloc_card(host, &sd_type);
+ if (IS_ERR(card))
+ return PTR_ERR(card);
+
+ card->type = MMC_TYPE_SD;
+ memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+ }
+
+ /*
+ * For native busses: get card RCA and quit open drain mode.
+ */
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_send_relative_addr(host, &card->rca);
+ if (err)
+ return err;
+
+ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+ }
+
+ if (!oldcard) {
+ err = mmc_sd_get_csd(host, card);
+ if (err)
+ return err;
+
+ mmc_decode_cid(card);
+ }
+
+ /*
+ * Select card, as all following commands rely on that.
+ */
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_select_card(card);
+ if (err)
+ return err;
+ }
+
+ err = mmc_sd_setup_card(host, card, oldcard != NULL);
+ if (err)
+ goto free_card;
+
+ /*
+ * Attempt to change to high-speed (if supported)
+ */
+ err = mmc_sd_switch_hs(card);
+ if (err > 0)
+ mmc_sd_go_highspeed(card);
+ else if (err)
+ goto free_card;
+
+ /*
+ * Set bus speed.
+ */
+ mmc_set_clock(host, mmc_sd_get_max_clock(card));
/*
* Switch to wider bus (if supported).
@@ -494,30 +635,12 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
}
- /*
- * Check if read-only switch is active.
- */
- if (!oldcard) {
- if (!host->ops->get_ro || host->ops->get_ro(host) < 0) {
- printk(KERN_WARNING "%s: host does not "
- "support reading read-only "
- "switch. assuming write-enable.\n",
- mmc_hostname(host));
- } else {
- if (host->ops->get_ro(host) > 0)
- mmc_card_set_readonly(card);
- }
- }
-
- if (!oldcard)
- host->card = card;
-
+ host->card = card;
return 0;
free_card:
if (!oldcard)
mmc_remove_card(card);
-err:
return err;
}
diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h
new file mode 100644
index 000000000000..3d8800fa7600
--- /dev/null
+++ b/drivers/mmc/core/sd.h
@@ -0,0 +1,17 @@
+#ifndef _MMC_CORE_SD_H
+#define _MMC_CORE_SD_H
+
+#include <linux/mmc/card.h>
+
+extern struct device_type sd_type;
+
+int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid);
+int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card);
+void mmc_decode_cid(struct mmc_card *card);
+int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
+ bool reinit);
+unsigned mmc_sd_get_max_clock(struct mmc_card *card);
+int mmc_sd_switch_hs(struct mmc_card *card);
+void mmc_sd_go_highspeed(struct mmc_card *card);
+
+#endif
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index 63772e7e7608..797cdb5887fd 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -346,3 +346,51 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
return 0;
}
+int mmc_app_sd_status(struct mmc_card *card, void *ssr)
+{
+ int err;
+ struct mmc_request mrq;
+ struct mmc_command cmd;
+ struct mmc_data data;
+ struct scatterlist sg;
+
+ BUG_ON(!card);
+ BUG_ON(!card->host);
+ BUG_ON(!ssr);
+
+ /* NOTE: caller guarantees ssr is heap-allocated */
+
+ err = mmc_app_cmd(card->host, card);
+ if (err)
+ return err;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ memset(&data, 0, sizeof(struct mmc_data));
+
+ mrq.cmd = &cmd;
+ mrq.data = &data;
+
+ cmd.opcode = SD_APP_SD_STATUS;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ data.blksz = 64;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+ data.sg = &sg;
+ data.sg_len = 1;
+
+ sg_init_one(&sg, ssr, 64);
+
+ mmc_set_data_timeout(&data, card);
+
+ mmc_wait_for_req(card->host, &mrq);
+
+ if (cmd.error)
+ return cmd.error;
+ if (data.error)
+ return data.error;
+
+ return 0;
+}
diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
index 9742d8a30664..ffc2305d905f 100644
--- a/drivers/mmc/core/sd_ops.h
+++ b/drivers/mmc/core/sd_ops.h
@@ -19,6 +19,7 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca);
int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
u8 value, u8 *resp);
+int mmc_app_sd_status(struct mmc_card *card, void *ssr);
#endif
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index b9dee28ee7d0..bd2755e8d9a3 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -18,6 +18,7 @@
#include "core.h"
#include "bus.h"
+#include "sd.h"
#include "sdio_bus.h"
#include "mmc_ops.h"
#include "sd_ops.h"
@@ -62,13 +63,19 @@ static int sdio_init_func(struct mmc_card *card, unsigned int fn)
func->num = fn;
- ret = sdio_read_fbr(func);
- if (ret)
- goto fail;
+ if (!(card->quirks & MMC_QUIRK_NONSTD_SDIO)) {
+ ret = sdio_read_fbr(func);
+ if (ret)
+ goto fail;
- ret = sdio_read_func_cis(func);
- if (ret)
- goto fail;
+ ret = sdio_read_func_cis(func);
+ if (ret)
+ goto fail;
+ } else {
+ func->vendor = func->card->cis.vendor;
+ func->device = func->card->cis.device;
+ func->max_blksize = func->card->cis.blksize;
+ }
card->sdio_func[fn - 1] = func;
@@ -159,9 +166,7 @@ static int sdio_enable_wide(struct mmc_card *card)
if (ret)
return ret;
- mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
-
- return 0;
+ return 1;
}
/*
@@ -221,10 +226,34 @@ static int sdio_disable_wide(struct mmc_card *card)
return 0;
}
+
+static int sdio_enable_4bit_bus(struct mmc_card *card)
+{
+ int err;
+
+ if (card->type == MMC_TYPE_SDIO)
+ return sdio_enable_wide(card);
+
+ if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
+ (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
+ err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
+ if (err)
+ return err;
+ } else
+ return 0;
+
+ err = sdio_enable_wide(card);
+ if (err <= 0)
+ mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1);
+
+ return err;
+}
+
+
/*
* Test if the card supports high-speed mode and, if so, switch to it.
*/
-static int sdio_enable_hs(struct mmc_card *card)
+static int mmc_sdio_switch_hs(struct mmc_card *card, int enable)
{
int ret;
u8 speed;
@@ -239,16 +268,56 @@ static int sdio_enable_hs(struct mmc_card *card)
if (ret)
return ret;
- speed |= SDIO_SPEED_EHS;
+ if (enable)
+ speed |= SDIO_SPEED_EHS;
+ else
+ speed &= ~SDIO_SPEED_EHS;
ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL);
if (ret)
return ret;
- mmc_card_set_highspeed(card);
- mmc_set_timing(card->host, MMC_TIMING_SD_HS);
+ return 1;
+}
- return 0;
+/*
+ * Enable SDIO/combo card's high-speed mode. Return 0/1 if [not]supported.
+ */
+static int sdio_enable_hs(struct mmc_card *card)
+{
+ int ret;
+
+ ret = mmc_sdio_switch_hs(card, true);
+ if (ret <= 0 || card->type == MMC_TYPE_SDIO)
+ return ret;
+
+ ret = mmc_sd_switch_hs(card);
+ if (ret <= 0)
+ mmc_sdio_switch_hs(card, false);
+
+ return ret;
+}
+
+static unsigned mmc_sdio_get_max_clock(struct mmc_card *card)
+{
+ unsigned max_dtr;
+
+ if (mmc_card_highspeed(card)) {
+ /*
+ * The SDIO specification doesn't mention how
+ * the CIS transfer speed register relates to
+ * high-speed, but it seems that 50 MHz is
+ * mandatory.
+ */
+ max_dtr = 50000000;
+ } else {
+ max_dtr = card->cis.max_dtr;
+ }
+
+ if (card->type == MMC_TYPE_SD_COMBO)
+ max_dtr = min(max_dtr, mmc_sd_get_max_clock(card));
+
+ return max_dtr;
}
/*
@@ -293,7 +362,24 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
goto err;
}
- card->type = MMC_TYPE_SDIO;
+ err = mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid);
+
+ if (!err) {
+ card->type = MMC_TYPE_SD_COMBO;
+
+ if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
+ memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
+ mmc_remove_card(card);
+ return -ENOENT;
+ }
+ } else {
+ card->type = MMC_TYPE_SDIO;
+
+ if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
+ mmc_remove_card(card);
+ return -ENOENT;
+ }
+ }
/*
* Call the optional HC's init_card function to handle quirks.
@@ -313,6 +399,17 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
}
/*
+ * Read CSD, before selecting the card
+ */
+ if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
+ err = mmc_sd_get_csd(host, card);
+ if (err)
+ return err;
+
+ mmc_decode_cid(card);
+ }
+
+ /*
* Select card, as all following commands rely on that.
*/
if (!powered_resume && !mmc_host_is_spi(host)) {
@@ -321,6 +418,23 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
goto remove;
}
+ if (card->quirks & MMC_QUIRK_NONSTD_SDIO) {
+ /*
+ * This is non-standard SDIO device, meaning it doesn't
+ * have any CIA (Common I/O area) registers present.
+ * It's host's responsibility to fill cccr and cis
+ * structures in init_card().
+ */
+ mmc_set_clock(host, card->cis.max_dtr);
+
+ if (card->cccr.high_speed) {
+ mmc_card_set_highspeed(card);
+ mmc_set_timing(card->host, MMC_TIMING_SD_HS);
+ }
+
+ goto finish;
+ }
+
/*
* Read the common registers.
*/
@@ -339,43 +453,57 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
int same = (card->cis.vendor == oldcard->cis.vendor &&
card->cis.device == oldcard->cis.device);
mmc_remove_card(card);
- if (!same) {
- err = -ENOENT;
- goto err;
- }
+ if (!same)
+ return -ENOENT;
+
card = oldcard;
return 0;
}
+ if (card->type == MMC_TYPE_SD_COMBO) {
+ err = mmc_sd_setup_card(host, card, oldcard != NULL);
+ /* handle as SDIO-only card if memory init failed */
+ if (err) {
+ mmc_go_idle(host);
+ if (mmc_host_is_spi(host))
+ /* should not fail, as it worked previously */
+ mmc_spi_set_crc(host, use_spi_crc);
+ card->type = MMC_TYPE_SDIO;
+ } else
+ card->dev.type = &sd_type;
+ }
+
+ /*
+ * If needed, disconnect card detection pull-up resistor.
+ */
+ err = sdio_disable_cd(card);
+ if (err)
+ goto remove;
+
/*
* Switch to high-speed (if supported).
*/
err = sdio_enable_hs(card);
- if (err)
+ if (err > 0)
+ mmc_sd_go_highspeed(card);
+ else if (err)
goto remove;
/*
* Change to the card's maximum speed.
*/
- if (mmc_card_highspeed(card)) {
- /*
- * The SDIO specification doesn't mention how
- * the CIS transfer speed register relates to
- * high-speed, but it seems that 50 MHz is
- * mandatory.
- */
- mmc_set_clock(host, 50000000);
- } else {
- mmc_set_clock(host, card->cis.max_dtr);
- }
+ mmc_set_clock(host, mmc_sdio_get_max_clock(card));
/*
* Switch to wider bus (if supported).
*/
- err = sdio_enable_wide(card);
- if (err)
+ err = sdio_enable_4bit_bus(card);
+ if (err > 0)
+ mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
+ else if (err)
goto remove;
+finish:
if (!oldcard)
host->card = card;
return 0;
@@ -487,9 +615,14 @@ static int mmc_sdio_resume(struct mmc_host *host)
mmc_claim_host(host);
err = mmc_sdio_init_card(host, host->ocr, host->card,
(host->pm_flags & MMC_PM_KEEP_POWER));
- if (!err)
+ if (!err) {
/* We may have switched to 1-bit mode during suspend. */
- err = sdio_enable_wide(host->card);
+ err = sdio_enable_4bit_bus(host->card);
+ if (err > 0) {
+ mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
+ err = 0;
+ }
+ }
if (!err && host->sdio_irqs)
mmc_signal_sdio_irq(host);
mmc_release_host(host);
@@ -574,13 +707,6 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
card->sdio_funcs = 0;
/*
- * If needed, disconnect card detection pull-up resistor.
- */
- err = sdio_disable_cd(card);
- if (err)
- goto remove;
-
- /*
* Initialize (but don't add) all present functions.
*/
for (i = 0; i < funcs; i++, card->sdio_funcs++) {
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index d25e22cee4c4..c997474c649f 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -121,6 +121,15 @@ config MMC_SDHCI_PLTFM
If unsure, say N.
+config MMC_SDHCI_CNS3XXX
+ bool "SDHCI support on the Cavium Networks CNS3xxx SoC"
+ depends on ARCH_CNS3XXX
+ depends on MMC_SDHCI_PLTFM
+ help
+ This selects the SDHCI support for CNS3xxx System-on-Chip devices.
+
+ If unsure, say N.
+
config MMC_SDHCI_S3C
tristate "SDHCI support on Samsung S3C SoC"
depends on MMC_SDHCI && (PLAT_S3C24XX || PLAT_S3C64XX)
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index f4e53c98d944..fe0ba4e2b8b0 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -12,7 +12,6 @@ obj-$(CONFIG_MMC_IMX) += imxmmc.o
obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
-obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
@@ -38,6 +37,10 @@ obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
+obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o
+sdhci-platform-y := sdhci-pltfm.o
+sdhci-platform-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
+
obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o
sdhci-of-y := sdhci-of-core.o
sdhci-of-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
index 7b0f3ef50f96..1145ea0792e6 100644
--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -1536,6 +1536,7 @@ static int __devexit mmc_spi_remove(struct spi_device *spi)
#if defined(CONFIG_OF)
static struct of_device_id mmc_spi_of_match_table[] __devinitdata = {
{ .compatible = "mmc-spi-slot", },
+ {},
};
#endif
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 24e09454e522..3bd8ff71ec22 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -1057,22 +1057,6 @@ msmsdcc_init_dma(struct msmsdcc_host *host)
return 0;
}
-#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ
-static void
-do_resume_work(struct work_struct *work)
-{
- struct msmsdcc_host *host =
- container_of(work, struct msmsdcc_host, resume_task);
- struct mmc_host *mmc = host->mmc;
-
- if (mmc) {
- mmc_resume_host(mmc);
- if (host->stat_irq)
- enable_irq(host->stat_irq);
- }
-}
-#endif
-
static int
msmsdcc_probe(struct platform_device *pdev)
{
@@ -1145,15 +1129,6 @@ msmsdcc_probe(struct platform_device *pdev)
host->dmares = dmares;
spin_lock_init(&host->lock);
-#ifdef CONFIG_MMC_EMBEDDED_SDIO
- if (plat->embedded_sdio)
- mmc_set_embedded_sdio_data(mmc,
- &plat->embedded_sdio->cis,
- &plat->embedded_sdio->cccr,
- plat->embedded_sdio->funcs,
- plat->embedded_sdio->num_funcs);
-#endif
-
/*
* Setup DMA
*/
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index da0039c9285e..d7dedcf33b5b 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -235,10 +235,6 @@ struct msmsdcc_host {
int cmdpoll;
struct msmsdcc_stats stats;
-#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ
- struct work_struct resume_task;
-#endif
-
/* Command parameters */
unsigned int cmd_timeout;
unsigned int cmd_pio_irqmask;
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index b032828c6126..4a8776f8afdd 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -28,6 +28,7 @@
#include <linux/clk.h>
#include <linux/mmc/host.h>
#include <linux/mmc/core.h>
+#include <linux/mmc/mmc.h>
#include <linux/io.h>
#include <linux/semaphore.h>
#include <linux/gpio.h>
@@ -78,6 +79,7 @@
#define INT_EN_MASK 0x307F0033
#define BWR_ENABLE (1 << 4)
#define BRR_ENABLE (1 << 5)
+#define DTO_ENABLE (1 << 20)
#define INIT_STREAM (1 << 1)
#define DP_SELECT (1 << 21)
#define DDIR (1 << 4)
@@ -523,7 +525,8 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
}
-static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host)
+static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
+ struct mmc_command *cmd)
{
unsigned int irq_mask;
@@ -532,6 +535,10 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host)
else
irq_mask = INT_EN_MASK;
+ /* Disable timeout for erases */
+ if (cmd->opcode == MMC_ERASE)
+ irq_mask &= ~DTO_ENABLE;
+
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
@@ -782,7 +789,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
mmc_hostname(host->mmc), cmd->opcode, cmd->arg);
host->cmd = cmd;
- omap_hsmmc_enable_irq(host);
+ omap_hsmmc_enable_irq(host, cmd);
host->response_busy = 0;
if (cmd->flags & MMC_RSP_PRESENT) {
@@ -1273,8 +1280,11 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
struct mmc_data *data = host->mrq->data;
int dma_ch, req_in_progress;
- if (ch_status & OMAP2_DMA_MISALIGNED_ERR_IRQ)
- dev_dbg(mmc_dev(host->mmc), "MISALIGNED_ADRS_ERR\n");
+ if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
+ dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n",
+ ch_status);
+ return;
+ }
spin_lock(&host->irq_lock);
if (host->dma_ch < 0) {
@@ -1598,6 +1608,14 @@ static int omap_hsmmc_get_ro(struct mmc_host *mmc)
return mmc_slot(host).get_ro(host->dev, 0);
}
+static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
+{
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
+
+ if (mmc_slot(host).init_card)
+ mmc_slot(host).init_card(card);
+}
+
static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
{
u32 hctl, capa, value;
@@ -1869,6 +1887,7 @@ static const struct mmc_host_ops omap_hsmmc_ops = {
.set_ios = omap_hsmmc_set_ios,
.get_cd = omap_hsmmc_get_cd,
.get_ro = omap_hsmmc_get_ro,
+ .init_card = omap_hsmmc_init_card,
/* NYET -- enable_sdio_irq */
};
@@ -1879,6 +1898,7 @@ static const struct mmc_host_ops omap_hsmmc_ps_ops = {
.set_ios = omap_hsmmc_set_ios,
.get_cd = omap_hsmmc_get_cd,
.get_ro = omap_hsmmc_get_ro,
+ .init_card = omap_hsmmc_init_card,
/* NYET -- enable_sdio_irq */
};
@@ -2094,12 +2114,25 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
mmc->max_seg_size = mmc->max_req_size;
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
- MMC_CAP_WAIT_WHILE_BUSY;
+ MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;
- if (mmc_slot(host).wires >= 8)
+ switch (mmc_slot(host).wires) {
+ case 8:
mmc->caps |= MMC_CAP_8_BIT_DATA;
- else if (mmc_slot(host).wires >= 4)
+ /* Fall through */
+ case 4:
mmc->caps |= MMC_CAP_4_BIT_DATA;
+ break;
+ case 1:
+ /* Nothing to crib here */
+ case 0:
+ /* Assuming nothing was given by board, Core use's 1-Bit */
+ break;
+ default:
+ /* Completely unexpected.. Core goes with 1-Bit Width */
+ dev_crit(mmc_dev(host->mmc), "Invalid width %d\n used!"
+ "using 1 instead\n", mmc_slot(host).wires);
+ }
if (mmc_slot(host).nonremovable)
mmc->caps |= MMC_CAP_NONREMOVABLE;
diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c
new file mode 100644
index 000000000000..b7050b380d5f
--- /dev/null
+++ b/drivers/mmc/host/sdhci-cns3xxx.c
@@ -0,0 +1,97 @@
+/*
+ * SDHCI support for CNS3xxx SoC
+ *
+ * Copyright 2008 Cavium Networks
+ * Copyright 2010 MontaVista Software, LLC.
+ *
+ * Authors: Scott Shu
+ * Anton Vorontsov <avorontsov@mvista.com>
+ *
+ * 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/delay.h>
+#include <linux/device.h>
+#include <linux/mmc/host.h>
+#include <linux/sdhci-pltfm.h>
+#include <mach/cns3xxx.h>
+#include "sdhci.h"
+#include "sdhci-pltfm.h"
+
+static unsigned int sdhci_cns3xxx_get_max_clk(struct sdhci_host *host)
+{
+ return 150000000;
+}
+
+static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ struct device *dev = mmc_dev(host->mmc);
+ int div = 1;
+ u16 clk;
+ unsigned long timeout;
+
+ if (clock == host->clock)
+ return;
+
+ sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+
+ if (clock == 0)
+ goto out;
+
+ while (host->max_clk / div > clock) {
+ /*
+ * On CNS3xxx divider grows linearly up to 4, and then
+ * exponentially up to 256.
+ */
+ if (div < 4)
+ div += 1;
+ else if (div < 256)
+ div *= 2;
+ else
+ break;
+ }
+
+ dev_dbg(dev, "desired SD clock: %d, actual: %d\n",
+ clock, host->max_clk / div);
+
+ /* Divide by 3 is special. */
+ if (div != 3)
+ div >>= 1;
+
+ clk = div << SDHCI_DIVIDER_SHIFT;
+ clk |= SDHCI_CLOCK_INT_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ timeout = 20;
+ while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
+ & SDHCI_CLOCK_INT_STABLE)) {
+ if (timeout == 0) {
+ dev_warn(dev, "clock is unstable");
+ break;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+out:
+ host->clock = clock;
+}
+
+static struct sdhci_ops sdhci_cns3xxx_ops = {
+ .get_max_clock = sdhci_cns3xxx_get_max_clk,
+ .set_clock = sdhci_cns3xxx_set_clock,
+};
+
+struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
+ .ops = &sdhci_cns3xxx_ops,
+ .quirks = SDHCI_QUIRK_BROKEN_DMA |
+ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+ SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
+ SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
+ SDHCI_QUIRK_NONSTANDARD_CLOCK,
+};
diff --git a/drivers/mmc/host/sdhci-of-core.c b/drivers/mmc/host/sdhci-of-core.c
index a2e9820cd42f..c51b71174c1d 100644
--- a/drivers/mmc/host/sdhci-of-core.c
+++ b/drivers/mmc/host/sdhci-of-core.c
@@ -85,14 +85,14 @@ void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg)
#ifdef CONFIG_PM
-static int sdhci_of_suspend(struct of_device *ofdev, pm_message_t state)
+static int sdhci_of_suspend(struct platform_device *ofdev, pm_message_t state)
{
struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
return mmc_suspend_host(host->mmc);
}
-static int sdhci_of_resume(struct of_device *ofdev)
+static int sdhci_of_resume(struct platform_device *ofdev)
{
struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
@@ -115,7 +115,7 @@ static bool __devinit sdhci_of_wp_inverted(struct device_node *np)
return machine_is(mpc837x_rdb) || machine_is(mpc837x_mds);
}
-static int __devinit sdhci_of_probe(struct of_device *ofdev,
+static int __devinit sdhci_of_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -154,6 +154,10 @@ static int __devinit sdhci_of_probe(struct of_device *ofdev,
host->ops = &sdhci_of_data->ops;
}
+ if (of_get_property(np, "sdhci,auto-cmd12", NULL))
+ host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
+
+
if (of_get_property(np, "sdhci,1-bit-only", NULL))
host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
@@ -179,7 +183,7 @@ err_addr_map:
return ret;
}
-static int __devexit sdhci_of_remove(struct of_device *ofdev)
+static int __devexit sdhci_of_remove(struct platform_device *ofdev)
{
struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 65483fdea45b..e8aa99deae9a 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -17,6 +17,7 @@
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
+#include <linux/device.h>
#include <linux/mmc/host.h>
@@ -84,7 +85,30 @@ static int ricoh_probe(struct sdhci_pci_chip *chip)
if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG ||
chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SONY)
chip->quirks |= SDHCI_QUIRK_NO_CARD_NO_RESET;
+ return 0;
+}
+
+static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot)
+{
+ slot->host->caps =
+ ((0x21 << SDHCI_TIMEOUT_CLK_SHIFT)
+ & SDHCI_TIMEOUT_CLK_MASK) |
+
+ ((0x21 << SDHCI_CLOCK_BASE_SHIFT)
+ & SDHCI_CLOCK_BASE_MASK) |
+ SDHCI_TIMEOUT_CLK_UNIT |
+ SDHCI_CAN_VDD_330 |
+ SDHCI_CAN_DO_SDMA;
+ return 0;
+}
+
+static int ricoh_mmc_resume(struct sdhci_pci_chip *chip)
+{
+ /* Apply a delay to allow controller to settle */
+ /* Otherwise it becomes confused if card state changed
+ during suspend */
+ msleep(500);
return 0;
}
@@ -95,6 +119,15 @@ static const struct sdhci_pci_fixes sdhci_ricoh = {
SDHCI_QUIRK_CLOCK_BEFORE_RESET,
};
+static const struct sdhci_pci_fixes sdhci_ricoh_mmc = {
+ .probe_slot = ricoh_mmc_probe_slot,
+ .resume = ricoh_mmc_resume,
+ .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR |
+ SDHCI_QUIRK_CLOCK_BEFORE_RESET |
+ SDHCI_QUIRK_NO_CARD_NO_RESET |
+ SDHCI_QUIRK_MISSING_CAPS
+};
+
static const struct sdhci_pci_fixes sdhci_ene_712 = {
.quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_BROKEN_DMA,
@@ -374,6 +407,22 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
},
{
+ .vendor = PCI_VENDOR_ID_RICOH,
+ .device = 0x843,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_RICOH,
+ .device = 0xe822,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc,
+ },
+
+ {
.vendor = PCI_VENDOR_ID_ENE,
.device = PCI_DEVICE_ID_ENE_CB712_SD,
.subvendor = PCI_ANY_ID,
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
index b6ee0d719698..e045e3c61dde 100644
--- a/drivers/mmc/host/sdhci-pltfm.c
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -24,6 +24,7 @@
#include <linux/delay.h>
#include <linux/highmem.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/mmc/host.h>
@@ -32,6 +33,7 @@
#include <linux/sdhci-pltfm.h>
#include "sdhci.h"
+#include "sdhci-pltfm.h"
/*****************************************************************************\
* *
@@ -51,10 +53,14 @@ static struct sdhci_ops sdhci_pltfm_ops = {
static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
{
struct sdhci_pltfm_data *pdata = pdev->dev.platform_data;
+ const struct platform_device_id *platid = platform_get_device_id(pdev);
struct sdhci_host *host;
struct resource *iomem;
int ret;
+ if (!pdata && platid && platid->driver_data)
+ pdata = (void *)platid->driver_data;
+
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!iomem) {
ret = -ENOMEM;
@@ -150,6 +156,15 @@ static int __devexit sdhci_pltfm_remove(struct platform_device *pdev)
return 0;
}
+static const struct platform_device_id sdhci_pltfm_ids[] = {
+ { "sdhci", },
+#ifdef CONFIG_MMC_SDHCI_CNS3XXX
+ { "sdhci-cns3xxx", (kernel_ulong_t)&sdhci_cns3xxx_pdata },
+#endif
+ { },
+};
+MODULE_DEVICE_TABLE(platform, sdhci_pltfm_ids);
+
static struct platform_driver sdhci_pltfm_driver = {
.driver = {
.name = "sdhci",
@@ -157,6 +172,7 @@ static struct platform_driver sdhci_pltfm_driver = {
},
.probe = sdhci_pltfm_probe,
.remove = __devexit_p(sdhci_pltfm_remove),
+ .id_table = sdhci_pltfm_ids,
};
/*****************************************************************************\
@@ -181,4 +197,3 @@ module_exit(sdhci_drv_exit);
MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver");
MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:sdhci");
diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h
new file mode 100644
index 000000000000..900f32902f73
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pltfm.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2010 MontaVista Software, LLC.
+ *
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * 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.
+ */
+
+#ifndef _DRIVERS_MMC_SDHCI_PLTFM_H
+#define _DRIVERS_MMC_SDHCI_PLTFM_H
+
+#include <linux/sdhci-pltfm.h>
+
+extern struct sdhci_pltfm_data sdhci_cns3xxx_pdata;
+
+#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index ad30f074ee15..0a7f2614c6f0 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/gpio.h>
#include <linux/mmc/host.h>
@@ -44,6 +45,8 @@ struct sdhci_s3c {
struct resource *ioarea;
struct s3c_sdhci_platdata *pdata;
unsigned int cur_clk;
+ int ext_cd_irq;
+ int ext_cd_gpio;
struct clk *clk_io;
struct clk *clk_bus[MAX_BUS_CLK];
@@ -110,11 +113,6 @@ static unsigned int sdhci_s3c_get_max_clk(struct sdhci_host *host)
return max;
}
-static unsigned int sdhci_s3c_get_timeout_clk(struct sdhci_host *host)
-{
- return sdhci_s3c_get_max_clk(host) / 1000000;
-}
-
/**
* sdhci_s3c_consider_clock - consider one the bus clocks for current setting
* @ourhost: Our SDHCI instance.
@@ -188,7 +186,6 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
ourhost->cur_clk = best_src;
host->max_clk = clk_get_rate(clk);
- host->timeout_clk = sdhci_s3c_get_timeout_clk(host);
ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
@@ -209,12 +206,93 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
}
}
+/**
+ * sdhci_s3c_get_min_clock - callback to get minimal supported clock value
+ * @host: The SDHCI host being queried
+ *
+ * To init mmc host properly a minimal clock value is needed. For high system
+ * bus clock's values the standard formula gives values out of allowed range.
+ * The clock still can be set to lower values, if clock source other then
+ * system bus is selected.
+*/
+static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)
+{
+ struct sdhci_s3c *ourhost = to_s3c(host);
+ unsigned int delta, min = UINT_MAX;
+ int src;
+
+ for (src = 0; src < MAX_BUS_CLK; src++) {
+ delta = sdhci_s3c_consider_clock(ourhost, src, 0);
+ if (delta == UINT_MAX)
+ continue;
+ /* delta is a negative value in this case */
+ if (-delta < min)
+ min = -delta;
+ }
+ return min;
+}
+
static struct sdhci_ops sdhci_s3c_ops = {
.get_max_clock = sdhci_s3c_get_max_clk,
- .get_timeout_clock = sdhci_s3c_get_timeout_clk,
.set_clock = sdhci_s3c_set_clock,
+ .get_min_clock = sdhci_s3c_get_min_clock,
};
+static void sdhci_s3c_notify_change(struct platform_device *dev, int state)
+{
+ struct sdhci_host *host = platform_get_drvdata(dev);
+ if (host) {
+ mutex_lock(&host->lock);
+ if (state) {
+ dev_dbg(&dev->dev, "card inserted.\n");
+ host->flags &= ~SDHCI_DEVICE_DEAD;
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ } else {
+ dev_dbg(&dev->dev, "card removed.\n");
+ host->flags |= SDHCI_DEVICE_DEAD;
+ host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ }
+ sdhci_card_detect(host);
+ mutex_unlock(&host->lock);
+ }
+}
+
+static irqreturn_t sdhci_s3c_gpio_card_detect_thread(int irq, void *dev_id)
+{
+ struct sdhci_s3c *sc = dev_id;
+ int status = gpio_get_value(sc->ext_cd_gpio);
+ if (sc->pdata->ext_cd_gpio_invert)
+ status = !status;
+ sdhci_s3c_notify_change(sc->pdev, status);
+ return IRQ_HANDLED;
+}
+
+static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc)
+{
+ struct s3c_sdhci_platdata *pdata = sc->pdata;
+ struct device *dev = &sc->pdev->dev;
+
+ if (gpio_request(pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) {
+ sc->ext_cd_gpio = pdata->ext_cd_gpio;
+ sc->ext_cd_irq = gpio_to_irq(pdata->ext_cd_gpio);
+ if (sc->ext_cd_irq &&
+ request_threaded_irq(sc->ext_cd_irq, NULL,
+ sdhci_s3c_gpio_card_detect_thread,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ dev_name(dev), sc) == 0) {
+ int status = gpio_get_value(sc->ext_cd_gpio);
+ if (pdata->ext_cd_gpio_invert)
+ status = !status;
+ sdhci_s3c_notify_change(sc->pdev, status);
+ } else {
+ dev_warn(dev, "cannot request irq for card detect\n");
+ sc->ext_cd_irq = 0;
+ }
+ } else {
+ dev_err(dev, "cannot request gpio for card detect\n");
+ }
+}
+
static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
{
struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data;
@@ -252,6 +330,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
sc->host = host;
sc->pdev = pdev;
sc->pdata = pdata;
+ sc->ext_cd_gpio = -1; /* invalid gpio number */
platform_set_drvdata(pdev, host);
@@ -318,6 +397,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
/* Setup quirks for the controller */
host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
+ host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
#ifndef CONFIG_MMC_SDHCI_S3C_DMA
@@ -332,15 +412,34 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
* SDHCI block, or a missing configuration that needs to be set. */
host->quirks |= SDHCI_QUIRK_NO_BUSY_IRQ;
+ if (pdata->cd_type == S3C_SDHCI_CD_NONE ||
+ pdata->cd_type == S3C_SDHCI_CD_PERMANENT)
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+
+ if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT)
+ host->mmc->caps = MMC_CAP_NONREMOVABLE;
+
host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR |
SDHCI_QUIRK_32BIT_DMA_SIZE);
+ /* HSMMC on Samsung SoCs uses SDCLK as timeout clock */
+ host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK;
+
ret = sdhci_add_host(host);
if (ret) {
dev_err(dev, "sdhci_add_host() failed\n");
goto err_add_host;
}
+ /* The following two methods of card detection might call
+ sdhci_s3c_notify_change() immediately, so they can be called
+ only after sdhci_add_host(). Setup errors are ignored. */
+ if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init)
+ pdata->ext_cd_init(&sdhci_s3c_notify_change);
+ if (pdata->cd_type == S3C_SDHCI_CD_GPIO &&
+ gpio_is_valid(pdata->ext_cd_gpio))
+ sdhci_s3c_setup_card_detect_gpio(sc);
+
return 0;
err_add_host:
@@ -365,10 +464,20 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
static int __devexit sdhci_s3c_remove(struct platform_device *pdev)
{
+ struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data;
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_s3c *sc = sdhci_priv(host);
int ptr;
+ if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup)
+ pdata->ext_cd_cleanup(&sdhci_s3c_notify_change);
+
+ if (sc->ext_cd_irq)
+ free_irq(sc->ext_cd_irq, sc);
+
+ if (gpio_is_valid(sc->ext_cd_gpio))
+ gpio_free(sc->ext_cd_gpio);
+
sdhci_remove_host(host, 1);
for (ptr = 0; ptr < 3; ptr++) {
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index c6d1bd8d4ac4..785512133b50 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -19,6 +19,7 @@
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/scatterlist.h>
+#include <linux/regulator/consumer.h>
#include <linux/leds.h>
@@ -817,8 +818,12 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
WARN_ON(!host->data);
mode = SDHCI_TRNS_BLK_CNT_EN;
- if (data->blocks > 1)
- mode |= SDHCI_TRNS_MULTI;
+ if (data->blocks > 1) {
+ if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
+ mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
+ else
+ mode |= SDHCI_TRNS_MULTI;
+ }
if (data->flags & MMC_DATA_READ)
mode |= SDHCI_TRNS_READ;
if (host->flags & SDHCI_REQ_USE_DMA)
@@ -1108,6 +1113,12 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
#ifndef SDHCI_USE_LEDS_CLASS
sdhci_activate_led(host);
#endif
+ if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) {
+ if (mrq->stop) {
+ mrq->data->stop = NULL;
+ mrq->stop = NULL;
+ }
+ }
host->mrq = mrq;
@@ -1159,6 +1170,11 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+ if (ios->bus_width == MMC_BUS_WIDTH_8)
+ ctrl |= SDHCI_CTRL_8BITBUS;
+ else
+ ctrl &= ~SDHCI_CTRL_8BITBUS;
+
if (ios->bus_width == MMC_BUS_WIDTH_4)
ctrl |= SDHCI_CTRL_4BITBUS;
else
@@ -1603,7 +1619,10 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
free_irq(host->irq, host);
- return 0;
+ if (host->vmmc)
+ ret = regulator_disable(host->vmmc);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(sdhci_suspend_host);
@@ -1612,6 +1631,13 @@ int sdhci_resume_host(struct sdhci_host *host)
{
int ret;
+ if (host->vmmc) {
+ int ret = regulator_enable(host->vmmc);
+ if (ret)
+ return ret;
+ }
+
+
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
if (host->ops->enable_dma)
host->ops->enable_dma(host);
@@ -1687,7 +1713,8 @@ int sdhci_add_host(struct sdhci_host *host)
host->version);
}
- caps = sdhci_readl(host, SDHCI_CAPABILITIES);
+ caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
+ sdhci_readl(host, SDHCI_CAPABILITIES);
if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
host->flags |= SDHCI_USE_SDMA;
@@ -1785,13 +1812,12 @@ int sdhci_add_host(struct sdhci_host *host)
* Set host parameters.
*/
mmc->ops = &sdhci_ops;
- if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK &&
- host->ops->set_clock && host->ops->get_min_clock)
+ if (host->ops->get_min_clock)
mmc->f_min = host->ops->get_min_clock(host);
else
mmc->f_min = host->max_clk / 256;
mmc->f_max = host->max_clk;
- mmc->caps = MMC_CAP_SDIO_IRQ;
+ mmc->caps |= MMC_CAP_SDIO_IRQ;
if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
mmc->caps |= MMC_CAP_4_BIT_DATA;
@@ -1884,6 +1910,14 @@ int sdhci_add_host(struct sdhci_host *host)
if (ret)
goto untasklet;
+ host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
+ if (IS_ERR(host->vmmc)) {
+ printk(KERN_INFO "%s: no vmmc regulator found\n", mmc_hostname(mmc));
+ host->vmmc = NULL;
+ } else {
+ regulator_enable(host->vmmc);
+ }
+
sdhci_init(host, 0);
#ifdef CONFIG_MMC_DEBUG
@@ -1968,6 +2002,11 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
tasklet_kill(&host->card_tasklet);
tasklet_kill(&host->finish_tasklet);
+ if (host->vmmc) {
+ regulator_disable(host->vmmc);
+ regulator_put(host->vmmc);
+ }
+
kfree(host->adma_desc);
kfree(host->align_buffer);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index c8468134adc9..036cfae76368 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -72,6 +72,7 @@
#define SDHCI_CTRL_ADMA1 0x08
#define SDHCI_CTRL_ADMA32 0x10
#define SDHCI_CTRL_ADMA64 0x18
+#define SDHCI_CTRL_8BITBUS 0x20
#define SDHCI_POWER_CONTROL 0x29
#define SDHCI_POWER_ON 0x01
@@ -240,12 +241,18 @@ struct sdhci_host {
#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25)
/* Controller cannot support End Attribute in NOP ADMA descriptor */
#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26)
+/* Controller is missing device caps. Use caps provided by host */
+#define SDHCI_QUIRK_MISSING_CAPS (1<<27)
+/* Controller uses Auto CMD12 command to stop the transfer */
+#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28)
int irq; /* Device IRQ */
void __iomem * ioaddr; /* Mapped address */
const struct sdhci_ops *ops; /* Low level hw interface */
+ struct regulator *vmmc; /* Power regulator */
+
/* Internal data */
struct mmc_host *mmc; /* MMC structure */
u64 dma_mask; /* custom DMA mask */
@@ -292,6 +299,8 @@ struct sdhci_host {
struct timer_list timer; /* Timer for timeouts */
+ unsigned int caps; /* Alternative capabilities */
+
unsigned long private[0] ____cacheline_aligned;
};
@@ -407,6 +416,7 @@ static inline void *sdhci_priv(struct sdhci_host *host)
return (void *)host->private;
}
+extern void sdhci_card_detect(struct sdhci_host *host);
extern int sdhci_add_host(struct sdhci_host *host);
extern void sdhci_remove_host(struct sdhci_host *host, int dead);
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index 6ac5f9f28ac3..00af55d7afba 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -43,7 +43,7 @@ struct of_flash {
#ifdef CONFIG_MTD_PARTITIONS
#define OF_FLASH_PARTS(info) ((info)->parts)
-static int parse_obsolete_partitions(struct of_device *dev,
+static int parse_obsolete_partitions(struct platform_device *dev,
struct of_flash *info,
struct device_node *dp)
{
@@ -93,7 +93,7 @@ static int parse_obsolete_partitions(struct of_device *dev,
#define parse_partitions(info, dev) (0)
#endif /* MTD_PARTITIONS */
-static int of_flash_remove(struct of_device *dev)
+static int of_flash_remove(struct platform_device *dev)
{
struct of_flash *info;
int i;
@@ -140,7 +140,7 @@ static int of_flash_remove(struct of_device *dev)
/* Helper function to handle probing of the obsolete "direct-mapped"
* compatible binding, which has an extra "probe-type" property
* describing the type of flash probe necessary. */
-static struct mtd_info * __devinit obsolete_probe(struct of_device *dev,
+static struct mtd_info * __devinit obsolete_probe(struct platform_device *dev,
struct map_info *map)
{
struct device_node *dp = dev->dev.of_node;
@@ -215,7 +215,7 @@ static void __devinit of_free_probes(const char **probes)
}
#endif
-static int __devinit of_flash_probe(struct of_device *dev,
+static int __devinit of_flash_probe(struct platform_device *dev,
const struct of_device_id *match)
{
#ifdef CONFIG_MTD_PARTITIONS
diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c
index 8984236a8d0a..3582ba1f9b09 100644
--- a/drivers/mtd/maps/sun_uflash.c
+++ b/drivers/mtd/maps/sun_uflash.c
@@ -48,7 +48,7 @@ struct map_info uflash_map_templ = {
.bankwidth = UFLASH_BUSWIDTH,
};
-int uflash_devinit(struct of_device *op, struct device_node *dp)
+int uflash_devinit(struct platform_device *op, struct device_node *dp)
{
struct uflash_dev *up;
@@ -108,7 +108,7 @@ int uflash_devinit(struct of_device *op, struct device_node *dp)
return 0;
}
-static int __devinit uflash_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit uflash_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -121,7 +121,7 @@ static int __devinit uflash_probe(struct of_device *op, const struct of_device_i
return uflash_devinit(op, dp);
}
-static int __devexit uflash_remove(struct of_device *op)
+static int __devexit uflash_remove(struct platform_device *op)
{
struct uflash_dev *up = dev_get_drvdata(&op->dev);
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
index 5084cc517944..80de0bff6c3a 100644
--- a/drivers/mtd/nand/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/fsl_elbc_nand.c
@@ -958,7 +958,7 @@ static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl)
return 0;
}
-static int fsl_elbc_ctrl_remove(struct of_device *ofdev)
+static int fsl_elbc_ctrl_remove(struct platform_device *ofdev)
{
struct fsl_elbc_ctrl *ctrl = dev_get_drvdata(&ofdev->dev);
int i;
@@ -1013,7 +1013,7 @@ static irqreturn_t fsl_elbc_ctrl_irq(int irqno, void *data)
* in the chip probe function.
*/
-static int __devinit fsl_elbc_ctrl_probe(struct of_device *ofdev,
+static int __devinit fsl_elbc_ctrl_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *child;
diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c
index 1312eda57ba6..4eff8b25e5af 100644
--- a/drivers/mtd/nand/fsl_upm.c
+++ b/drivers/mtd/nand/fsl_upm.c
@@ -217,7 +217,7 @@ err:
return ret;
}
-static int __devinit fun_probe(struct of_device *ofdev,
+static int __devinit fun_probe(struct platform_device *ofdev,
const struct of_device_id *ofid)
{
struct fsl_upm_nand *fun;
@@ -335,7 +335,7 @@ err1:
return ret;
}
-static int __devexit fun_remove(struct of_device *ofdev)
+static int __devexit fun_remove(struct platform_device *ofdev)
{
struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev);
int i;
diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c
index 0a130dcaa129..df0c1da4ff49 100644
--- a/drivers/mtd/nand/mpc5121_nfc.c
+++ b/drivers/mtd/nand/mpc5121_nfc.c
@@ -647,7 +647,7 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd)
iounmap(prv->csreg);
}
-static int __devinit mpc5121_nfc_probe(struct of_device *op,
+static int __devinit mpc5121_nfc_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *rootnode, *dn = op->dev.of_node;
@@ -869,7 +869,7 @@ error:
return retval;
}
-static int __devexit mpc5121_nfc_remove(struct of_device *op)
+static int __devexit mpc5121_nfc_remove(struct platform_device *op)
{
struct device *dev = &op->dev;
struct mtd_info *mtd = dev_get_drvdata(dev);
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 16a1714df008..a3c7473dd409 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -889,17 +889,17 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
}
/**
- * __nand_unlock - [REPLACABLE] unlocks specified locked blockes
+ * __nand_unlock - [REPLACEABLE] unlocks specified locked blocks
*
- * @param mtd - mtd info
- * @param ofs - offset to start unlock from
- * @param len - length to unlock
- * @invert - when = 0, unlock the range of blocks within the lower and
+ * @mtd: mtd info
+ * @ofs: offset to start unlock from
+ * @len: length to unlock
+ * @invert: when = 0, unlock the range of blocks within the lower and
* upper boundary address
- * whne = 1, unlock the range of blocks outside the boundaries
+ * when = 1, unlock the range of blocks outside the boundaries
* of the lower and upper boundary address
*
- * @return - unlock status
+ * return - unlock status
*/
static int __nand_unlock(struct mtd_info *mtd, loff_t ofs,
uint64_t len, int invert)
@@ -931,13 +931,13 @@ static int __nand_unlock(struct mtd_info *mtd, loff_t ofs,
}
/**
- * nand_unlock - [REPLACABLE] unlocks specified locked blockes
+ * nand_unlock - [REPLACEABLE] unlocks specified locked blocks
*
- * @param mtd - mtd info
- * @param ofs - offset to start unlock from
- * @param len - length to unlock
+ * @mtd: mtd info
+ * @ofs: offset to start unlock from
+ * @len: length to unlock
*
- * @return - unlock status
+ * return - unlock status
*/
int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
@@ -982,16 +982,16 @@ out:
}
/**
- * nand_lock - [REPLACABLE] locks all blockes present in the device
+ * nand_lock - [REPLACEABLE] locks all blocks present in the device
*
- * @param mtd - mtd info
- * @param ofs - offset to start unlock from
- * @param len - length to unlock
+ * @mtd: mtd info
+ * @ofs: offset to start unlock from
+ * @len: length to unlock
*
- * @return - lock status
+ * return - lock status
*
- * This feature is not support in many NAND parts. 'Micron' NAND parts
- * do have this feature, but it allows only to lock all blocks not for
+ * This feature is not supported in many NAND parts. 'Micron' NAND parts
+ * do have this feature, but it allows only to lock all blocks, not for
* specified range for block.
*
* Implementing 'lock' feature by making use of 'unlock', for now.
@@ -2093,6 +2093,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
* nand_fill_oob - [Internal] Transfer client buffer to oob
* @chip: nand chip structure
* @oob: oob data buffer
+ * @len: oob data write length
* @ops: oob ops structure
*/
static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c
index 98fd2bdf8be1..510554e6c115 100644
--- a/drivers/mtd/nand/ndfc.c
+++ b/drivers/mtd/nand/ndfc.c
@@ -35,7 +35,7 @@
struct ndfc_controller {
- struct of_device *ofdev;
+ struct platform_device *ofdev;
void __iomem *ndfcbase;
struct mtd_info mtd;
struct nand_chip chip;
@@ -225,7 +225,7 @@ err:
return ret;
}
-static int __devinit ndfc_probe(struct of_device *ofdev,
+static int __devinit ndfc_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct ndfc_controller *ndfc = &ndfc_ctrl;
@@ -277,7 +277,7 @@ static int __devinit ndfc_probe(struct of_device *ofdev,
return 0;
}
-static int __devexit ndfc_remove(struct of_device *ofdev)
+static int __devexit ndfc_remove(struct platform_device *ofdev)
{
struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c
index f02af24d033a..6ddb2461d740 100644
--- a/drivers/mtd/nand/pasemi_nand.c
+++ b/drivers/mtd/nand/pasemi_nand.c
@@ -89,7 +89,7 @@ int pasemi_device_ready(struct mtd_info *mtd)
return !!(inl(lpcctl) & LBICTRL_LPCCTL_NR);
}
-static int __devinit pasemi_nand_probe(struct of_device *ofdev,
+static int __devinit pasemi_nand_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct pci_dev *pdev;
@@ -185,7 +185,7 @@ static int __devinit pasemi_nand_probe(struct of_device *ofdev,
return err;
}
-static int __devexit pasemi_nand_remove(struct of_device *ofdev)
+static int __devexit pasemi_nand_remove(struct platform_device *ofdev)
{
struct nand_chip *chip;
diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c
index cc728b12de82..a8e403eebedb 100644
--- a/drivers/mtd/nand/socrates_nand.c
+++ b/drivers/mtd/nand/socrates_nand.c
@@ -162,7 +162,7 @@ static const char *part_probes[] = { "cmdlinepart", NULL };
/*
* Probe for the NAND device.
*/
-static int __devinit socrates_nand_probe(struct of_device *ofdev,
+static int __devinit socrates_nand_probe(struct platform_device *ofdev,
const struct of_device_id *ofid)
{
struct socrates_nand_host *host;
@@ -276,7 +276,7 @@ out:
/*
* Remove a NAND device.
*/
-static int __devexit socrates_nand_remove(struct of_device *ofdev)
+static int __devexit socrates_nand_remove(struct platform_device *ofdev)
{
struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev);
struct mtd_info *mtd = &host->mtd;
diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c
index 4f1cc7164ad9..6028226a7270 100644
--- a/drivers/net/arm/ixp4xx_eth.c
+++ b/drivers/net/arm/ixp4xx_eth.c
@@ -241,7 +241,7 @@ static inline void memcpy_swab32(u32 *dest, u32 *src, int cnt)
static spinlock_t mdio_lock;
static struct eth_regs __iomem *mdio_regs; /* mdio command and status only */
-struct mii_bus *mdio_bus;
+static struct mii_bus *mdio_bus;
static int ports_open;
static struct port *npe_port_tab[MAX_NPES];
static struct dma_pool *dma_pool;
diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c
index af753936e835..b1bdc909090f 100644
--- a/drivers/net/can/mscan/mpc5xxx_can.c
+++ b/drivers/net/can/mscan/mpc5xxx_can.c
@@ -38,7 +38,7 @@
struct mpc5xxx_can_data {
unsigned int type;
- u32 (*get_clock)(struct of_device *ofdev, const char *clock_name,
+ u32 (*get_clock)(struct platform_device *ofdev, const char *clock_name,
int *mscan_clksrc);
};
@@ -48,7 +48,7 @@ static struct of_device_id __devinitdata mpc52xx_cdm_ids[] = {
{}
};
-static u32 __devinit mpc52xx_can_get_clock(struct of_device *ofdev,
+static u32 __devinit mpc52xx_can_get_clock(struct platform_device *ofdev,
const char *clock_name,
int *mscan_clksrc)
{
@@ -101,7 +101,7 @@ static u32 __devinit mpc52xx_can_get_clock(struct of_device *ofdev,
return freq;
}
#else /* !CONFIG_PPC_MPC52xx */
-static u32 __devinit mpc52xx_can_get_clock(struct of_device *ofdev,
+static u32 __devinit mpc52xx_can_get_clock(struct platform_device *ofdev,
const char *clock_name,
int *mscan_clksrc)
{
@@ -129,7 +129,7 @@ static struct of_device_id __devinitdata mpc512x_clock_ids[] = {
{}
};
-static u32 __devinit mpc512x_can_get_clock(struct of_device *ofdev,
+static u32 __devinit mpc512x_can_get_clock(struct platform_device *ofdev,
const char *clock_name,
int *mscan_clksrc)
{
@@ -239,7 +239,7 @@ exit_unmap:
return freq;
}
#else /* !CONFIG_PPC_MPC512x */
-static u32 __devinit mpc512x_can_get_clock(struct of_device *ofdev,
+static u32 __devinit mpc512x_can_get_clock(struct platform_device *ofdev,
const char *clock_name,
int *mscan_clksrc)
{
@@ -247,7 +247,7 @@ static u32 __devinit mpc512x_can_get_clock(struct of_device *ofdev,
}
#endif /* CONFIG_PPC_MPC512x */
-static int __devinit mpc5xxx_can_probe(struct of_device *ofdev,
+static int __devinit mpc5xxx_can_probe(struct platform_device *ofdev,
const struct of_device_id *id)
{
struct mpc5xxx_can_data *data = (struct mpc5xxx_can_data *)id->data;
@@ -317,7 +317,7 @@ exit_unmap_mem:
return err;
}
-static int __devexit mpc5xxx_can_remove(struct of_device *ofdev)
+static int __devexit mpc5xxx_can_remove(struct platform_device *ofdev)
{
struct net_device *dev = dev_get_drvdata(&ofdev->dev);
struct mscan_priv *priv = netdev_priv(dev);
@@ -334,7 +334,7 @@ static int __devexit mpc5xxx_can_remove(struct of_device *ofdev)
#ifdef CONFIG_PM
static struct mscan_regs saved_regs;
-static int mpc5xxx_can_suspend(struct of_device *ofdev, pm_message_t state)
+static int mpc5xxx_can_suspend(struct platform_device *ofdev, pm_message_t state)
{
struct net_device *dev = dev_get_drvdata(&ofdev->dev);
struct mscan_priv *priv = netdev_priv(dev);
@@ -345,7 +345,7 @@ static int mpc5xxx_can_suspend(struct of_device *ofdev, pm_message_t state)
return 0;
}
-static int mpc5xxx_can_resume(struct of_device *ofdev)
+static int mpc5xxx_can_resume(struct platform_device *ofdev)
{
struct net_device *dev = dev_get_drvdata(&ofdev->dev);
struct mscan_priv *priv = netdev_priv(dev);
diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c
index ac1a83d7c204..5bfccfdf3bbb 100644
--- a/drivers/net/can/sja1000/sja1000_of_platform.c
+++ b/drivers/net/can/sja1000/sja1000_of_platform.c
@@ -67,7 +67,7 @@ static void sja1000_ofp_write_reg(const struct sja1000_priv *priv,
out_8(priv->reg_base + reg, val);
}
-static int __devexit sja1000_ofp_remove(struct of_device *ofdev)
+static int __devexit sja1000_ofp_remove(struct platform_device *ofdev)
{
struct net_device *dev = dev_get_drvdata(&ofdev->dev);
struct sja1000_priv *priv = netdev_priv(dev);
@@ -87,7 +87,7 @@ static int __devexit sja1000_ofp_remove(struct of_device *ofdev)
return 0;
}
-static int __devinit sja1000_ofp_probe(struct of_device *ofdev,
+static int __devinit sja1000_ofp_probe(struct platform_device *ofdev,
const struct of_device_id *id)
{
struct device_node *np = ofdev->dev.of_node;
diff --git a/drivers/net/ehea/ehea.h b/drivers/net/ehea/ehea.h
index 0060e422f171..99a929964e3c 100644
--- a/drivers/net/ehea/ehea.h
+++ b/drivers/net/ehea/ehea.h
@@ -413,7 +413,7 @@ struct ehea_port_res {
struct ehea_adapter {
u64 handle;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
struct ehea_port *port[EHEA_MAX_PORTS];
struct ehea_eq *neq; /* notification event queue */
struct tasklet_struct neq_tasklet;
@@ -465,7 +465,7 @@ struct ehea_port {
struct net_device *netdev;
struct net_device_stats stats;
struct ehea_port_res port_res[EHEA_MAX_PORT_RES];
- struct of_device ofdev; /* Open Firmware Device */
+ struct platform_device ofdev; /* Open Firmware Device */
struct ehea_mc_list *mc_list; /* Multicast MAC addresses */
struct vlan_group *vgrp;
struct ehea_eq *qp_eq;
diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c
index 3beba70b7dea..897719b49f96 100644
--- a/drivers/net/ehea/ehea_main.c
+++ b/drivers/net/ehea/ehea_main.c
@@ -107,10 +107,10 @@ struct ehea_fw_handle_array ehea_fw_handles;
struct ehea_bcmc_reg_array ehea_bcmc_regs;
-static int __devinit ehea_probe_adapter(struct of_device *dev,
+static int __devinit ehea_probe_adapter(struct platform_device *dev,
const struct of_device_id *id);
-static int __devexit ehea_remove(struct of_device *dev);
+static int __devexit ehea_remove(struct platform_device *dev);
static struct of_device_id ehea_device_table[] = {
{
@@ -3376,7 +3376,7 @@ static ssize_t ehea_remove_port(struct device *dev,
static DEVICE_ATTR(probe_port, S_IWUSR, NULL, ehea_probe_port);
static DEVICE_ATTR(remove_port, S_IWUSR, NULL, ehea_remove_port);
-int ehea_create_device_sysfs(struct of_device *dev)
+int ehea_create_device_sysfs(struct platform_device *dev)
{
int ret = device_create_file(&dev->dev, &dev_attr_probe_port);
if (ret)
@@ -3387,13 +3387,13 @@ out:
return ret;
}
-void ehea_remove_device_sysfs(struct of_device *dev)
+void ehea_remove_device_sysfs(struct platform_device *dev)
{
device_remove_file(&dev->dev, &dev_attr_probe_port);
device_remove_file(&dev->dev, &dev_attr_remove_port);
}
-static int __devinit ehea_probe_adapter(struct of_device *dev,
+static int __devinit ehea_probe_adapter(struct platform_device *dev,
const struct of_device_id *id)
{
struct ehea_adapter *adapter;
@@ -3492,7 +3492,7 @@ out:
return ret;
}
-static int __devexit ehea_remove(struct of_device *dev)
+static int __devexit ehea_remove(struct platform_device *dev)
{
struct ehea_adapter *adapter = dev_get_drvdata(&dev->dev);
int i;
diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c
index d1a5b17b2a95..e3e10b4add9c 100644
--- a/drivers/net/fec_mpc52xx.c
+++ b/drivers/net/fec_mpc52xx.c
@@ -850,7 +850,7 @@ static const struct net_device_ops mpc52xx_fec_netdev_ops = {
/* ======================================================================== */
static int __devinit
-mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match)
+mpc52xx_fec_probe(struct platform_device *op, const struct of_device_id *match)
{
int rv;
struct net_device *ndev;
@@ -995,7 +995,7 @@ err_netdev:
}
static int
-mpc52xx_fec_remove(struct of_device *op)
+mpc52xx_fec_remove(struct platform_device *op)
{
struct net_device *ndev;
struct mpc52xx_fec_priv *priv;
@@ -1025,7 +1025,7 @@ mpc52xx_fec_remove(struct of_device *op)
}
#ifdef CONFIG_PM
-static int mpc52xx_fec_of_suspend(struct of_device *op, pm_message_t state)
+static int mpc52xx_fec_of_suspend(struct platform_device *op, pm_message_t state)
{
struct net_device *dev = dev_get_drvdata(&op->dev);
@@ -1035,7 +1035,7 @@ static int mpc52xx_fec_of_suspend(struct of_device *op, pm_message_t state)
return 0;
}
-static int mpc52xx_fec_of_resume(struct of_device *op)
+static int mpc52xx_fec_of_resume(struct platform_device *op)
{
struct net_device *dev = dev_get_drvdata(&op->dev);
diff --git a/drivers/net/fec_mpc52xx_phy.c b/drivers/net/fec_mpc52xx_phy.c
index dbaf72cbb233..0b4cb6f15984 100644
--- a/drivers/net/fec_mpc52xx_phy.c
+++ b/drivers/net/fec_mpc52xx_phy.c
@@ -61,7 +61,7 @@ static int mpc52xx_fec_mdio_write(struct mii_bus *bus, int phy_id, int reg,
data | FEC_MII_WRITE_FRAME);
}
-static int mpc52xx_fec_mdio_probe(struct of_device *of,
+static int mpc52xx_fec_mdio_probe(struct platform_device *of,
const struct of_device_id *match)
{
struct device *dev = &of->dev;
@@ -122,7 +122,7 @@ static int mpc52xx_fec_mdio_probe(struct of_device *of,
return err;
}
-static int mpc52xx_fec_mdio_remove(struct of_device *of)
+static int mpc52xx_fec_mdio_remove(struct platform_device *of)
{
struct device *dev = &of->dev;
struct mii_bus *bus = dev_get_drvdata(dev);
diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c
index f08cff9020bd..d6e3111959ab 100644
--- a/drivers/net/fs_enet/fs_enet-main.c
+++ b/drivers/net/fs_enet/fs_enet-main.c
@@ -997,7 +997,7 @@ static const struct net_device_ops fs_enet_netdev_ops = {
#endif
};
-static int __devinit fs_enet_probe(struct of_device *ofdev,
+static int __devinit fs_enet_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct net_device *ndev;
@@ -1105,7 +1105,7 @@ out_free_fpi:
return ret;
}
-static int fs_enet_remove(struct of_device *ofdev)
+static int fs_enet_remove(struct platform_device *ofdev)
{
struct net_device *ndev = dev_get_drvdata(&ofdev->dev);
struct fs_enet_private *fep = netdev_priv(ndev);
diff --git a/drivers/net/fs_enet/mac-fcc.c b/drivers/net/fs_enet/mac-fcc.c
index 48e91b6242ce..7a84e45487e8 100644
--- a/drivers/net/fs_enet/mac-fcc.c
+++ b/drivers/net/fs_enet/mac-fcc.c
@@ -84,7 +84,7 @@ static inline int fcc_cr_cmd(struct fs_enet_private *fep, u32 op)
static int do_pd_setup(struct fs_enet_private *fep)
{
- struct of_device *ofdev = to_of_device(fep->dev);
+ struct platform_device *ofdev = to_platform_device(fep->dev);
struct fs_platform_info *fpi = fep->fpi;
int ret = -EINVAL;
diff --git a/drivers/net/fs_enet/mac-fec.c b/drivers/net/fs_enet/mac-fec.c
index 7ca1642276d0..61035fc5599b 100644
--- a/drivers/net/fs_enet/mac-fec.c
+++ b/drivers/net/fs_enet/mac-fec.c
@@ -96,7 +96,7 @@ static int whack_reset(struct fec __iomem *fecp)
static int do_pd_setup(struct fs_enet_private *fep)
{
- struct of_device *ofdev = to_of_device(fep->dev);
+ struct platform_device *ofdev = to_platform_device(fep->dev);
fep->interrupt = of_irq_to_resource(ofdev->dev.of_node, 0, NULL);
if (fep->interrupt == NO_IRQ)
diff --git a/drivers/net/fs_enet/mac-scc.c b/drivers/net/fs_enet/mac-scc.c
index a3c44544846d..22a02a767069 100644
--- a/drivers/net/fs_enet/mac-scc.c
+++ b/drivers/net/fs_enet/mac-scc.c
@@ -96,7 +96,7 @@ static inline int scc_cr_cmd(struct fs_enet_private *fep, u32 op)
static int do_pd_setup(struct fs_enet_private *fep)
{
- struct of_device *ofdev = to_of_device(fep->dev);
+ struct platform_device *ofdev = to_platform_device(fep->dev);
fep->interrupt = of_irq_to_resource(ofdev->dev.of_node, 0, NULL);
if (fep->interrupt == NO_IRQ)
diff --git a/drivers/net/fs_enet/mii-bitbang.c b/drivers/net/fs_enet/mii-bitbang.c
index 3607340f3da7..3cda2b515471 100644
--- a/drivers/net/fs_enet/mii-bitbang.c
+++ b/drivers/net/fs_enet/mii-bitbang.c
@@ -150,7 +150,7 @@ static int __devinit fs_mii_bitbang_init(struct mii_bus *bus,
return 0;
}
-static int __devinit fs_enet_mdio_probe(struct of_device *ofdev,
+static int __devinit fs_enet_mdio_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct mii_bus *new_bus;
@@ -200,7 +200,7 @@ out:
return ret;
}
-static int fs_enet_mdio_remove(struct of_device *ofdev)
+static int fs_enet_mdio_remove(struct platform_device *ofdev)
{
struct mii_bus *bus = dev_get_drvdata(&ofdev->dev);
struct bb_info *bitbang = bus->priv;
diff --git a/drivers/net/fs_enet/mii-fec.c b/drivers/net/fs_enet/mii-fec.c
index bddffd169b93..dbb9c48623df 100644
--- a/drivers/net/fs_enet/mii-fec.c
+++ b/drivers/net/fs_enet/mii-fec.c
@@ -101,7 +101,7 @@ static int fs_enet_fec_mii_reset(struct mii_bus *bus)
return 0;
}
-static int __devinit fs_enet_mdio_probe(struct of_device *ofdev,
+static int __devinit fs_enet_mdio_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct resource res;
@@ -192,7 +192,7 @@ out:
return ret;
}
-static int fs_enet_mdio_remove(struct of_device *ofdev)
+static int fs_enet_mdio_remove(struct platform_device *ofdev)
{
struct mii_bus *bus = dev_get_drvdata(&ofdev->dev);
struct fec_info *fec = bus->priv;
diff --git a/drivers/net/fsl_pq_mdio.c b/drivers/net/fsl_pq_mdio.c
index f53f850b6418..d4bf91aac25f 100644
--- a/drivers/net/fsl_pq_mdio.c
+++ b/drivers/net/fsl_pq_mdio.c
@@ -265,7 +265,7 @@ static int get_ucc_id_for_range(u64 start, u64 end, u32 *ucc_id)
#endif
-static int fsl_pq_mdio_probe(struct of_device *ofdev,
+static int fsl_pq_mdio_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -425,7 +425,7 @@ err_free_priv:
}
-static int fsl_pq_mdio_remove(struct of_device *ofdev)
+static int fsl_pq_mdio_remove(struct platform_device *ofdev)
{
struct device *device = &ofdev->dev;
struct mii_bus *bus = dev_get_drvdata(device);
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index a1b6301bc674..4f7c3f3ca234 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -122,9 +122,9 @@ static irqreturn_t gfar_interrupt(int irq, void *dev_id);
static void adjust_link(struct net_device *dev);
static void init_registers(struct net_device *dev);
static int init_phy(struct net_device *dev);
-static int gfar_probe(struct of_device *ofdev,
+static int gfar_probe(struct platform_device *ofdev,
const struct of_device_id *match);
-static int gfar_remove(struct of_device *ofdev);
+static int gfar_remove(struct platform_device *ofdev);
static void free_skb_resources(struct gfar_private *priv);
static void gfar_set_multi(struct net_device *dev);
static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);
@@ -605,7 +605,7 @@ static int gfar_parse_group(struct device_node *np,
return 0;
}
-static int gfar_of_init(struct of_device *ofdev, struct net_device **pdev)
+static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
{
const char *model;
const char *ctype;
@@ -959,7 +959,7 @@ static void gfar_detect_errata(struct gfar_private *priv)
/* Set up the ethernet device structure, private data,
* and anything else we need before we start */
-static int gfar_probe(struct of_device *ofdev,
+static int gfar_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
u32 tempval;
@@ -1238,7 +1238,7 @@ register_fail:
return err;
}
-static int gfar_remove(struct of_device *ofdev)
+static int gfar_remove(struct platform_device *ofdev)
{
struct gfar_private *priv = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index 710810e2adb4..68984eb88ae0 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -1054,7 +1054,7 @@ struct gfar_private {
struct device_node *node;
struct net_device *ndev;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
enum gfar_errata errata;
struct gfar_priv_grp gfargrp[MAXGROUPS];
diff --git a/drivers/net/greth.c b/drivers/net/greth.c
index 4d09eab3548e..f15c64f1cd38 100644
--- a/drivers/net/greth.c
+++ b/drivers/net/greth.c
@@ -1373,7 +1373,7 @@ error:
}
/* Initialize the GRETH MAC */
-static int __devinit greth_of_probe(struct of_device *ofdev, const struct of_device_id *match)
+static int __devinit greth_of_probe(struct platform_device *ofdev, const struct of_device_id *match)
{
struct net_device *dev;
struct greth_private *greth;
@@ -1412,7 +1412,7 @@ static int __devinit greth_of_probe(struct of_device *ofdev, const struct of_dev
}
regs = (struct greth_regs *) greth->regs;
- greth->irq = ofdev->irqs[0];
+ greth->irq = ofdev->archdata.irqs[0];
dev_set_drvdata(greth->dev, dev);
SET_NETDEV_DEV(dev, greth->dev);
@@ -1572,7 +1572,7 @@ error1:
return err;
}
-static int __devexit greth_of_remove(struct of_device *of_dev)
+static int __devexit greth_of_remove(struct platform_device *of_dev)
{
struct net_device *ndev = dev_get_drvdata(&of_dev->dev);
struct greth_private *greth = netdev_priv(ndev);
diff --git a/drivers/net/greth.h b/drivers/net/greth.h
index 973388d6abca..03ad903cd676 100644
--- a/drivers/net/greth.h
+++ b/drivers/net/greth.h
@@ -118,7 +118,7 @@ struct greth_private {
int irq;
- struct device *dev; /* Pointer to of_device->dev */
+ struct device *dev; /* Pointer to platform_device->dev */
struct net_device *netdev;
struct napi_struct napi;
spinlock_t devlock;
diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c
index eeec7bc2ce74..3506fd6ad726 100644
--- a/drivers/net/ibm_newemac/core.c
+++ b/drivers/net/ibm_newemac/core.c
@@ -2245,7 +2245,7 @@ static int emac_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
struct emac_depentry {
u32 phandle;
struct device_node *node;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
void *drvdata;
};
@@ -2719,7 +2719,7 @@ static const struct net_device_ops emac_gige_netdev_ops = {
.ndo_change_mtu = emac_change_mtu,
};
-static int __devinit emac_probe(struct of_device *ofdev,
+static int __devinit emac_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct net_device *ndev;
@@ -2941,7 +2941,7 @@ static int __devinit emac_probe(struct of_device *ofdev,
return err;
}
-static int __devexit emac_remove(struct of_device *ofdev)
+static int __devexit emac_remove(struct platform_device *ofdev)
{
struct emac_instance *dev = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/net/ibm_newemac/core.h b/drivers/net/ibm_newemac/core.h
index b1cbe6fdfc7a..9e37e3d9c51d 100644
--- a/drivers/net/ibm_newemac/core.h
+++ b/drivers/net/ibm_newemac/core.h
@@ -170,12 +170,12 @@ struct emac_instance {
struct net_device *ndev;
struct resource rsrc_regs;
struct emac_regs __iomem *emacp;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
struct device_node **blist; /* bootlist entry */
/* MAL linkage */
u32 mal_ph;
- struct of_device *mal_dev;
+ struct platform_device *mal_dev;
u32 mal_rx_chan;
u32 mal_tx_chan;
struct mal_instance *mal;
@@ -196,24 +196,24 @@ struct emac_instance {
/* Shared MDIO if any */
u32 mdio_ph;
- struct of_device *mdio_dev;
+ struct platform_device *mdio_dev;
struct emac_instance *mdio_instance;
struct mutex mdio_lock;
/* ZMII infos if any */
u32 zmii_ph;
u32 zmii_port;
- struct of_device *zmii_dev;
+ struct platform_device *zmii_dev;
/* RGMII infos if any */
u32 rgmii_ph;
u32 rgmii_port;
- struct of_device *rgmii_dev;
+ struct platform_device *rgmii_dev;
/* TAH infos if any */
u32 tah_ph;
u32 tah_port;
- struct of_device *tah_dev;
+ struct platform_device *tah_dev;
/* IRQs */
int wol_irq;
diff --git a/drivers/net/ibm_newemac/mal.c b/drivers/net/ibm_newemac/mal.c
index fcff9e0bd382..d5717e2123e1 100644
--- a/drivers/net/ibm_newemac/mal.c
+++ b/drivers/net/ibm_newemac/mal.c
@@ -517,7 +517,7 @@ void *mal_dump_regs(struct mal_instance *mal, void *buf)
return regs + 1;
}
-static int __devinit mal_probe(struct of_device *ofdev,
+static int __devinit mal_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct mal_instance *mal;
@@ -730,7 +730,7 @@ static int __devinit mal_probe(struct of_device *ofdev,
return err;
}
-static int __devexit mal_remove(struct of_device *ofdev)
+static int __devexit mal_remove(struct platform_device *ofdev)
{
struct mal_instance *mal = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/net/ibm_newemac/mal.h b/drivers/net/ibm_newemac/mal.h
index 9ededfbf0726..66084214bf45 100644
--- a/drivers/net/ibm_newemac/mal.h
+++ b/drivers/net/ibm_newemac/mal.h
@@ -210,7 +210,7 @@ struct mal_instance {
dma_addr_t bd_dma;
struct mal_descriptor *bd_virt;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
int index;
spinlock_t lock;
diff --git a/drivers/net/ibm_newemac/rgmii.c b/drivers/net/ibm_newemac/rgmii.c
index 108919bcdf13..dd61798897ac 100644
--- a/drivers/net/ibm_newemac/rgmii.c
+++ b/drivers/net/ibm_newemac/rgmii.c
@@ -93,7 +93,7 @@ static inline u32 rgmii_mode_mask(int mode, int input)
}
}
-int __devinit rgmii_attach(struct of_device *ofdev, int input, int mode)
+int __devinit rgmii_attach(struct platform_device *ofdev, int input, int mode)
{
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
struct rgmii_regs __iomem *p = dev->base;
@@ -122,7 +122,7 @@ int __devinit rgmii_attach(struct of_device *ofdev, int input, int mode)
return 0;
}
-void rgmii_set_speed(struct of_device *ofdev, int input, int speed)
+void rgmii_set_speed(struct platform_device *ofdev, int input, int speed)
{
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
struct rgmii_regs __iomem *p = dev->base;
@@ -144,7 +144,7 @@ void rgmii_set_speed(struct of_device *ofdev, int input, int speed)
mutex_unlock(&dev->lock);
}
-void rgmii_get_mdio(struct of_device *ofdev, int input)
+void rgmii_get_mdio(struct platform_device *ofdev, int input)
{
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
struct rgmii_regs __iomem *p = dev->base;
@@ -165,7 +165,7 @@ void rgmii_get_mdio(struct of_device *ofdev, int input)
DBG2(dev, " fer = 0x%08x\n", fer);
}
-void rgmii_put_mdio(struct of_device *ofdev, int input)
+void rgmii_put_mdio(struct platform_device *ofdev, int input)
{
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
struct rgmii_regs __iomem *p = dev->base;
@@ -186,7 +186,7 @@ void rgmii_put_mdio(struct of_device *ofdev, int input)
mutex_unlock(&dev->lock);
}
-void rgmii_detach(struct of_device *ofdev, int input)
+void rgmii_detach(struct platform_device *ofdev, int input)
{
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
struct rgmii_regs __iomem *p;
@@ -206,13 +206,13 @@ void rgmii_detach(struct of_device *ofdev, int input)
mutex_unlock(&dev->lock);
}
-int rgmii_get_regs_len(struct of_device *ofdev)
+int rgmii_get_regs_len(struct platform_device *ofdev)
{
return sizeof(struct emac_ethtool_regs_subhdr) +
sizeof(struct rgmii_regs);
}
-void *rgmii_dump_regs(struct of_device *ofdev, void *buf)
+void *rgmii_dump_regs(struct platform_device *ofdev, void *buf)
{
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
struct emac_ethtool_regs_subhdr *hdr = buf;
@@ -228,7 +228,7 @@ void *rgmii_dump_regs(struct of_device *ofdev, void *buf)
}
-static int __devinit rgmii_probe(struct of_device *ofdev,
+static int __devinit rgmii_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -293,7 +293,7 @@ static int __devinit rgmii_probe(struct of_device *ofdev,
return rc;
}
-static int __devexit rgmii_remove(struct of_device *ofdev)
+static int __devexit rgmii_remove(struct platform_device *ofdev)
{
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/net/ibm_newemac/rgmii.h b/drivers/net/ibm_newemac/rgmii.h
index c4a4b358a270..d69799049865 100644
--- a/drivers/net/ibm_newemac/rgmii.h
+++ b/drivers/net/ibm_newemac/rgmii.h
@@ -51,20 +51,20 @@ struct rgmii_instance {
int users;
/* OF device instance */
- struct of_device *ofdev;
+ struct platform_device *ofdev;
};
#ifdef CONFIG_IBM_NEW_EMAC_RGMII
extern int rgmii_init(void);
extern void rgmii_exit(void);
-extern int rgmii_attach(struct of_device *ofdev, int input, int mode);
-extern void rgmii_detach(struct of_device *ofdev, int input);
-extern void rgmii_get_mdio(struct of_device *ofdev, int input);
-extern void rgmii_put_mdio(struct of_device *ofdev, int input);
-extern void rgmii_set_speed(struct of_device *ofdev, int input, int speed);
-extern int rgmii_get_regs_len(struct of_device *ofdev);
-extern void *rgmii_dump_regs(struct of_device *ofdev, void *buf);
+extern int rgmii_attach(struct platform_device *ofdev, int input, int mode);
+extern void rgmii_detach(struct platform_device *ofdev, int input);
+extern void rgmii_get_mdio(struct platform_device *ofdev, int input);
+extern void rgmii_put_mdio(struct platform_device *ofdev, int input);
+extern void rgmii_set_speed(struct platform_device *ofdev, int input, int speed);
+extern int rgmii_get_regs_len(struct platform_device *ofdev);
+extern void *rgmii_dump_regs(struct platform_device *ofdev, void *buf);
#else
diff --git a/drivers/net/ibm_newemac/tah.c b/drivers/net/ibm_newemac/tah.c
index 044637144c43..299aa49490c0 100644
--- a/drivers/net/ibm_newemac/tah.c
+++ b/drivers/net/ibm_newemac/tah.c
@@ -23,7 +23,7 @@
#include "emac.h"
#include "core.h"
-int __devinit tah_attach(struct of_device *ofdev, int channel)
+int __devinit tah_attach(struct platform_device *ofdev, int channel)
{
struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
@@ -35,7 +35,7 @@ int __devinit tah_attach(struct of_device *ofdev, int channel)
return 0;
}
-void tah_detach(struct of_device *ofdev, int channel)
+void tah_detach(struct platform_device *ofdev, int channel)
{
struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
@@ -44,7 +44,7 @@ void tah_detach(struct of_device *ofdev, int channel)
mutex_unlock(&dev->lock);
}
-void tah_reset(struct of_device *ofdev)
+void tah_reset(struct platform_device *ofdev)
{
struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
struct tah_regs __iomem *p = dev->base;
@@ -66,13 +66,13 @@ void tah_reset(struct of_device *ofdev)
TAH_MR_DIG);
}
-int tah_get_regs_len(struct of_device *ofdev)
+int tah_get_regs_len(struct platform_device *ofdev)
{
return sizeof(struct emac_ethtool_regs_subhdr) +
sizeof(struct tah_regs);
}
-void *tah_dump_regs(struct of_device *ofdev, void *buf)
+void *tah_dump_regs(struct platform_device *ofdev, void *buf)
{
struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
struct emac_ethtool_regs_subhdr *hdr = buf;
@@ -87,7 +87,7 @@ void *tah_dump_regs(struct of_device *ofdev, void *buf)
return regs + 1;
}
-static int __devinit tah_probe(struct of_device *ofdev,
+static int __devinit tah_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -139,7 +139,7 @@ static int __devinit tah_probe(struct of_device *ofdev,
return rc;
}
-static int __devexit tah_remove(struct of_device *ofdev)
+static int __devexit tah_remove(struct platform_device *ofdev)
{
struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/net/ibm_newemac/tah.h b/drivers/net/ibm_newemac/tah.h
index a068b5658dad..61dbeca006d1 100644
--- a/drivers/net/ibm_newemac/tah.h
+++ b/drivers/net/ibm_newemac/tah.h
@@ -48,7 +48,7 @@ struct tah_instance {
int users;
/* OF device instance */
- struct of_device *ofdev;
+ struct platform_device *ofdev;
};
@@ -74,11 +74,11 @@ struct tah_instance {
extern int tah_init(void);
extern void tah_exit(void);
-extern int tah_attach(struct of_device *ofdev, int channel);
-extern void tah_detach(struct of_device *ofdev, int channel);
-extern void tah_reset(struct of_device *ofdev);
-extern int tah_get_regs_len(struct of_device *ofdev);
-extern void *tah_dump_regs(struct of_device *ofdev, void *buf);
+extern int tah_attach(struct platform_device *ofdev, int channel);
+extern void tah_detach(struct platform_device *ofdev, int channel);
+extern void tah_reset(struct platform_device *ofdev);
+extern int tah_get_regs_len(struct platform_device *ofdev);
+extern void *tah_dump_regs(struct platform_device *ofdev, void *buf);
#else
diff --git a/drivers/net/ibm_newemac/zmii.c b/drivers/net/ibm_newemac/zmii.c
index 046dcd069c45..34ed6ee8ca8a 100644
--- a/drivers/net/ibm_newemac/zmii.c
+++ b/drivers/net/ibm_newemac/zmii.c
@@ -82,7 +82,7 @@ static inline u32 zmii_mode_mask(int mode, int input)
}
}
-int __devinit zmii_attach(struct of_device *ofdev, int input, int *mode)
+int __devinit zmii_attach(struct platform_device *ofdev, int input, int *mode)
{
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
struct zmii_regs __iomem *p = dev->base;
@@ -148,7 +148,7 @@ int __devinit zmii_attach(struct of_device *ofdev, int input, int *mode)
return 0;
}
-void zmii_get_mdio(struct of_device *ofdev, int input)
+void zmii_get_mdio(struct platform_device *ofdev, int input)
{
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
u32 fer;
@@ -161,7 +161,7 @@ void zmii_get_mdio(struct of_device *ofdev, int input)
out_be32(&dev->base->fer, fer | ZMII_FER_MDI(input));
}
-void zmii_put_mdio(struct of_device *ofdev, int input)
+void zmii_put_mdio(struct platform_device *ofdev, int input)
{
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
@@ -170,7 +170,7 @@ void zmii_put_mdio(struct of_device *ofdev, int input)
}
-void zmii_set_speed(struct of_device *ofdev, int input, int speed)
+void zmii_set_speed(struct platform_device *ofdev, int input, int speed)
{
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
u32 ssr;
@@ -191,7 +191,7 @@ void zmii_set_speed(struct of_device *ofdev, int input, int speed)
mutex_unlock(&dev->lock);
}
-void zmii_detach(struct of_device *ofdev, int input)
+void zmii_detach(struct platform_device *ofdev, int input)
{
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
@@ -210,13 +210,13 @@ void zmii_detach(struct of_device *ofdev, int input)
mutex_unlock(&dev->lock);
}
-int zmii_get_regs_len(struct of_device *ofdev)
+int zmii_get_regs_len(struct platform_device *ofdev)
{
return sizeof(struct emac_ethtool_regs_subhdr) +
sizeof(struct zmii_regs);
}
-void *zmii_dump_regs(struct of_device *ofdev, void *buf)
+void *zmii_dump_regs(struct platform_device *ofdev, void *buf)
{
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
struct emac_ethtool_regs_subhdr *hdr = buf;
@@ -231,7 +231,7 @@ void *zmii_dump_regs(struct of_device *ofdev, void *buf)
return regs + 1;
}
-static int __devinit zmii_probe(struct of_device *ofdev,
+static int __devinit zmii_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -286,7 +286,7 @@ static int __devinit zmii_probe(struct of_device *ofdev,
return rc;
}
-static int __devexit zmii_remove(struct of_device *ofdev)
+static int __devexit zmii_remove(struct platform_device *ofdev)
{
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/net/ibm_newemac/zmii.h b/drivers/net/ibm_newemac/zmii.h
index 6c9beba0c4b6..1333fa2b2781 100644
--- a/drivers/net/ibm_newemac/zmii.h
+++ b/drivers/net/ibm_newemac/zmii.h
@@ -48,20 +48,20 @@ struct zmii_instance {
u32 fer_save;
/* OF device instance */
- struct of_device *ofdev;
+ struct platform_device *ofdev;
};
#ifdef CONFIG_IBM_NEW_EMAC_ZMII
extern int zmii_init(void);
extern void zmii_exit(void);
-extern int zmii_attach(struct of_device *ofdev, int input, int *mode);
-extern void zmii_detach(struct of_device *ofdev, int input);
-extern void zmii_get_mdio(struct of_device *ofdev, int input);
-extern void zmii_put_mdio(struct of_device *ofdev, int input);
-extern void zmii_set_speed(struct of_device *ofdev, int input, int speed);
-extern int zmii_get_regs_len(struct of_device *ocpdev);
-extern void *zmii_dump_regs(struct of_device *ofdev, void *buf);
+extern int zmii_attach(struct platform_device *ofdev, int input, int *mode);
+extern void zmii_detach(struct platform_device *ofdev, int input);
+extern void zmii_get_mdio(struct platform_device *ofdev, int input);
+extern void zmii_put_mdio(struct platform_device *ofdev, int input);
+extern void zmii_set_speed(struct platform_device *ofdev, int input, int speed);
+extern int zmii_get_regs_len(struct platform_device *ocpdev);
+extern void *zmii_dump_regs(struct platform_device *ofdev, void *buf);
#else
# define zmii_init() 0
diff --git a/drivers/net/irda/sh_irda.c b/drivers/net/irda/sh_irda.c
index edd5666f0ffb..9e3f4f54281d 100644
--- a/drivers/net/irda/sh_irda.c
+++ b/drivers/net/irda/sh_irda.c
@@ -748,7 +748,6 @@ static int __devinit sh_irda_probe(struct platform_device *pdev)
struct net_device *ndev;
struct sh_irda_self *self;
struct resource *res;
- char clk_name[8];
int irq;
int err = -ENOMEM;
@@ -775,10 +774,9 @@ static int __devinit sh_irda_probe(struct platform_device *pdev)
if (err)
goto err_mem_2;
- snprintf(clk_name, sizeof(clk_name), "irda%d", pdev->id);
- self->clk = clk_get(&pdev->dev, clk_name);
+ self->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(self->clk)) {
- dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+ dev_err(&pdev->dev, "cannot get irda clock\n");
goto err_mem_3;
}
diff --git a/drivers/net/ll_temac_main.c b/drivers/net/ll_temac_main.c
index 4eea3f70c5cf..c7b624711f5e 100644
--- a/drivers/net/ll_temac_main.c
+++ b/drivers/net/ll_temac_main.c
@@ -159,7 +159,7 @@ static void temac_dma_dcr_out(struct temac_local *lp, int reg, u32 value)
* temac_dcr_setup - If the DMA is DCR based, then setup the address and
* I/O functions
*/
-static int temac_dcr_setup(struct temac_local *lp, struct of_device *op,
+static int temac_dcr_setup(struct temac_local *lp, struct platform_device *op,
struct device_node *np)
{
unsigned int dcrs;
@@ -184,7 +184,7 @@ static int temac_dcr_setup(struct temac_local *lp, struct of_device *op,
* temac_dcr_setup - This is a stub for when DCR is not supported,
* such as with MicroBlaze
*/
-static int temac_dcr_setup(struct temac_local *lp, struct of_device *op,
+static int temac_dcr_setup(struct temac_local *lp, struct platform_device *op,
struct device_node *np)
{
return -1;
@@ -952,7 +952,7 @@ static const struct attribute_group temac_attr_group = {
};
static int __init
-temac_of_probe(struct of_device *op, const struct of_device_id *match)
+temac_of_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *np;
struct temac_local *lp;
@@ -1094,7 +1094,7 @@ temac_of_probe(struct of_device *op, const struct of_device_id *match)
return rc;
}
-static int __devexit temac_of_remove(struct of_device *op)
+static int __devexit temac_of_remove(struct platform_device *op)
{
struct net_device *ndev = dev_get_drvdata(&op->dev);
struct temac_local *lp = netdev_priv(ndev);
diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c
index 04e552aa14ec..617f898ba5f0 100644
--- a/drivers/net/myri_sbus.c
+++ b/drivers/net/myri_sbus.c
@@ -926,7 +926,7 @@ static const struct net_device_ops myri_ops = {
.ndo_validate_addr = eth_validate_addr,
};
-static int __devinit myri_sbus_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit myri_sbus_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
static unsigned version_printed;
@@ -1124,7 +1124,7 @@ err:
return -ENODEV;
}
-static int __devexit myri_sbus_remove(struct of_device *op)
+static int __devexit myri_sbus_remove(struct platform_device *op)
{
struct myri_eth *mp = dev_get_drvdata(&op->dev);
struct net_device *net_dev = mp->dev;
diff --git a/drivers/net/myri_sbus.h b/drivers/net/myri_sbus.h
index ff363e95d9cf..80a2fa5cf757 100644
--- a/drivers/net/myri_sbus.h
+++ b/drivers/net/myri_sbus.h
@@ -288,7 +288,7 @@ struct myri_eth {
struct myri_eeprom eeprom; /* Local copy of EEPROM. */
unsigned int reg_size; /* Size of register space. */
unsigned int shmem_base; /* Offset to shared ram. */
- struct of_device *myri_op; /* Our OF device struct. */
+ struct platform_device *myri_op; /* Our OF device struct. */
};
/* We use this to acquire receive skb's that we can DMA directly into. */
diff --git a/drivers/net/niu.c b/drivers/net/niu.c
index 404f2d552888..bc695d53cdcc 100644
--- a/drivers/net/niu.c
+++ b/drivers/net/niu.c
@@ -9103,7 +9103,7 @@ retry:
static int __devinit niu_n2_irq_init(struct niu *np, u8 *ldg_num_map)
{
#ifdef CONFIG_SPARC64
- struct of_device *op = np->op;
+ struct platform_device *op = np->op;
const u32 *int_prop;
int i;
@@ -9688,7 +9688,7 @@ static void __devinit niu_driver_version(void)
static struct net_device * __devinit niu_alloc_and_init(
struct device *gen_dev, struct pci_dev *pdev,
- struct of_device *op, const struct niu_ops *ops,
+ struct platform_device *op, const struct niu_ops *ops,
u8 port)
{
struct net_device *dev;
@@ -10064,7 +10064,7 @@ static const struct niu_ops niu_phys_ops = {
.unmap_single = niu_phys_unmap_single,
};
-static int __devinit niu_of_probe(struct of_device *op,
+static int __devinit niu_of_probe(struct platform_device *op,
const struct of_device_id *match)
{
union niu_parent_id parent_id;
@@ -10179,7 +10179,7 @@ err_out:
return err;
}
-static int __devexit niu_of_remove(struct of_device *op)
+static int __devexit niu_of_remove(struct platform_device *op)
{
struct net_device *dev = dev_get_drvdata(&op->dev);
diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c
index fc5fef2a8175..f62c7b717bc8 100644
--- a/drivers/net/phy/mdio-gpio.c
+++ b/drivers/net/phy/mdio-gpio.c
@@ -188,7 +188,7 @@ static int __devexit mdio_gpio_remove(struct platform_device *pdev)
#ifdef CONFIG_OF_GPIO
-static int __devinit mdio_ofgpio_probe(struct of_device *ofdev,
+static int __devinit mdio_ofgpio_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct mdio_gpio_platform_data *pdata;
@@ -224,7 +224,7 @@ out_free:
return -ENODEV;
}
-static int __devexit mdio_ofgpio_remove(struct of_device *ofdev)
+static int __devexit mdio_ofgpio_remove(struct platform_device *ofdev)
{
mdio_gpio_bus_destroy(&ofdev->dev);
kfree(ofdev->dev.platform_data);
diff --git a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c
index 09c071bd6ad4..618643e3ca3e 100644
--- a/drivers/net/sunbmac.c
+++ b/drivers/net/sunbmac.c
@@ -97,7 +97,7 @@ static int qec_global_reset(void __iomem *gregs)
static void qec_init(struct bigmac *bp)
{
- struct of_device *qec_op = bp->qec_op;
+ struct platform_device *qec_op = bp->qec_op;
void __iomem *gregs = bp->gregs;
u8 bsizes = bp->bigmac_bursts;
u32 regval;
@@ -1083,8 +1083,8 @@ static const struct net_device_ops bigmac_ops = {
.ndo_validate_addr = eth_validate_addr,
};
-static int __devinit bigmac_ether_init(struct of_device *op,
- struct of_device *qec_op)
+static int __devinit bigmac_ether_init(struct platform_device *op,
+ struct platform_device *qec_op)
{
static int version_printed;
struct net_device *dev;
@@ -1242,25 +1242,25 @@ fail_and_cleanup:
/* QEC can be the parent of either QuadEthernet or a BigMAC. We want
* the latter.
*/
-static int __devinit bigmac_sbus_probe(struct of_device *op,
+static int __devinit bigmac_sbus_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device *parent = op->dev.parent;
- struct of_device *qec_op;
+ struct platform_device *qec_op;
- qec_op = to_of_device(parent);
+ qec_op = to_platform_device(parent);
return bigmac_ether_init(op, qec_op);
}
-static int __devexit bigmac_sbus_remove(struct of_device *op)
+static int __devexit bigmac_sbus_remove(struct platform_device *op)
{
struct bigmac *bp = dev_get_drvdata(&op->dev);
struct device *parent = op->dev.parent;
struct net_device *net_dev = bp->dev;
- struct of_device *qec_op;
+ struct platform_device *qec_op;
- qec_op = to_of_device(parent);
+ qec_op = to_platform_device(parent);
unregister_netdev(net_dev);
diff --git a/drivers/net/sunbmac.h b/drivers/net/sunbmac.h
index 8840bc0b840b..8db88945b889 100644
--- a/drivers/net/sunbmac.h
+++ b/drivers/net/sunbmac.h
@@ -329,8 +329,8 @@ struct bigmac {
unsigned int timer_ticks;
struct net_device_stats enet_stats;
- struct of_device *qec_op;
- struct of_device *bigmac_op;
+ struct platform_device *qec_op;
+ struct platform_device *bigmac_op;
struct net_device *dev;
};
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index eec443f64079..bd0df1c14955 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -1591,7 +1591,7 @@ static int happy_meal_init(struct happy_meal *hp)
*/
#ifdef CONFIG_SBUS
if ((hp->happy_flags & HFLAG_PCI) == 0) {
- struct of_device *op = hp->happy_dev;
+ struct platform_device *op = hp->happy_dev;
if (sbus_can_dma_64bit()) {
sbus_set_sbus64(&op->dev,
hp->happy_bursts);
@@ -2480,7 +2480,7 @@ static void hme_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info
#ifdef CONFIG_SBUS
else {
const struct linux_prom_registers *regs;
- struct of_device *op = hp->happy_dev;
+ struct platform_device *op = hp->happy_dev;
regs = of_get_property(op->dev.of_node, "regs", NULL);
if (regs)
sprintf(info->bus_info, "SBUS:%d",
@@ -2515,13 +2515,13 @@ static int hme_version_printed;
*
* Return NULL on failure.
*/
-static struct quattro * __devinit quattro_sbus_find(struct of_device *child)
+static struct quattro * __devinit quattro_sbus_find(struct platform_device *child)
{
struct device *parent = child->dev.parent;
- struct of_device *op;
+ struct platform_device *op;
struct quattro *qp;
- op = to_of_device(parent);
+ op = to_platform_device(parent);
qp = dev_get_drvdata(&op->dev);
if (qp)
return qp;
@@ -2551,7 +2551,7 @@ static int __init quattro_sbus_register_irqs(void)
struct quattro *qp;
for (qp = qfe_sbus_list; qp != NULL; qp = qp->next) {
- struct of_device *op = qp->quattro_dev;
+ struct platform_device *op = qp->quattro_dev;
int err, qfe_slot, skip = 0;
for (qfe_slot = 0; qfe_slot < 4; qfe_slot++) {
@@ -2580,7 +2580,7 @@ static void quattro_sbus_free_irqs(void)
struct quattro *qp;
for (qp = qfe_sbus_list; qp != NULL; qp = qp->next) {
- struct of_device *op = qp->quattro_dev;
+ struct platform_device *op = qp->quattro_dev;
int qfe_slot, skip = 0;
for (qfe_slot = 0; qfe_slot < 4; qfe_slot++) {
@@ -2639,7 +2639,7 @@ static const struct net_device_ops hme_netdev_ops = {
};
#ifdef CONFIG_SBUS
-static int __devinit happy_meal_sbus_probe_one(struct of_device *op, int is_qfe)
+static int __devinit happy_meal_sbus_probe_one(struct platform_device *op, int is_qfe)
{
struct device_node *dp = op->dev.of_node, *sbus_dp;
struct quattro *qp = NULL;
@@ -2648,7 +2648,7 @@ static int __devinit happy_meal_sbus_probe_one(struct of_device *op, int is_qfe)
int i, qfe_slot = -1;
int err = -ENODEV;
- sbus_dp = to_of_device(op->dev.parent)->dev.of_node;
+ sbus_dp = op->dev.parent->of_node;
/* We can match PCI devices too, do not accept those here. */
if (strcmp(sbus_dp->name, "sbus"))
@@ -3235,7 +3235,7 @@ static void happy_meal_pci_exit(void)
#endif
#ifdef CONFIG_SBUS
-static int __devinit hme_sbus_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit hme_sbus_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
const char *model = of_get_property(dp, "model", NULL);
@@ -3247,7 +3247,7 @@ static int __devinit hme_sbus_probe(struct of_device *op, const struct of_device
return happy_meal_sbus_probe_one(op, is_qfe);
}
-static int __devexit hme_sbus_remove(struct of_device *op)
+static int __devexit hme_sbus_remove(struct platform_device *op)
{
struct happy_meal *hp = dev_get_drvdata(&op->dev);
struct net_device *net_dev = hp->dev;
diff --git a/drivers/net/sunhme.h b/drivers/net/sunhme.h
index efd2ca0fcad3..756b5bf3aa89 100644
--- a/drivers/net/sunhme.h
+++ b/drivers/net/sunhme.h
@@ -407,7 +407,7 @@ struct happy_meal {
void (*write_rxd)(struct happy_meal_rxd *, u32, u32);
#endif
- /* This is either an of_device or a pci_dev. */
+ /* This is either an platform_device or a pci_dev. */
void *happy_dev;
struct device *dma_dev;
diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c
index ee364fa75634..8dcb858f2168 100644
--- a/drivers/net/sunlance.c
+++ b/drivers/net/sunlance.c
@@ -250,7 +250,7 @@ struct lance_private {
int rx_new, tx_new;
int rx_old, tx_old;
- struct of_device *ledma; /* If set this points to ledma */
+ struct platform_device *ledma; /* If set this points to ledma */
char tpe; /* cable-selection is TPE */
char auto_select; /* cable-selection by carrier */
char burst_sizes; /* ledma SBus burst sizes */
@@ -265,8 +265,8 @@ struct lance_private {
char *name;
dma_addr_t init_block_dvma;
struct net_device *dev; /* Backpointer */
- struct of_device *op;
- struct of_device *lebuffer;
+ struct platform_device *op;
+ struct platform_device *lebuffer;
struct timer_list multicast_timer;
};
@@ -1272,7 +1272,7 @@ static void lance_free_hwresources(struct lance_private *lp)
if (lp->lregs)
of_iounmap(&lp->op->resource[0], lp->lregs, LANCE_REG_SIZE);
if (lp->dregs) {
- struct of_device *ledma = lp->ledma;
+ struct platform_device *ledma = lp->ledma;
of_iounmap(&ledma->resource[0], lp->dregs,
resource_size(&ledma->resource[0]));
@@ -1319,9 +1319,9 @@ static const struct net_device_ops sparc_lance_ops = {
.ndo_validate_addr = eth_validate_addr,
};
-static int __devinit sparc_lance_probe_one(struct of_device *op,
- struct of_device *ledma,
- struct of_device *lebuffer)
+static int __devinit sparc_lance_probe_one(struct platform_device *op,
+ struct platform_device *ledma,
+ struct platform_device *lebuffer)
{
struct device_node *dp = op->dev.of_node;
static unsigned version_printed;
@@ -1503,9 +1503,9 @@ fail:
return -ENODEV;
}
-static int __devinit sunlance_sbus_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit sunlance_sbus_probe(struct platform_device *op, const struct of_device_id *match)
{
- struct of_device *parent = to_of_device(op->dev.parent);
+ struct platform_device *parent = to_platform_device(op->dev.parent);
struct device_node *parent_dp = parent->dev.of_node;
int err;
@@ -1519,7 +1519,7 @@ static int __devinit sunlance_sbus_probe(struct of_device *op, const struct of_d
return err;
}
-static int __devexit sunlance_sbus_remove(struct of_device *op)
+static int __devexit sunlance_sbus_remove(struct platform_device *op)
{
struct lance_private *lp = dev_get_drvdata(&op->dev);
struct net_device *net_dev = lp->dev;
diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c
index 5f84a5dadedd..72e65d4666ef 100644
--- a/drivers/net/sunqe.c
+++ b/drivers/net/sunqe.c
@@ -689,7 +689,7 @@ static void qe_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
const struct linux_prom_registers *regs;
struct sunqe *qep = netdev_priv(dev);
- struct of_device *op;
+ struct platform_device *op;
strcpy(info->driver, "sunqe");
strcpy(info->version, "3.0");
@@ -720,7 +720,7 @@ static const struct ethtool_ops qe_ethtool_ops = {
};
/* This is only called once at boot time for each card probed. */
-static void qec_init_once(struct sunqec *qecp, struct of_device *op)
+static void qec_init_once(struct sunqec *qecp, struct platform_device *op)
{
u8 bsizes = qecp->qec_bursts;
@@ -770,9 +770,9 @@ static u8 __devinit qec_get_burst(struct device_node *dp)
return bsizes;
}
-static struct sunqec * __devinit get_qec(struct of_device *child)
+static struct sunqec * __devinit get_qec(struct platform_device *child)
{
- struct of_device *op = to_of_device(child->dev.parent);
+ struct platform_device *op = to_platform_device(child->dev.parent);
struct sunqec *qecp;
qecp = dev_get_drvdata(&op->dev);
@@ -836,7 +836,7 @@ static const struct net_device_ops qec_ops = {
.ndo_validate_addr = eth_validate_addr,
};
-static int __devinit qec_ether_init(struct of_device *op)
+static int __devinit qec_ether_init(struct platform_device *op)
{
static unsigned version_printed;
struct net_device *dev;
@@ -941,12 +941,12 @@ fail:
return res;
}
-static int __devinit qec_sbus_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit qec_sbus_probe(struct platform_device *op, const struct of_device_id *match)
{
return qec_ether_init(op);
}
-static int __devexit qec_sbus_remove(struct of_device *op)
+static int __devexit qec_sbus_remove(struct platform_device *op)
{
struct sunqe *qp = dev_get_drvdata(&op->dev);
struct net_device *net_dev = qp->dev;
@@ -997,7 +997,7 @@ static void __exit qec_exit(void)
while (root_qec_dev) {
struct sunqec *next = root_qec_dev->next_module;
- struct of_device *op = root_qec_dev->op;
+ struct platform_device *op = root_qec_dev->op;
free_irq(op->archdata.irqs[0], (void *) root_qec_dev);
of_iounmap(&op->resource[0], root_qec_dev->gregs,
diff --git a/drivers/net/sunqe.h b/drivers/net/sunqe.h
index 5813a7b2faa5..581781b6b2fa 100644
--- a/drivers/net/sunqe.h
+++ b/drivers/net/sunqe.h
@@ -314,7 +314,7 @@ struct sunqec {
void __iomem *gregs; /* QEC Global Registers */
struct sunqe *qes[4]; /* Each child MACE */
unsigned int qec_bursts; /* Support burst sizes */
- struct of_device *op; /* QEC's OF device */
+ struct platform_device *op; /* QEC's OF device */
struct sunqec *next_module; /* List of all QECs in system */
};
@@ -342,7 +342,7 @@ struct sunqe {
__u32 buffers_dvma; /* DVMA visible address. */
struct sunqec *parent;
u8 mconfig; /* Base MACE mconfig value */
- struct of_device *op; /* QE's OF device struct */
+ struct platform_device *op; /* QE's OF device struct */
struct net_device *dev; /* QE's netdevice struct */
int channel; /* Who am I? */
};
diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c
index 8d532f9b50d0..a4c3f5708246 100644
--- a/drivers/net/ucc_geth.c
+++ b/drivers/net/ucc_geth.c
@@ -3601,7 +3601,7 @@ static void ucc_geth_timeout(struct net_device *dev)
#ifdef CONFIG_PM
-static int ucc_geth_suspend(struct of_device *ofdev, pm_message_t state)
+static int ucc_geth_suspend(struct platform_device *ofdev, pm_message_t state)
{
struct net_device *ndev = dev_get_drvdata(&ofdev->dev);
struct ucc_geth_private *ugeth = netdev_priv(ndev);
@@ -3629,7 +3629,7 @@ static int ucc_geth_suspend(struct of_device *ofdev, pm_message_t state)
return 0;
}
-static int ucc_geth_resume(struct of_device *ofdev)
+static int ucc_geth_resume(struct platform_device *ofdev)
{
struct net_device *ndev = dev_get_drvdata(&ofdev->dev);
struct ucc_geth_private *ugeth = netdev_priv(ndev);
@@ -3732,7 +3732,7 @@ static const struct net_device_ops ucc_geth_netdev_ops = {
#endif
};
-static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *match)
+static int ucc_geth_probe(struct platform_device* ofdev, const struct of_device_id *match)
{
struct device *device = &ofdev->dev;
struct device_node *np = ofdev->dev.of_node;
@@ -3954,7 +3954,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma
return 0;
}
-static int ucc_geth_remove(struct of_device* ofdev)
+static int ucc_geth_remove(struct platform_device* ofdev)
{
struct device *device = &ofdev->dev;
struct net_device *dev = dev_get_drvdata(device);
diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c
index 88e363033e23..6c571e198835 100644
--- a/drivers/net/wan/ixp4xx_hss.c
+++ b/drivers/net/wan/ixp4xx_hss.c
@@ -396,7 +396,7 @@ static void hss_config(struct port *port)
msg.cmd = PORT_CONFIG_WRITE;
msg.hss_port = port->id;
msg.index = HSS_CONFIG_TX_PCR;
- msg.data32 = PCR_FRM_SYNC_OUTPUT_RISING | PCR_MSB_ENDIAN |
+ msg.data32 = PCR_FRM_PULSE_DISABLED | PCR_MSB_ENDIAN |
PCR_TX_DATA_ENABLE | PCR_SOF_NO_FBIT;
if (port->clock_type == CLOCK_INT)
msg.data32 |= PCR_SYNC_CLK_DIR_OUTPUT;
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index 8e9fbfd804b6..2372abb29c2e 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -6,6 +6,8 @@
*
*/
+#include <linux/sched.h>
+#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/ieee80211.h>
#include <net/cfg80211.h>
diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c
index b2c2f391b29d..ecbbb688eba0 100644
--- a/drivers/net/xilinx_emaclite.c
+++ b/drivers/net/xilinx_emaclite.c
@@ -1086,7 +1086,7 @@ static void xemaclite_remove_ndev(struct net_device *ndev)
*
* Return: Value of the parameter if the parameter is found, or 0 otherwise
*/
-static bool get_bool(struct of_device *ofdev, const char *s)
+static bool get_bool(struct platform_device *ofdev, const char *s)
{
u32 *p = (u32 *)of_get_property(ofdev->dev.of_node, s, NULL);
@@ -1115,7 +1115,7 @@ static struct net_device_ops xemaclite_netdev_ops;
* Return: 0, if the driver is bound to the Emaclite device, or
* a negative error if there is failure.
*/
-static int __devinit xemaclite_of_probe(struct of_device *ofdev,
+static int __devinit xemaclite_of_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct resource r_irq; /* Interrupt resources */
@@ -1240,7 +1240,7 @@ error2:
*
* Return: 0, always.
*/
-static int __devexit xemaclite_of_remove(struct of_device *of_dev)
+static int __devexit xemaclite_of_remove(struct platform_device *of_dev)
{
struct device *dev = &of_dev->dev;
struct net_device *ndev = dev_get_drvdata(dev);
diff --git a/drivers/of/device.c b/drivers/of/device.c
index 0d8a0644f540..92de0eb74aea 100644
--- a/drivers/of/device.c
+++ b/drivers/of/device.c
@@ -14,7 +14,7 @@
* @ids: array of of device match structures to search in
* @dev: the of device structure to match against
*
- * Used by a driver to check whether an of_device present in the
+ * Used by a driver to check whether an platform_device present in the
* system is in its list of supported devices.
*/
const struct of_device_id *of_match_device(const struct of_device_id *matches,
diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c
index 40e208d130f5..f01e26440f11 100644
--- a/drivers/parport/parport_serial.c
+++ b/drivers/parport/parport_serial.c
@@ -342,7 +342,6 @@ static int __devinit parport_register (struct pci_dev *dev,
dev_dbg(&dev->dev,
"PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n",
io_lo, io_hi, irq);
- irq = PARPORT_IRQ_NONE;
}
port = parport_pc_probe_port (io_lo, io_hi, irq,
PARPORT_DMA_NONE, &dev->dev, IRQF_SHARED);
diff --git a/drivers/parport/parport_sunbpp.c b/drivers/parport/parport_sunbpp.c
index 210a6441a066..55ba118f1cf1 100644
--- a/drivers/parport/parport_sunbpp.c
+++ b/drivers/parport/parport_sunbpp.c
@@ -286,7 +286,7 @@ static struct parport_operations parport_sunbpp_ops =
.owner = THIS_MODULE,
};
-static int __devinit bpp_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit bpp_probe(struct platform_device *op, const struct of_device_id *match)
{
struct parport_operations *ops;
struct bpp_regs __iomem *regs;
@@ -351,7 +351,7 @@ out_unmap:
return err;
}
-static int __devexit bpp_remove(struct of_device *op)
+static int __devexit bpp_remove(struct platform_device *op)
{
struct parport *p = dev_get_drvdata(&op->dev);
struct parport_operations *ops = p->ops;
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index c988514eb551..c80a7a6e7698 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -215,7 +215,7 @@ config PCMCIA_PXA2XX
depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \
|| MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \
|| ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \
- || MACH_VPAC270)
+ || MACH_VPAC270 || MACH_BALLOON3)
select PCMCIA_SOC_COMMON
help
Say Y here to include support for the PXA2xx PCMCIA controller
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index 7a2b1604bf1c..8d9386a22eb3 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -69,6 +69,7 @@ pxa2xx-obj-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o
pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o
pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o
pxa2xx-obj-$(CONFIG_MACH_VPAC270) += pxa2xx_vpac270.o
+pxa2xx-obj-$(CONFIG_MACH_BALLOON3) += pxa2xx_balloon3.o
obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_base.o $(pxa2xx-obj-y)
diff --git a/drivers/pcmcia/electra_cf.c b/drivers/pcmcia/electra_cf.c
index f94d8281cfb0..546d3024b6f0 100644
--- a/drivers/pcmcia/electra_cf.c
+++ b/drivers/pcmcia/electra_cf.c
@@ -44,7 +44,7 @@ struct electra_cf_socket {
unsigned present:1;
unsigned active:1;
- struct of_device *ofdev;
+ struct platform_device *ofdev;
unsigned long mem_phys;
void __iomem * mem_base;
unsigned long mem_size;
@@ -181,7 +181,7 @@ static struct pccard_operations electra_cf_ops = {
.set_mem_map = electra_cf_set_mem_map,
};
-static int __devinit electra_cf_probe(struct of_device *ofdev,
+static int __devinit electra_cf_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device *device = &ofdev->dev;
@@ -325,7 +325,7 @@ fail1:
}
-static int __devexit electra_cf_remove(struct of_device *ofdev)
+static int __devexit electra_cf_remove(struct platform_device *ofdev)
{
struct device *device = &ofdev->dev;
struct electra_cf_socket *cf;
diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c
index f2f90a7d3e12..f0ecad99ce81 100644
--- a/drivers/pcmcia/m8xx_pcmcia.c
+++ b/drivers/pcmcia/m8xx_pcmcia.c
@@ -1149,7 +1149,7 @@ static struct pccard_operations m8xx_services = {
.set_mem_map = m8xx_set_mem_map,
};
-static int __init m8xx_probe(struct of_device *ofdev,
+static int __init m8xx_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct pcmcia_win *w;
@@ -1249,7 +1249,7 @@ static int __init m8xx_probe(struct of_device *ofdev,
return 0;
}
-static int m8xx_remove(struct of_device *ofdev)
+static int m8xx_remove(struct platform_device *ofdev)
{
u32 m, i;
struct pcmcia_win *w;
diff --git a/drivers/pcmcia/pxa2xx_balloon3.c b/drivers/pcmcia/pxa2xx_balloon3.c
new file mode 100644
index 000000000000..dbbdd0063202
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_balloon3.c
@@ -0,0 +1,158 @@
+/*
+ * linux/drivers/pcmcia/pxa2xx_balloon3.c
+ *
+ * Balloon3 PCMCIA specific routines.
+ *
+ * Author: Nick Bane
+ * Created: June, 2006
+ * Copyright: Toby Churchill Ltd
+ * Derived from pxa2xx_mainstone.c, by Nico Pitre
+ *
+ * Various modification by Marek Vasut <marek.vasut@gmail.com>
+ *
+ * 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/gpio.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <mach/balloon3.h>
+
+#include "soc_common.h"
+
+/*
+ * These are a list of interrupt sources that provokes a polled
+ * check of status
+ */
+static struct pcmcia_irqs irqs[] = {
+ { 0, BALLOON3_S0_CD_IRQ, "PCMCIA0 CD" },
+ { 0, BALLOON3_BP_NSTSCHG_IRQ, "PCMCIA0 STSCHG" },
+};
+
+static int balloon3_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+ uint16_t ver;
+ int ret;
+ static void __iomem *fpga_ver;
+
+ ver = __raw_readw(BALLOON3_FPGA_VER);
+ if (ver > 0x0201)
+ pr_warn("The FPGA code, version 0x%04x, is newer than rel-0.3. "
+ "PCMCIA/CF support might be broken in this version!",
+ ver);
+
+ skt->socket.pci_irq = BALLOON3_BP_CF_NRDY_IRQ;
+ return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void balloon3_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static unsigned long balloon3_pcmcia_status[2] = {
+ BALLOON3_CF_nSTSCHG_BVD1,
+ BALLOON3_CF_nSTSCHG_BVD1
+};
+
+static void balloon3_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+ struct pcmcia_state *state)
+{
+ uint16_t status;
+ int flip;
+
+ /* This actually reads the STATUS register */
+ status = __raw_readw(BALLOON3_CF_STATUS_REG);
+ flip = (status ^ balloon3_pcmcia_status[skt->nr])
+ & BALLOON3_CF_nSTSCHG_BVD1;
+ /*
+ * Workaround for STSCHG which can't be deasserted:
+ * We therefore disable/enable corresponding IRQs
+ * as needed to avoid IRQ locks.
+ */
+ if (flip) {
+ balloon3_pcmcia_status[skt->nr] = status;
+ if (status & BALLOON3_CF_nSTSCHG_BVD1)
+ enable_irq(BALLOON3_BP_NSTSCHG_IRQ);
+ else
+ disable_irq(BALLOON3_BP_NSTSCHG_IRQ);
+ }
+
+ state->detect = !gpio_get_value(BALLOON3_GPIO_S0_CD);
+ state->ready = !!(status & BALLOON3_CF_nIRQ);
+ state->bvd1 = !!(status & BALLOON3_CF_nSTSCHG_BVD1);
+ state->bvd2 = 0; /* not available */
+ state->vs_3v = 1; /* Always true its a CF card */
+ state->vs_Xv = 0; /* not available */
+ state->wrprot = 0; /* not available */
+}
+
+static int balloon3_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+ const socket_state_t *state)
+{
+ __raw_writew((state->flags & SS_RESET) ? BALLOON3_CF_RESET : 0,
+ BALLOON3_CF_CONTROL_REG);
+ return 0;
+}
+
+static void balloon3_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+}
+
+static void balloon3_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+}
+
+static struct pcmcia_low_level balloon3_pcmcia_ops = {
+ .owner = THIS_MODULE,
+ .hw_init = balloon3_pcmcia_hw_init,
+ .hw_shutdown = balloon3_pcmcia_hw_shutdown,
+ .socket_state = balloon3_pcmcia_socket_state,
+ .configure_socket = balloon3_pcmcia_configure_socket,
+ .socket_init = balloon3_pcmcia_socket_init,
+ .socket_suspend = balloon3_pcmcia_socket_suspend,
+ .first = 0,
+ .nr = 1,
+};
+
+static struct platform_device *balloon3_pcmcia_device;
+
+static int __init balloon3_pcmcia_init(void)
+{
+ int ret;
+
+ balloon3_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
+ if (!balloon3_pcmcia_device)
+ return -ENOMEM;
+
+ ret = platform_device_add_data(balloon3_pcmcia_device,
+ &balloon3_pcmcia_ops, sizeof(balloon3_pcmcia_ops));
+
+ if (!ret)
+ ret = platform_device_add(balloon3_pcmcia_device);
+
+ if (ret)
+ platform_device_put(balloon3_pcmcia_device);
+
+ return ret;
+}
+
+static void __exit balloon3_pcmcia_exit(void)
+{
+ platform_device_unregister(balloon3_pcmcia_device);
+}
+
+module_init(balloon3_pcmcia_init);
+module_exit(balloon3_pcmcia_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nick Bane <nick@cecomputing.co.uk>");
+MODULE_ALIAS("platform:pxa2xx-pcmcia");
+MODULE_DESCRIPTION("Balloon3 board CF/PCMCIA driver");
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 1e5506be39b4..07343568a12e 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -136,6 +136,12 @@ config BATTERY_Z2
help
Say Y to include support for the battery on the Zipit Z2.
+config BATTERY_S3C_ADC
+ tristate "Battery driver for Samsung ADC based monitoring"
+ depends on S3C_ADC
+ help
+ Say Y here to enable support for iPAQ h1930/h1940/rx1950 battery
+
config CHARGER_PCF50633
tristate "NXP PCF50633 MBC"
depends on MFD_PCF50633
@@ -153,4 +159,11 @@ config BATTERY_JZ4740
This driver can be build as a module. If so, the module will be
called jz4740-battery.
+config BATTERY_INTEL_MID
+ tristate "Battery driver for Intel MID platforms"
+ depends on INTEL_SCU_IPC && SPI
+ help
+ Say Y here to enable the battery driver on Intel MID
+ platforms.
+
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index cf95009d9bcd..10143aaf4ee3 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -33,5 +33,7 @@ obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
+obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
+obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
diff --git a/drivers/power/intel_mid_battery.c b/drivers/power/intel_mid_battery.c
new file mode 100644
index 000000000000..c61ffec2ff10
--- /dev/null
+++ b/drivers/power/intel_mid_battery.c
@@ -0,0 +1,799 @@
+/*
+ * intel_mid_battery.c - Intel MID PMIC Battery Driver
+ *
+ * Copyright (C) 2009 Intel Corporation
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Author: Nithish Mahalingam <nithish.mahalingam@intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/param.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+#include <asm/intel_scu_ipc.h>
+
+#define DRIVER_NAME "pmic_battery"
+
+/*********************************************************************
+ * Generic defines
+ *********************************************************************/
+
+static int debug;
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "Flag to enable PMIC Battery debug messages.");
+
+#define PMIC_BATT_DRV_INFO_UPDATED 1
+#define PMIC_BATT_PRESENT 1
+#define PMIC_BATT_NOT_PRESENT 0
+#define PMIC_USB_PRESENT PMIC_BATT_PRESENT
+#define PMIC_USB_NOT_PRESENT PMIC_BATT_NOT_PRESENT
+
+/* pmic battery register related */
+#define PMIC_BATT_CHR_SCHRGINT_ADDR 0xD2
+#define PMIC_BATT_CHR_SBATOVP_MASK (1 << 1)
+#define PMIC_BATT_CHR_STEMP_MASK (1 << 2)
+#define PMIC_BATT_CHR_SCOMP_MASK (1 << 3)
+#define PMIC_BATT_CHR_SUSBDET_MASK (1 << 4)
+#define PMIC_BATT_CHR_SBATDET_MASK (1 << 5)
+#define PMIC_BATT_CHR_SDCLMT_MASK (1 << 6)
+#define PMIC_BATT_CHR_SUSBOVP_MASK (1 << 7)
+#define PMIC_BATT_CHR_EXCPT_MASK 0xC6
+#define PMIC_BATT_ADC_ACCCHRG_MASK (1 << 31)
+#define PMIC_BATT_ADC_ACCCHRGVAL_MASK 0x7FFFFFFF
+
+/* pmic ipc related */
+#define PMIC_BATT_CHR_IPC_FCHRG_SUBID 0x4
+#define PMIC_BATT_CHR_IPC_TCHRG_SUBID 0x6
+
+/* types of battery charging */
+enum batt_charge_type {
+ BATT_USBOTG_500MA_CHARGE,
+ BATT_USBOTG_TRICKLE_CHARGE,
+};
+
+/* valid battery events */
+enum batt_event {
+ BATT_EVENT_BATOVP_EXCPT,
+ BATT_EVENT_USBOVP_EXCPT,
+ BATT_EVENT_TEMP_EXCPT,
+ BATT_EVENT_DCLMT_EXCPT,
+ BATT_EVENT_EXCPT
+};
+
+
+/*********************************************************************
+ * Battery properties
+ *********************************************************************/
+
+/*
+ * pmic battery info
+ */
+struct pmic_power_module_info {
+ bool is_dev_info_updated;
+ struct device *dev;
+ /* pmic battery data */
+ unsigned long update_time; /* jiffies when data read */
+ unsigned int usb_is_present;
+ unsigned int batt_is_present;
+ unsigned int batt_health;
+ unsigned int usb_health;
+ unsigned int batt_status;
+ unsigned int batt_charge_now; /* in mAS */
+ unsigned int batt_prev_charge_full; /* in mAS */
+ unsigned int batt_charge_rate; /* in units per second */
+
+ struct power_supply usb;
+ struct power_supply batt;
+ int irq; /* GPE_ID or IRQ# */
+ struct workqueue_struct *monitor_wqueue;
+ struct delayed_work monitor_battery;
+ struct work_struct handler;
+};
+
+static unsigned int delay_time = 2000; /* in ms */
+
+/*
+ * pmic ac properties
+ */
+static enum power_supply_property pmic_usb_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_HEALTH,
+};
+
+/*
+ * pmic battery properties
+ */
+static enum power_supply_property pmic_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+};
+
+
+/*
+ * Glue functions for talking to the IPC
+ */
+
+struct battery_property {
+ u32 capacity; /* Charger capacity */
+ u8 crnt; /* Quick charge current value*/
+ u8 volt; /* Fine adjustment of constant charge voltage */
+ u8 prot; /* CHRGPROT register value */
+ u8 prot2; /* CHRGPROT1 register value */
+ u8 timer; /* Charging timer */
+};
+
+#define IPCMSG_BATTERY 0xEF
+
+/* Battery coulomb counter accumulator commands */
+#define IPC_CMD_CC_WR 0 /* Update coulomb counter value */
+#define IPC_CMD_CC_RD 1 /* Read coulomb counter value */
+#define IPC_CMD_BATTERY_PROPERTY 2 /* Read Battery property */
+
+/**
+ * pmic_scu_ipc_battery_cc_read - read battery cc
+ * @value: battery coulomb counter read
+ *
+ * Reads the battery couloumb counter value, returns 0 on success, or
+ * an error code
+ *
+ * This function may sleep. Locking for SCU accesses is handled for
+ * the caller.
+ */
+static int pmic_scu_ipc_battery_cc_read(u32 *value)
+{
+ return intel_scu_ipc_command(IPCMSG_BATTERY, IPC_CMD_CC_RD,
+ NULL, 0, value, 1);
+}
+
+/**
+ * pmic_scu_ipc_battery_property_get - fetch properties
+ * @prop: battery properties
+ *
+ * Retrieve the battery properties from the power management
+ *
+ * This function may sleep. Locking for SCU accesses is handled for
+ * the caller.
+ */
+static int pmic_scu_ipc_battery_property_get(struct battery_property *prop)
+{
+ u32 data[3];
+ u8 *p = (u8 *)&data[1];
+ int err = intel_scu_ipc_command(IPC_CMD_BATTERY_PROPERTY,
+ IPCMSG_BATTERY, NULL, 0, data, 3);
+
+ prop->capacity = data[0];
+ prop->crnt = *p++;
+ prop->volt = *p++;
+ prop->prot = *p++;
+ prop->prot2 = *p++;
+ prop->timer = *p++;
+
+ return err;
+}
+
+/**
+ * pmic_scu_ipc_set_charger - set charger
+ * @charger: charger to select
+ *
+ * Switch the charging mode for the SCU
+ */
+
+static int pmic_scu_ipc_set_charger(int charger)
+{
+ return intel_scu_ipc_simple_command(charger, IPCMSG_BATTERY);
+}
+
+/**
+ * pmic_battery_log_event - log battery events
+ * @event: battery event to be logged
+ * Context: can sleep
+ *
+ * There are multiple battery events which may be of interest to users;
+ * this battery function logs the different battery events onto the
+ * kernel log messages.
+ */
+static void pmic_battery_log_event(enum batt_event event)
+{
+ printk(KERN_WARNING "pmic-battery: ");
+ switch (event) {
+ case BATT_EVENT_BATOVP_EXCPT:
+ printk(KERN_CONT "battery overvoltage condition\n");
+ break;
+ case BATT_EVENT_USBOVP_EXCPT:
+ printk(KERN_CONT "usb charger overvoltage condition\n");
+ break;
+ case BATT_EVENT_TEMP_EXCPT:
+ printk(KERN_CONT "high battery temperature condition\n");
+ break;
+ case BATT_EVENT_DCLMT_EXCPT:
+ printk(KERN_CONT "over battery charge current condition\n");
+ break;
+ default:
+ printk(KERN_CONT "charger/battery exception %d\n", event);
+ break;
+ }
+}
+
+/**
+ * pmic_battery_read_status - read battery status information
+ * @pbi: device info structure to update the read information
+ * Context: can sleep
+ *
+ * PMIC power source information need to be updated based on the data read
+ * from the PMIC battery registers.
+ *
+ */
+static void pmic_battery_read_status(struct pmic_power_module_info *pbi)
+{
+ unsigned int update_time_intrvl;
+ unsigned int chrg_val;
+ u32 ccval;
+ u8 r8;
+ struct battery_property batt_prop;
+ int batt_present = 0;
+ int usb_present = 0;
+ int batt_exception = 0;
+
+ /* make sure the last batt_status read happened delay_time before */
+ if (pbi->update_time && time_before(jiffies, pbi->update_time +
+ msecs_to_jiffies(delay_time)))
+ return;
+
+ update_time_intrvl = jiffies_to_msecs(jiffies - pbi->update_time);
+ pbi->update_time = jiffies;
+
+ /* read coulomb counter registers and schrgint register */
+ if (pmic_scu_ipc_battery_cc_read(&ccval)) {
+ dev_warn(pbi->dev, "%s(): ipc config cmd failed\n",
+ __func__);
+ return;
+ }
+
+ if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) {
+ dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
+ __func__);
+ return;
+ }
+
+ /*
+ * set pmic_power_module_info members based on pmic register values
+ * read.
+ */
+
+ /* set batt_is_present */
+ if (r8 & PMIC_BATT_CHR_SBATDET_MASK) {
+ pbi->batt_is_present = PMIC_BATT_PRESENT;
+ batt_present = 1;
+ } else {
+ pbi->batt_is_present = PMIC_BATT_NOT_PRESENT;
+ pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+
+ /* set batt_health */
+ if (batt_present) {
+ if (r8 & PMIC_BATT_CHR_SBATOVP_MASK) {
+ pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ pmic_battery_log_event(BATT_EVENT_BATOVP_EXCPT);
+ batt_exception = 1;
+ } else if (r8 & PMIC_BATT_CHR_SDCLMT_MASK) {
+ pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ pmic_battery_log_event(BATT_EVENT_DCLMT_EXCPT);
+ batt_exception = 1;
+ } else if (r8 & PMIC_BATT_CHR_STEMP_MASK) {
+ pbi->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT;
+ pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ pmic_battery_log_event(BATT_EVENT_TEMP_EXCPT);
+ batt_exception = 1;
+ } else {
+ pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD;
+ }
+ }
+
+ /* set usb_is_present */
+ if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) {
+ pbi->usb_is_present = PMIC_USB_PRESENT;
+ usb_present = 1;
+ } else {
+ pbi->usb_is_present = PMIC_USB_NOT_PRESENT;
+ pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ }
+
+ if (usb_present) {
+ if (r8 & PMIC_BATT_CHR_SUSBOVP_MASK) {
+ pbi->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ pmic_battery_log_event(BATT_EVENT_USBOVP_EXCPT);
+ } else {
+ pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD;
+ }
+ }
+
+ chrg_val = ccval & PMIC_BATT_ADC_ACCCHRGVAL_MASK;
+
+ /* set batt_prev_charge_full to battery capacity the first time */
+ if (!pbi->is_dev_info_updated) {
+ if (pmic_scu_ipc_battery_property_get(&batt_prop)) {
+ dev_warn(pbi->dev, "%s(): ipc config cmd failed\n",
+ __func__);
+ return;
+ }
+ pbi->batt_prev_charge_full = batt_prop.capacity;
+ }
+
+ /* set batt_status */
+ if (batt_present && !batt_exception) {
+ if (r8 & PMIC_BATT_CHR_SCOMP_MASK) {
+ pbi->batt_status = POWER_SUPPLY_STATUS_FULL;
+ pbi->batt_prev_charge_full = chrg_val;
+ } else if (ccval & PMIC_BATT_ADC_ACCCHRG_MASK) {
+ pbi->batt_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ } else {
+ pbi->batt_status = POWER_SUPPLY_STATUS_CHARGING;
+ }
+ }
+
+ /* set batt_charge_rate */
+ if (pbi->is_dev_info_updated && batt_present && !batt_exception) {
+ if (pbi->batt_status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ if (pbi->batt_charge_now - chrg_val) {
+ pbi->batt_charge_rate = ((pbi->batt_charge_now -
+ chrg_val) * 1000 * 60) /
+ update_time_intrvl;
+ }
+ } else if (pbi->batt_status == POWER_SUPPLY_STATUS_CHARGING) {
+ if (chrg_val - pbi->batt_charge_now) {
+ pbi->batt_charge_rate = ((chrg_val -
+ pbi->batt_charge_now) * 1000 * 60) /
+ update_time_intrvl;
+ }
+ } else
+ pbi->batt_charge_rate = 0;
+ } else {
+ pbi->batt_charge_rate = -1;
+ }
+
+ /* batt_charge_now */
+ if (batt_present && !batt_exception)
+ pbi->batt_charge_now = chrg_val;
+ else
+ pbi->batt_charge_now = -1;
+
+ pbi->is_dev_info_updated = PMIC_BATT_DRV_INFO_UPDATED;
+}
+
+/**
+ * pmic_usb_get_property - usb power source get property
+ * @psy: usb power supply context
+ * @psp: usb power source property
+ * @val: usb power source property value
+ * Context: can sleep
+ *
+ * PMIC usb power source property needs to be provided to power_supply
+ * subsytem for it to provide the information to users.
+ */
+static int pmic_usb_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct pmic_power_module_info *pbi = container_of(psy,
+ struct pmic_power_module_info, usb);
+
+ /* update pmic_power_module_info members */
+ pmic_battery_read_status(pbi);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = pbi->usb_is_present;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = pbi->usb_health;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static inline unsigned long mAStouAh(unsigned long v)
+{
+ /* seconds to hours, mA to µA */
+ return (v * 1000) / 3600;
+}
+
+/**
+ * pmic_battery_get_property - battery power source get property
+ * @psy: battery power supply context
+ * @psp: battery power source property
+ * @val: battery power source property value
+ * Context: can sleep
+ *
+ * PMIC battery power source property needs to be provided to power_supply
+ * subsytem for it to provide the information to users.
+ */
+static int pmic_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct pmic_power_module_info *pbi = container_of(psy,
+ struct pmic_power_module_info, batt);
+
+ /* update pmic_power_module_info members */
+ pmic_battery_read_status(pbi);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = pbi->batt_status;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = pbi->batt_health;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = pbi->batt_is_present;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = mAStouAh(pbi->batt_charge_now);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = mAStouAh(pbi->batt_prev_charge_full);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * pmic_battery_monitor - monitor battery status
+ * @work: work structure
+ * Context: can sleep
+ *
+ * PMIC battery status needs to be monitored for any change
+ * and information needs to be frequently updated.
+ */
+static void pmic_battery_monitor(struct work_struct *work)
+{
+ struct pmic_power_module_info *pbi = container_of(work,
+ struct pmic_power_module_info, monitor_battery.work);
+
+ /* update pmic_power_module_info members */
+ pmic_battery_read_status(pbi);
+ queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 10);
+}
+
+/**
+ * pmic_battery_set_charger - set battery charger
+ * @pbi: device info structure
+ * @chrg: charge mode to set battery charger in
+ * Context: can sleep
+ *
+ * PMIC battery charger needs to be enabled based on the usb charge
+ * capabilities connected to the platform.
+ */
+static int pmic_battery_set_charger(struct pmic_power_module_info *pbi,
+ enum batt_charge_type chrg)
+{
+ int retval;
+
+ /* set usblmt bits and chrgcntl register bits appropriately */
+ switch (chrg) {
+ case BATT_USBOTG_500MA_CHARGE:
+ retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_FCHRG_SUBID);
+ break;
+ case BATT_USBOTG_TRICKLE_CHARGE:
+ retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_TCHRG_SUBID);
+ break;
+ default:
+ dev_warn(pbi->dev, "%s(): out of range usb charger "
+ "charge detected\n", __func__);
+ return -EINVAL;
+ }
+
+ if (retval) {
+ dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
+ __func__);
+ return retval;;
+ }
+
+ return 0;
+}
+
+/**
+ * pmic_battery_interrupt_handler - pmic battery interrupt handler
+ * Context: interrupt context
+ *
+ * PMIC battery interrupt handler which will be called with either
+ * battery full condition occurs or usb otg & battery connect
+ * condition occurs.
+ */
+static irqreturn_t pmic_battery_interrupt_handler(int id, void *dev)
+{
+ struct pmic_power_module_info *pbi = dev;
+
+ schedule_work(&pbi->handler);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * pmic_battery_handle_intrpt - pmic battery service interrupt
+ * @work: work structure
+ * Context: can sleep
+ *
+ * PMIC battery needs to either update the battery status as full
+ * if it detects battery full condition caused the interrupt or needs
+ * to enable battery charger if it detects usb and battery detect
+ * caused the source of interrupt.
+ */
+static void pmic_battery_handle_intrpt(struct work_struct *work)
+{
+ struct pmic_power_module_info *pbi = container_of(work,
+ struct pmic_power_module_info, handler);
+ enum batt_charge_type chrg;
+ u8 r8;
+
+ if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) {
+ dev_warn(pbi->dev, "%s(): ipc pmic read failed\n",
+ __func__);
+ return;
+ }
+ /* find the cause of the interrupt */
+ if (r8 & PMIC_BATT_CHR_SBATDET_MASK) {
+ pbi->batt_is_present = PMIC_BATT_PRESENT;
+ } else {
+ pbi->batt_is_present = PMIC_BATT_NOT_PRESENT;
+ pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ return;
+ }
+
+ if (r8 & PMIC_BATT_CHR_EXCPT_MASK) {
+ pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ pmic_battery_log_event(BATT_EVENT_EXCPT);
+ return;
+ } else {
+ pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD;
+ pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD;
+ }
+
+ if (r8 & PMIC_BATT_CHR_SCOMP_MASK) {
+ u32 ccval;
+ pbi->batt_status = POWER_SUPPLY_STATUS_FULL;
+
+ if (pmic_scu_ipc_battery_cc_read(&ccval)) {
+ dev_warn(pbi->dev, "%s(): ipc config cmd "
+ "failed\n", __func__);
+ return;
+ }
+ pbi->batt_prev_charge_full = ccval &
+ PMIC_BATT_ADC_ACCCHRGVAL_MASK;
+ return;
+ }
+
+ if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) {
+ pbi->usb_is_present = PMIC_USB_PRESENT;
+ } else {
+ pbi->usb_is_present = PMIC_USB_NOT_PRESENT;
+ pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ return;
+ }
+
+ /* setup battery charging */
+
+#if 0
+ /* check usb otg power capability and set charger accordingly */
+ retval = langwell_udc_maxpower(&power);
+ if (retval) {
+ dev_warn(pbi->dev,
+ "%s(): usb otg power query failed with error code %d\n",
+ __func__, retval);
+ return;
+ }
+
+ if (power >= 500)
+ chrg = BATT_USBOTG_500MA_CHARGE;
+ else
+#endif
+ chrg = BATT_USBOTG_TRICKLE_CHARGE;
+
+ /* enable battery charging */
+ if (pmic_battery_set_charger(pbi, chrg)) {
+ dev_warn(pbi->dev,
+ "%s(): failed to set up battery charging\n", __func__);
+ return;
+ }
+
+ dev_dbg(pbi->dev,
+ "pmic-battery: %s() - setting up battery charger successful\n",
+ __func__);
+}
+
+/**
+ * pmic_battery_probe - pmic battery initialize
+ * @irq: pmic battery device irq
+ * @dev: pmic battery device structure
+ * Context: can sleep
+ *
+ * PMIC battery initializes its internal data structue and other
+ * infrastructure components for it to work as expected.
+ */
+static __devinit int probe(int irq, struct device *dev)
+{
+ int retval = 0;
+ struct pmic_power_module_info *pbi;
+
+ dev_dbg(dev, "pmic-battery: found pmic battery device\n");
+
+ pbi = kzalloc(sizeof(*pbi), GFP_KERNEL);
+ if (!pbi) {
+ dev_err(dev, "%s(): memory allocation failed\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ pbi->dev = dev;
+ pbi->irq = irq;
+ dev_set_drvdata(dev, pbi);
+
+ /* initialize all required framework before enabling interrupts */
+ INIT_WORK(&pbi->handler, pmic_battery_handle_intrpt);
+ INIT_DELAYED_WORK(&pbi->monitor_battery, pmic_battery_monitor);
+ pbi->monitor_wqueue =
+ create_singlethread_workqueue(dev_name(dev));
+ if (!pbi->monitor_wqueue) {
+ dev_err(dev, "%s(): wqueue init failed\n", __func__);
+ retval = -ESRCH;
+ goto wqueue_failed;
+ }
+
+ /* register interrupt */
+ retval = request_irq(pbi->irq, pmic_battery_interrupt_handler,
+ 0, DRIVER_NAME, pbi);
+ if (retval) {
+ dev_err(dev, "%s(): cannot get IRQ\n", __func__);
+ goto requestirq_failed;
+ }
+
+ /* register pmic-batt with power supply subsystem */
+ pbi->batt.name = "pmic-batt";
+ pbi->batt.type = POWER_SUPPLY_TYPE_BATTERY;
+ pbi->batt.properties = pmic_battery_props;
+ pbi->batt.num_properties = ARRAY_SIZE(pmic_battery_props);
+ pbi->batt.get_property = pmic_battery_get_property;
+ retval = power_supply_register(dev, &pbi->batt);
+ if (retval) {
+ dev_err(dev,
+ "%s(): failed to register pmic battery device with power supply subsystem\n",
+ __func__);
+ goto power_reg_failed;
+ }
+
+ dev_dbg(dev, "pmic-battery: %s() - pmic battery device "
+ "registration with power supply subsystem successful\n",
+ __func__);
+
+ queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 1);
+
+ /* register pmic-usb with power supply subsystem */
+ pbi->usb.name = "pmic-usb";
+ pbi->usb.type = POWER_SUPPLY_TYPE_USB;
+ pbi->usb.properties = pmic_usb_props;
+ pbi->usb.num_properties = ARRAY_SIZE(pmic_usb_props);
+ pbi->usb.get_property = pmic_usb_get_property;
+ retval = power_supply_register(dev, &pbi->usb);
+ if (retval) {
+ dev_err(dev,
+ "%s(): failed to register pmic usb device with power supply subsystem\n",
+ __func__);
+ goto power_reg_failed_1;
+ }
+
+ if (debug)
+ printk(KERN_INFO "pmic-battery: %s() - pmic usb device "
+ "registration with power supply subsystem successful\n",
+ __func__);
+
+ return retval;
+
+power_reg_failed_1:
+ power_supply_unregister(&pbi->batt);
+power_reg_failed:
+ cancel_rearming_delayed_workqueue(pbi->monitor_wqueue,
+ &pbi->monitor_battery);
+requestirq_failed:
+ destroy_workqueue(pbi->monitor_wqueue);
+wqueue_failed:
+ kfree(pbi);
+
+ return retval;
+}
+
+static int __devinit platform_pmic_battery_probe(struct platform_device *pdev)
+{
+ return probe(pdev->id, &pdev->dev);
+}
+
+/**
+ * pmic_battery_remove - pmic battery finalize
+ * @dev: pmic battery device structure
+ * Context: can sleep
+ *
+ * PMIC battery finalizes its internal data structue and other
+ * infrastructure components that it initialized in
+ * pmic_battery_probe.
+ */
+
+static int __devexit platform_pmic_battery_remove(struct platform_device *pdev)
+{
+ struct pmic_power_module_info *pbi = dev_get_drvdata(&pdev->dev);
+
+ free_irq(pbi->irq, pbi);
+ cancel_rearming_delayed_workqueue(pbi->monitor_wqueue,
+ &pbi->monitor_battery);
+ destroy_workqueue(pbi->monitor_wqueue);
+
+ power_supply_unregister(&pbi->usb);
+ power_supply_unregister(&pbi->batt);
+
+ flush_scheduled_work();
+ kfree(pbi);
+ return 0;
+}
+
+static struct platform_driver platform_pmic_battery_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = platform_pmic_battery_probe,
+ .remove = __devexit_p(platform_pmic_battery_remove),
+};
+
+static int __init platform_pmic_battery_module_init(void)
+{
+ return platform_driver_register(&platform_pmic_battery_driver);
+}
+
+static void __exit platform_pmic_battery_module_exit(void)
+{
+ platform_driver_unregister(&platform_pmic_battery_driver);
+}
+
+module_init(platform_pmic_battery_module_init);
+module_exit(platform_pmic_battery_module_exit);
+
+MODULE_AUTHOR("Nithish Mahalingam <nithish.mahalingam@intel.com>");
+MODULE_DESCRIPTION("Intel Moorestown PMIC Battery Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c
index 5ab9109b69e6..aafc1c506eda 100644
--- a/drivers/power/olpc_battery.c
+++ b/drivers/power/olpc_battery.c
@@ -1,7 +1,7 @@
/*
* Battery driver for One Laptop Per Child board.
*
- * Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
+ * Copyright © 2006-2010 David Woodhouse <dwmw2@infradead.org>
*
* 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
diff --git a/drivers/power/s3c_adc_battery.c b/drivers/power/s3c_adc_battery.c
new file mode 100644
index 000000000000..fe16b482e912
--- /dev/null
+++ b/drivers/power/s3c_adc_battery.c
@@ -0,0 +1,431 @@
+/*
+ * iPAQ h1930/h1940/rx1950 battery controler driver
+ * Copyright (c) Vasily Khoruzhick
+ * Based on h1940_battery.c by Arnaud Patard
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/leds.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/s3c_adc_battery.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <plat/adc.h>
+
+#define BAT_POLL_INTERVAL 10000 /* ms */
+#define JITTER_DELAY 500 /* ms */
+
+struct s3c_adc_bat {
+ struct power_supply psy;
+ struct s3c_adc_client *client;
+ struct s3c_adc_bat_pdata *pdata;
+ int volt_value;
+ int cur_value;
+ unsigned int timestamp;
+ int level;
+ int status;
+ int cable_plugged:1;
+};
+
+static struct delayed_work bat_work;
+
+static void s3c_adc_bat_ext_power_changed(struct power_supply *psy)
+{
+ schedule_delayed_work(&bat_work,
+ msecs_to_jiffies(JITTER_DELAY));
+}
+
+static enum power_supply_property s3c_adc_backup_bat_props[] = {
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+};
+
+static int s3c_adc_backup_bat_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct s3c_adc_bat *bat = container_of(psy, struct s3c_adc_bat, psy);
+
+ if (!bat) {
+ dev_err(psy->dev, "%s: no battery infos ?!\n", __func__);
+ return -EINVAL;
+ }
+
+ if (bat->volt_value < 0 ||
+ jiffies_to_msecs(jiffies - bat->timestamp) >
+ BAT_POLL_INTERVAL) {
+ bat->volt_value = s3c_adc_read(bat->client,
+ bat->pdata->backup_volt_channel);
+ bat->volt_value *= bat->pdata->backup_volt_mult;
+ bat->timestamp = jiffies;
+ }
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = bat->volt_value;
+ return 0;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ val->intval = bat->pdata->backup_volt_min;
+ return 0;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = bat->pdata->backup_volt_max;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct s3c_adc_bat backup_bat = {
+ .psy = {
+ .name = "backup-battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = s3c_adc_backup_bat_props,
+ .num_properties = ARRAY_SIZE(s3c_adc_backup_bat_props),
+ .get_property = s3c_adc_backup_bat_get_property,
+ .use_for_apm = 1,
+ },
+};
+
+static enum power_supply_property s3c_adc_main_bat_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+static int calc_full_volt(int volt_val, int cur_val, int impedance)
+{
+ return volt_val + cur_val * impedance / 1000;
+}
+
+static int s3c_adc_bat_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct s3c_adc_bat *bat = container_of(psy, struct s3c_adc_bat, psy);
+
+ int new_level;
+ int full_volt;
+ const struct s3c_adc_bat_thresh *lut = bat->pdata->lut_noac;
+ unsigned int lut_size = bat->pdata->lut_noac_cnt;
+
+ if (!bat) {
+ dev_err(psy->dev, "no battery infos ?!\n");
+ return -EINVAL;
+ }
+
+ if (bat->volt_value < 0 || bat->cur_value < 0 ||
+ jiffies_to_msecs(jiffies - bat->timestamp) >
+ BAT_POLL_INTERVAL) {
+ bat->volt_value = s3c_adc_read(bat->client,
+ bat->pdata->volt_channel) * bat->pdata->volt_mult;
+ bat->cur_value = s3c_adc_read(bat->client,
+ bat->pdata->current_channel) * bat->pdata->current_mult;
+ bat->timestamp = jiffies;
+ }
+
+ if (bat->cable_plugged &&
+ ((bat->pdata->gpio_charge_finished < 0) ||
+ !gpio_get_value(bat->pdata->gpio_charge_finished))) {
+ lut = bat->pdata->lut_acin;
+ lut_size = bat->pdata->lut_acin_cnt;
+ }
+
+ new_level = 100000;
+ full_volt = calc_full_volt((bat->volt_value / 1000),
+ (bat->cur_value / 1000), bat->pdata->internal_impedance);
+
+ if (full_volt < calc_full_volt(lut->volt, lut->cur,
+ bat->pdata->internal_impedance)) {
+ lut_size--;
+ while (lut_size--) {
+ int lut_volt1;
+ int lut_volt2;
+
+ lut_volt1 = calc_full_volt(lut[0].volt, lut[0].cur,
+ bat->pdata->internal_impedance);
+ lut_volt2 = calc_full_volt(lut[1].volt, lut[1].cur,
+ bat->pdata->internal_impedance);
+ if (full_volt < lut_volt1 && full_volt >= lut_volt2) {
+ new_level = (lut[1].level +
+ (lut[0].level - lut[1].level) *
+ (full_volt - lut_volt2) /
+ (lut_volt1 - lut_volt2)) * 1000;
+ break;
+ }
+ new_level = lut[1].level * 1000;
+ lut++;
+ }
+ }
+
+ bat->level = new_level;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (bat->pdata->gpio_charge_finished < 0)
+ val->intval = bat->level == 100000 ?
+ POWER_SUPPLY_STATUS_FULL : bat->status;
+ else
+ val->intval = bat->status;
+ return 0;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = 100000;
+ return 0;
+ case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
+ val->intval = 0;
+ return 0;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = bat->level;
+ return 0;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = bat->volt_value;
+ return 0;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = bat->cur_value;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct s3c_adc_bat main_bat = {
+ .psy = {
+ .name = "main-battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = s3c_adc_main_bat_props,
+ .num_properties = ARRAY_SIZE(s3c_adc_main_bat_props),
+ .get_property = s3c_adc_bat_get_property,
+ .external_power_changed = s3c_adc_bat_ext_power_changed,
+ .use_for_apm = 1,
+ },
+};
+
+static void s3c_adc_bat_work(struct work_struct *work)
+{
+ struct s3c_adc_bat *bat = &main_bat;
+ int is_charged;
+ int is_plugged;
+ static int was_plugged;
+
+ is_plugged = power_supply_am_i_supplied(&bat->psy);
+ bat->cable_plugged = is_plugged;
+ if (is_plugged != was_plugged) {
+ was_plugged = is_plugged;
+ if (is_plugged) {
+ if (bat->pdata->enable_charger)
+ bat->pdata->enable_charger();
+ bat->status = POWER_SUPPLY_STATUS_CHARGING;
+ } else {
+ if (bat->pdata->disable_charger)
+ bat->pdata->disable_charger();
+ bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
+ }
+ } else {
+ if ((bat->pdata->gpio_charge_finished >= 0) && is_plugged) {
+ is_charged = gpio_get_value(
+ main_bat.pdata->gpio_charge_finished);
+ if (is_charged) {
+ if (bat->pdata->disable_charger)
+ bat->pdata->disable_charger();
+ bat->status = POWER_SUPPLY_STATUS_FULL;
+ } else {
+ if (bat->pdata->enable_charger)
+ bat->pdata->enable_charger();
+ bat->status = POWER_SUPPLY_STATUS_CHARGING;
+ }
+ }
+ }
+
+ power_supply_changed(&bat->psy);
+}
+
+static irqreturn_t s3c_adc_bat_charged(int irq, void *dev_id)
+{
+ schedule_delayed_work(&bat_work,
+ msecs_to_jiffies(JITTER_DELAY));
+ return IRQ_HANDLED;
+}
+
+static int __init s3c_adc_bat_probe(struct platform_device *pdev)
+{
+ struct s3c_adc_client *client;
+ struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
+ int ret;
+
+ client = s3c_adc_register(pdev, NULL, NULL, 0);
+ if (IS_ERR(client)) {
+ dev_err(&pdev->dev, "cannot register adc\n");
+ return PTR_ERR(client);
+ }
+
+ platform_set_drvdata(pdev, client);
+
+ main_bat.client = client;
+ main_bat.pdata = pdata;
+ main_bat.volt_value = -1;
+ main_bat.cur_value = -1;
+ main_bat.cable_plugged = 0;
+ main_bat.status = POWER_SUPPLY_STATUS_DISCHARGING;
+
+ ret = power_supply_register(&pdev->dev, &main_bat.psy);
+ if (ret)
+ goto err_reg_main;
+ if (pdata->backup_volt_mult) {
+ backup_bat.client = client;
+ backup_bat.pdata = pdev->dev.platform_data;
+ backup_bat.volt_value = -1;
+ ret = power_supply_register(&pdev->dev, &backup_bat.psy);
+ if (ret)
+ goto err_reg_backup;
+ }
+
+ INIT_DELAYED_WORK(&bat_work, s3c_adc_bat_work);
+
+ if (pdata->gpio_charge_finished >= 0) {
+ ret = gpio_request(pdata->gpio_charge_finished, "charged");
+ if (ret)
+ goto err_gpio;
+
+ ret = request_irq(gpio_to_irq(pdata->gpio_charge_finished),
+ s3c_adc_bat_charged,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "battery charged", NULL);
+ if (ret)
+ goto err_irq;
+ }
+
+ if (pdata->init) {
+ ret = pdata->init();
+ if (ret)
+ goto err_platform;
+ }
+
+ dev_info(&pdev->dev, "successfully loaded\n");
+ device_init_wakeup(&pdev->dev, 1);
+
+ /* Schedule timer to check current status */
+ schedule_delayed_work(&bat_work,
+ msecs_to_jiffies(JITTER_DELAY));
+
+ return 0;
+
+err_platform:
+ if (pdata->gpio_charge_finished >= 0)
+ free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL);
+err_irq:
+ if (pdata->gpio_charge_finished >= 0)
+ gpio_free(pdata->gpio_charge_finished);
+err_gpio:
+ if (pdata->backup_volt_mult)
+ power_supply_unregister(&backup_bat.psy);
+err_reg_backup:
+ power_supply_unregister(&main_bat.psy);
+err_reg_main:
+ return ret;
+}
+
+static int s3c_adc_bat_remove(struct platform_device *pdev)
+{
+ struct s3c_adc_client *client = platform_get_drvdata(pdev);
+ struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
+
+ power_supply_unregister(&main_bat.psy);
+ if (pdata->backup_volt_mult)
+ power_supply_unregister(&backup_bat.psy);
+
+ s3c_adc_release(client);
+
+ if (pdata->gpio_charge_finished >= 0) {
+ free_irq(gpio_to_irq(pdata->gpio_charge_finished), NULL);
+ gpio_free(pdata->gpio_charge_finished);
+ }
+
+ cancel_delayed_work(&bat_work);
+
+ if (pdata->exit)
+ pdata->exit();
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c_adc_bat_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
+
+ if (pdata->gpio_charge_finished >= 0) {
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(
+ gpio_to_irq(pdata->gpio_charge_finished));
+ else {
+ disable_irq(gpio_to_irq(pdata->gpio_charge_finished));
+ main_bat.pdata->disable_charger();
+ }
+ }
+
+ return 0;
+}
+
+static int s3c_adc_bat_resume(struct platform_device *pdev)
+{
+ struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
+
+ if (pdata->gpio_charge_finished >= 0) {
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(
+ gpio_to_irq(pdata->gpio_charge_finished));
+ else
+ enable_irq(gpio_to_irq(pdata->gpio_charge_finished));
+ }
+
+ /* Schedule timer to check current status */
+ schedule_delayed_work(&bat_work,
+ msecs_to_jiffies(JITTER_DELAY));
+
+ return 0;
+}
+#else
+#define s3c_adc_battery_suspend NULL
+#define s3c_adc_battery_resume NULL
+#endif
+
+static struct platform_driver s3c_adc_bat_driver = {
+ .driver = {
+ .name = "s3c-adc-battery",
+ },
+ .probe = s3c_adc_bat_probe,
+ .remove = s3c_adc_bat_remove,
+ .suspend = s3c_adc_bat_suspend,
+ .resume = s3c_adc_bat_resume,
+};
+
+static int __init s3c_adc_bat_init(void)
+{
+ return platform_driver_register(&s3c_adc_bat_driver);
+}
+module_init(s3c_adc_bat_init);
+
+static void __exit s3c_adc_bat_exit(void)
+{
+ platform_driver_unregister(&s3c_adc_bat_driver);
+}
+module_exit(s3c_adc_bat_exit);
+
+MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
+MODULE_DESCRIPTION("iPAQ H1930/H1940/RX1950 battery controler driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c
index 4e8afce0c818..5071d85ec12d 100644
--- a/drivers/power/wm97xx_battery.c
+++ b/drivers/power/wm97xx_battery.c
@@ -29,7 +29,6 @@ static DEFINE_MUTEX(bat_lock);
static struct work_struct bat_work;
static struct mutex work_lock;
static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
-static struct wm97xx_batt_info *gpdata;
static enum power_supply_property *prop;
static unsigned long wm97xx_read_bat(struct power_supply *bat_ps)
@@ -172,12 +171,6 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev)
struct wm97xx_pdata *wmdata = dev->dev.platform_data;
struct wm97xx_batt_pdata *pdata;
- if (gpdata) {
- dev_err(&dev->dev, "Do not pass platform_data through "
- "wm97xx_bat_set_pdata!\n");
- return -EINVAL;
- }
-
if (!wmdata) {
dev_err(&dev->dev, "No platform data supplied\n");
return -EINVAL;
@@ -308,15 +301,6 @@ static void __exit wm97xx_bat_exit(void)
platform_driver_unregister(&wm97xx_bat_driver);
}
-/* The interface is deprecated, as well as linux/wm97xx_batt.h */
-void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data);
-
-void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data)
-{
- gpdata = data;
-}
-EXPORT_SYMBOL_GPL(wm97xx_bat_set_pdata);
-
module_init(wm97xx_bat_init);
module_exit(wm97xx_bat_exit);
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 04f2e085116a..172951bf23a4 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -100,6 +100,14 @@ config REGULATOR_MAX8925
help
Say y here to support the voltage regulaltor of Maxim MAX8925 PMIC.
+config REGULATOR_MAX8998
+ tristate "Maxim 8998 voltage regulator"
+ depends on MFD_MAX8998
+ help
+ This driver controls a Maxim 8998 voltage output regulator
+ via I2C bus. The provided regulator is suitable for S3C6410
+ and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages.
+
config REGULATOR_TWL4030
bool "TI TWL4030/TWL5030/TWL6030/TPS695x0 PMIC"
depends on TWL4030_CORE
@@ -201,5 +209,31 @@ config REGULATOR_88PM8607
help
This driver supports 88PM8607 voltage regulator chips.
+config REGULATOR_ISL6271A
+ tristate "Intersil ISL6271A Power regulator"
+ depends on I2C
+ help
+ This driver supports ISL6271A voltage regulator chip.
+
+config REGULATOR_AD5398
+ tristate "Analog Devices AD5398/AD5821 regulators"
+ depends on I2C
+ help
+ This driver supports AD5398 and AD5821 current regulator chips.
+ If building into module, its name is ad5398.ko.
+
+config REGULATOR_AB8500
+ bool "ST-Ericsson AB8500 Power Regulators"
+ depends on AB8500_CORE
+ help
+ This driver supports the regulators found on the ST-Ericsson mixed
+ signal AB8500 PMIC
+
+config REGULATOR_TPS6586X
+ tristate "TI TPS6586X Power regulators"
+ depends on MFD_TPS6586X
+ help
+ This driver supports TPS6586X voltage regulator chips.
+
endif
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 4e7feece22d5..8285fd832e16 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
+obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
obj-$(CONFIG_REGULATOR_DUMMY) += dummy.o
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
@@ -16,12 +17,14 @@ obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o
obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o
obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o
+obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o
+obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
@@ -31,5 +34,7 @@ obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o
obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
+obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
+obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o
ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
new file mode 100644
index 000000000000..dc3f1a491675
--- /dev/null
+++ b/drivers/regulator/ab8500.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ *
+ * AB8500 peripheral regulators
+ *
+ * AB8500 supports the following regulators,
+ * LDOs - VAUDIO, VANAMIC2/2, VDIGMIC, VINTCORE12, VTVOUT,
+ * VAUX1/2/3, VANA
+ *
+ * for DB8500 cut 1.0 and previous versions of the silicon, all accesses
+ * to registers are through the DB8500 SPI. In cut 1.1 onwards, these
+ * accesses are through the DB8500 PRCMU I2C
+ *
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/ab8500.h>
+
+/**
+ * struct ab8500_regulator_info - ab8500 regulator information
+ * @desc: regulator description
+ * @ab8500: ab8500 parent
+ * @regulator_dev: regulator device
+ * @max_uV: maximum voltage (for variable voltage supplies)
+ * @min_uV: minimum voltage (for variable voltage supplies)
+ * @fixed_uV: typical voltage (for fixed voltage supplies)
+ * @update_reg: register to control on/off
+ * @mask: mask to enable/disable regulator
+ * @enable: bits to enable the regulator in normal(high power) mode
+ * @voltage_reg: register to control regulator voltage
+ * @voltage_mask: mask to control regulator voltage
+ * @supported_voltages: supported voltage table
+ * @voltages_len: number of supported voltages for the regulator
+ */
+struct ab8500_regulator_info {
+ struct device *dev;
+ struct regulator_desc desc;
+ struct ab8500 *ab8500;
+ struct regulator_dev *regulator;
+ int max_uV;
+ int min_uV;
+ int fixed_uV;
+ int update_reg;
+ int mask;
+ int enable;
+ int voltage_reg;
+ int voltage_mask;
+ int const *supported_voltages;
+ int voltages_len;
+};
+
+/* voltage tables for the vauxn/vintcore supplies */
+static const int ldo_vauxn_voltages[] = {
+ 1100000,
+ 1200000,
+ 1300000,
+ 1400000,
+ 1500000,
+ 1800000,
+ 1850000,
+ 1900000,
+ 2500000,
+ 2650000,
+ 2700000,
+ 2750000,
+ 2800000,
+ 2900000,
+ 3000000,
+ 3300000,
+};
+
+static const int ldo_vintcore_voltages[] = {
+ 1200000,
+ 1225000,
+ 1250000,
+ 1275000,
+ 1300000,
+ 1325000,
+ 1350000,
+};
+
+static int ab8500_regulator_enable(struct regulator_dev *rdev)
+{
+ int regulator_id, ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ regulator_id = rdev_get_id(rdev);
+ if (regulator_id >= AB8500_NUM_REGULATORS)
+ return -EINVAL;
+
+ ret = ab8500_set_bits(info->ab8500, info->update_reg,
+ info->mask, info->enable);
+ if (ret < 0)
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set enable bits for regulator\n");
+ return ret;
+}
+
+static int ab8500_regulator_disable(struct regulator_dev *rdev)
+{
+ int regulator_id, ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ regulator_id = rdev_get_id(rdev);
+ if (regulator_id >= AB8500_NUM_REGULATORS)
+ return -EINVAL;
+
+ ret = ab8500_set_bits(info->ab8500, info->update_reg,
+ info->mask, 0x0);
+ if (ret < 0)
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set disable bits for regulator\n");
+ return ret;
+}
+
+static int ab8500_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ int regulator_id, ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ regulator_id = rdev_get_id(rdev);
+ if (regulator_id >= AB8500_NUM_REGULATORS)
+ return -EINVAL;
+
+ ret = ab8500_read(info->ab8500, info->update_reg);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't read 0x%x register\n", info->update_reg);
+ return ret;
+ }
+
+ if (ret & info->mask)
+ return true;
+ else
+ return false;
+}
+
+static int ab8500_list_voltage(struct regulator_dev *rdev, unsigned selector)
+{
+ int regulator_id;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ regulator_id = rdev_get_id(rdev);
+ if (regulator_id >= AB8500_NUM_REGULATORS)
+ return -EINVAL;
+
+ /* return the uV for the fixed regulators */
+ if (info->fixed_uV)
+ return info->fixed_uV;
+
+ if (selector > info->voltages_len)
+ return -EINVAL;
+
+ return info->supported_voltages[selector];
+}
+
+static int ab8500_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ int regulator_id, ret, val;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ regulator_id = rdev_get_id(rdev);
+ if (regulator_id >= AB8500_NUM_REGULATORS)
+ return -EINVAL;
+
+ ret = ab8500_read(info->ab8500, info->voltage_reg);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't read voltage reg for regulator\n");
+ return ret;
+ }
+
+ /* vintcore has a different layout */
+ val = ret & info->voltage_mask;
+ if (regulator_id == AB8500_LDO_INTCORE)
+ ret = info->supported_voltages[val >> 0x3];
+ else
+ ret = info->supported_voltages[val];
+
+ return ret;
+}
+
+static int ab8500_get_best_voltage_index(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+ int i;
+
+ /* check the supported voltage */
+ for (i = 0; i < info->voltages_len; i++) {
+ if ((info->supported_voltages[i] >= min_uV) &&
+ (info->supported_voltages[i] <= max_uV))
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int ab8500_regulator_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ int regulator_id, ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ regulator_id = rdev_get_id(rdev);
+ if (regulator_id >= AB8500_NUM_REGULATORS)
+ return -EINVAL;
+
+ /* get the appropriate voltages within the range */
+ ret = ab8500_get_best_voltage_index(rdev, min_uV, max_uV);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't get best voltage for regulator\n");
+ return ret;
+ }
+
+ /* set the registers for the request */
+ ret = ab8500_set_bits(info->ab8500, info->voltage_reg,
+ info->voltage_mask, ret);
+ if (ret < 0)
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set voltage reg for regulator\n");
+
+ return ret;
+}
+
+static struct regulator_ops ab8500_regulator_ops = {
+ .enable = ab8500_regulator_enable,
+ .disable = ab8500_regulator_disable,
+ .is_enabled = ab8500_regulator_is_enabled,
+ .get_voltage = ab8500_regulator_get_voltage,
+ .set_voltage = ab8500_regulator_set_voltage,
+ .list_voltage = ab8500_list_voltage,
+};
+
+static int ab8500_fixed_get_voltage(struct regulator_dev *rdev)
+{
+ int regulator_id;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ regulator_id = rdev_get_id(rdev);
+ if (regulator_id >= AB8500_NUM_REGULATORS)
+ return -EINVAL;
+
+ return info->fixed_uV;
+}
+
+static struct regulator_ops ab8500_ldo_fixed_ops = {
+ .enable = ab8500_regulator_enable,
+ .disable = ab8500_regulator_disable,
+ .is_enabled = ab8500_regulator_is_enabled,
+ .get_voltage = ab8500_fixed_get_voltage,
+ .list_voltage = ab8500_list_voltage,
+};
+
+#define AB8500_LDO(_id, min, max, reg, reg_mask, reg_enable, \
+ volt_reg, volt_mask, voltages, \
+ len_volts) \
+{ \
+ .desc = { \
+ .name = "LDO-" #_id, \
+ .ops = &ab8500_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = AB8500_LDO_##_id, \
+ .owner = THIS_MODULE, \
+ }, \
+ .min_uV = (min) * 1000, \
+ .max_uV = (max) * 1000, \
+ .update_reg = reg, \
+ .mask = reg_mask, \
+ .enable = reg_enable, \
+ .voltage_reg = volt_reg, \
+ .voltage_mask = volt_mask, \
+ .supported_voltages = voltages, \
+ .voltages_len = len_volts, \
+ .fixed_uV = 0, \
+}
+
+#define AB8500_FIXED_LDO(_id, fixed, reg, reg_mask, \
+ reg_enable) \
+{ \
+ .desc = { \
+ .name = "LDO-" #_id, \
+ .ops = &ab8500_ldo_fixed_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = AB8500_LDO_##_id, \
+ .owner = THIS_MODULE, \
+ }, \
+ .fixed_uV = fixed * 1000, \
+ .update_reg = reg, \
+ .mask = reg_mask, \
+ .enable = reg_enable, \
+}
+
+static struct ab8500_regulator_info ab8500_regulator_info[] = {
+ /*
+ * Variable Voltage LDOs
+ * name, min uV, max uV, ctrl reg, reg mask, enable mask,
+ * volt ctrl reg, volt ctrl mask, volt table, num supported volts
+ */
+ AB8500_LDO(AUX1, 1100, 3300, 0x0409, 0x3, 0x1, 0x041f, 0xf,
+ ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)),
+ AB8500_LDO(AUX2, 1100, 3300, 0x0409, 0xc, 0x4, 0x0420, 0xf,
+ ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)),
+ AB8500_LDO(AUX3, 1100, 3300, 0x040a, 0x3, 0x1, 0x0421, 0xf,
+ ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)),
+ AB8500_LDO(INTCORE, 1100, 3300, 0x0380, 0x4, 0x4, 0x0380, 0x38,
+ ldo_vintcore_voltages, ARRAY_SIZE(ldo_vintcore_voltages)),
+
+ /*
+ * Fixed Voltage LDOs
+ * name, o/p uV, ctrl reg, enable, disable
+ */
+ AB8500_FIXED_LDO(TVOUT, 2000, 0x0380, 0x2, 0x2),
+ AB8500_FIXED_LDO(AUDIO, 2000, 0x0383, 0x2, 0x2),
+ AB8500_FIXED_LDO(ANAMIC1, 2050, 0x0383, 0x4, 0x4),
+ AB8500_FIXED_LDO(ANAMIC2, 2050, 0x0383, 0x8, 0x8),
+ AB8500_FIXED_LDO(DMIC, 1800, 0x0383, 0x10, 0x10),
+ AB8500_FIXED_LDO(ANA, 1200, 0x0383, 0xc, 0x4),
+};
+
+static inline struct ab8500_regulator_info *find_regulator_info(int id)
+{
+ struct ab8500_regulator_info *info;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
+ info = &ab8500_regulator_info[i];
+ if (info->desc.id == id)
+ return info;
+ }
+ return NULL;
+}
+
+static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
+{
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_platform_data *pdata = dev_get_platdata(ab8500->dev);
+ int i, err;
+
+ if (!ab8500) {
+ dev_err(&pdev->dev, "null mfd parent\n");
+ return -EINVAL;
+ }
+
+ /* register all regulators */
+ for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
+ struct ab8500_regulator_info *info = NULL;
+
+ /* assign per-regulator data */
+ info = &ab8500_regulator_info[i];
+ info->dev = &pdev->dev;
+ info->ab8500 = ab8500;
+
+ info->regulator = regulator_register(&info->desc, &pdev->dev,
+ pdata->regulator[i], info);
+ if (IS_ERR(info->regulator)) {
+ err = PTR_ERR(info->regulator);
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ info->desc.name);
+ /* when we fail, un-register all earlier regulators */
+ i--;
+ while (i > 0) {
+ info = &ab8500_regulator_info[i];
+ regulator_unregister(info->regulator);
+ i--;
+ }
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static __devexit int ab8500_regulator_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
+ struct ab8500_regulator_info *info = NULL;
+ info = &ab8500_regulator_info[i];
+ regulator_unregister(info->regulator);
+ }
+
+ return 0;
+}
+
+static struct platform_driver ab8500_regulator_driver = {
+ .probe = ab8500_regulator_probe,
+ .remove = __devexit_p(ab8500_regulator_remove),
+ .driver = {
+ .name = "ab8500-regulator",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ab8500_regulator_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&ab8500_regulator_driver);
+ if (ret != 0)
+ pr_err("Failed to register ab8500 regulator: %d\n", ret);
+
+ return ret;
+}
+subsys_initcall(ab8500_regulator_init);
+
+static void __exit ab8500_regulator_exit(void)
+{
+ platform_driver_unregister(&ab8500_regulator_driver);
+}
+module_exit(ab8500_regulator_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sundar Iyer <sundar.iyer@stericsson.com>");
+MODULE_DESCRIPTION("Regulator Driver for ST-Ericsson AB8500 Mixed-Sig PMIC");
+MODULE_ALIAS("platform:ab8500-regulator");
diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c
new file mode 100644
index 000000000000..d59d2f2314af
--- /dev/null
+++ b/drivers/regulator/ad5398.c
@@ -0,0 +1,288 @@
+/*
+ * Voltage and current regulation for AD5398 and AD5821
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#define AD5398_CURRENT_EN_MASK 0x8000
+
+struct ad5398_chip_info {
+ struct i2c_client *client;
+ int min_uA;
+ int max_uA;
+ unsigned int current_level;
+ unsigned int current_mask;
+ unsigned int current_offset;
+ struct regulator_dev rdev;
+};
+
+static int ad5398_calc_current(struct ad5398_chip_info *chip,
+ unsigned selector)
+{
+ unsigned range_uA = chip->max_uA - chip->min_uA;
+
+ return chip->min_uA + (selector * range_uA / chip->current_level);
+}
+
+static int ad5398_read_reg(struct i2c_client *client, unsigned short *data)
+{
+ unsigned short val;
+ int ret;
+
+ ret = i2c_master_recv(client, (char *)&val, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "I2C read error\n");
+ return ret;
+ }
+ *data = be16_to_cpu(val);
+
+ return ret;
+}
+
+static int ad5398_write_reg(struct i2c_client *client, const unsigned short data)
+{
+ unsigned short val;
+ int ret;
+
+ val = cpu_to_be16(data);
+ ret = i2c_master_send(client, (char *)&val, 2);
+ if (ret < 0)
+ dev_err(&client->dev, "I2C write error\n");
+
+ return ret;
+}
+
+static int ad5398_get_current_limit(struct regulator_dev *rdev)
+{
+ struct ad5398_chip_info *chip = rdev_get_drvdata(rdev);
+ struct i2c_client *client = chip->client;
+ unsigned short data;
+ int ret;
+
+ ret = ad5398_read_reg(client, &data);
+ if (ret < 0)
+ return ret;
+
+ ret = (data & chip->current_mask) >> chip->current_offset;
+
+ return ad5398_calc_current(chip, ret);
+}
+
+static int ad5398_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA)
+{
+ struct ad5398_chip_info *chip = rdev_get_drvdata(rdev);
+ struct i2c_client *client = chip->client;
+ unsigned range_uA = chip->max_uA - chip->min_uA;
+ unsigned selector;
+ unsigned short data;
+ int ret;
+
+ if (min_uA > chip->max_uA || min_uA < chip->min_uA)
+ return -EINVAL;
+ if (max_uA > chip->max_uA || max_uA < chip->min_uA)
+ return -EINVAL;
+
+ selector = ((min_uA - chip->min_uA) * chip->current_level +
+ range_uA - 1) / range_uA;
+ if (ad5398_calc_current(chip, selector) > max_uA)
+ return -EINVAL;
+
+ dev_dbg(&client->dev, "changing current %dmA\n",
+ ad5398_calc_current(chip, selector) / 1000);
+
+ /* read chip enable bit */
+ ret = ad5398_read_reg(client, &data);
+ if (ret < 0)
+ return ret;
+
+ /* prepare register data */
+ selector = (selector << chip->current_offset) & chip->current_mask;
+ data = (unsigned short)selector | (data & AD5398_CURRENT_EN_MASK);
+
+ /* write the new current value back as well as enable bit */
+ ret = ad5398_write_reg(client, data);
+
+ return ret;
+}
+
+static int ad5398_is_enabled(struct regulator_dev *rdev)
+{
+ struct ad5398_chip_info *chip = rdev_get_drvdata(rdev);
+ struct i2c_client *client = chip->client;
+ unsigned short data;
+ int ret;
+
+ ret = ad5398_read_reg(client, &data);
+ if (ret < 0)
+ return ret;
+
+ if (data & AD5398_CURRENT_EN_MASK)
+ return 1;
+ else
+ return 0;
+}
+
+static int ad5398_enable(struct regulator_dev *rdev)
+{
+ struct ad5398_chip_info *chip = rdev_get_drvdata(rdev);
+ struct i2c_client *client = chip->client;
+ unsigned short data;
+ int ret;
+
+ ret = ad5398_read_reg(client, &data);
+ if (ret < 0)
+ return ret;
+
+ if (data & AD5398_CURRENT_EN_MASK)
+ return 0;
+
+ data |= AD5398_CURRENT_EN_MASK;
+
+ ret = ad5398_write_reg(client, data);
+
+ return ret;
+}
+
+static int ad5398_disable(struct regulator_dev *rdev)
+{
+ struct ad5398_chip_info *chip = rdev_get_drvdata(rdev);
+ struct i2c_client *client = chip->client;
+ unsigned short data;
+ int ret;
+
+ ret = ad5398_read_reg(client, &data);
+ if (ret < 0)
+ return ret;
+
+ if (!(data & AD5398_CURRENT_EN_MASK))
+ return 0;
+
+ data &= ~AD5398_CURRENT_EN_MASK;
+
+ ret = ad5398_write_reg(client, data);
+
+ return ret;
+}
+
+static struct regulator_ops ad5398_ops = {
+ .get_current_limit = ad5398_get_current_limit,
+ .set_current_limit = ad5398_set_current_limit,
+ .enable = ad5398_enable,
+ .disable = ad5398_disable,
+ .is_enabled = ad5398_is_enabled,
+};
+
+static struct regulator_desc ad5398_reg = {
+ .name = "isink",
+ .id = 0,
+ .ops = &ad5398_ops,
+ .type = REGULATOR_CURRENT,
+ .owner = THIS_MODULE,
+};
+
+struct ad5398_current_data_format {
+ int current_bits;
+ int current_offset;
+ int min_uA;
+ int max_uA;
+};
+
+static const struct ad5398_current_data_format df_10_4_120 = {10, 4, 0, 120000};
+
+static const struct i2c_device_id ad5398_id[] = {
+ { "ad5398", (kernel_ulong_t)&df_10_4_120 },
+ { "ad5821", (kernel_ulong_t)&df_10_4_120 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ad5398_id);
+
+static int __devinit ad5398_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct regulator_dev *rdev;
+ struct regulator_init_data *init_data = client->dev.platform_data;
+ struct ad5398_chip_info *chip;
+ const struct ad5398_current_data_format *df =
+ (struct ad5398_current_data_format *)id->driver_data;
+ int ret;
+
+ if (!init_data)
+ return -EINVAL;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->client = client;
+
+ chip->min_uA = df->min_uA;
+ chip->max_uA = df->max_uA;
+ chip->current_level = 1 << df->current_bits;
+ chip->current_offset = df->current_offset;
+ chip->current_mask = (chip->current_level - 1) << chip->current_offset;
+
+ rdev = regulator_register(&ad5398_reg, &client->dev, init_data, chip);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(&client->dev, "failed to register %s %s\n",
+ id->name, ad5398_reg.name);
+ goto err;
+ }
+
+ i2c_set_clientdata(client, chip);
+ dev_dbg(&client->dev, "%s regulator driver is registered.\n", id->name);
+ return 0;
+
+err:
+ kfree(chip);
+ return ret;
+}
+
+static int __devexit ad5398_remove(struct i2c_client *client)
+{
+ struct ad5398_chip_info *chip = i2c_get_clientdata(client);
+
+ regulator_unregister(&chip->rdev);
+ kfree(chip);
+ i2c_set_clientdata(client, NULL);
+
+ return 0;
+}
+
+static struct i2c_driver ad5398_driver = {
+ .probe = ad5398_probe,
+ .remove = __devexit_p(ad5398_remove),
+ .driver = {
+ .name = "ad5398",
+ },
+ .id_table = ad5398_id,
+};
+
+static int __init ad5398_init(void)
+{
+ return i2c_add_driver(&ad5398_driver);
+}
+subsys_initcall(ad5398_init);
+
+static void __exit ad5398_exit(void)
+{
+ i2c_del_driver(&ad5398_driver);
+}
+module_exit(ad5398_exit);
+
+MODULE_DESCRIPTION("AD5398 and AD5821 current regulator driver");
+MODULE_AUTHOR("Sonic Zhang");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("i2c:ad5398-regulator");
diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c
new file mode 100644
index 000000000000..e49d2bd393f2
--- /dev/null
+++ b/drivers/regulator/isl6271a-regulator.c
@@ -0,0 +1,236 @@
+/*
+ * isl6271a-regulator.c
+ *
+ * Support for Intersil ISL6271A voltage regulator
+ *
+ * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#define ISL6271A_VOLTAGE_MIN 850000
+#define ISL6271A_VOLTAGE_MAX 1600000
+#define ISL6271A_VOLTAGE_STEP 50000
+
+/* PMIC details */
+struct isl_pmic {
+ struct i2c_client *client;
+ struct regulator_dev *rdev[3];
+ struct mutex mtx;
+};
+
+static int isl6271a_get_voltage(struct regulator_dev *dev)
+{
+ struct isl_pmic *pmic = rdev_get_drvdata(dev);
+ int idx, data;
+
+ mutex_lock(&pmic->mtx);
+
+ idx = i2c_smbus_read_byte(pmic->client);
+ if (idx < 0) {
+ dev_err(&pmic->client->dev, "Error getting voltage\n");
+ data = idx;
+ goto out;
+ }
+
+ /* Convert the data from chip to microvolts */
+ data = ISL6271A_VOLTAGE_MIN + (ISL6271A_VOLTAGE_STEP * (idx & 0xf));
+
+out:
+ mutex_unlock(&pmic->mtx);
+ return data;
+}
+
+static int isl6271a_set_voltage(struct regulator_dev *dev, int minuV, int maxuV)
+{
+ struct isl_pmic *pmic = rdev_get_drvdata(dev);
+ int vsel, err, data;
+
+ if (minuV < ISL6271A_VOLTAGE_MIN || minuV > ISL6271A_VOLTAGE_MAX)
+ return -EINVAL;
+ if (maxuV < ISL6271A_VOLTAGE_MIN || maxuV > ISL6271A_VOLTAGE_MAX)
+ return -EINVAL;
+
+ /* Align to 50000 mV */
+ vsel = minuV - (minuV % ISL6271A_VOLTAGE_STEP);
+
+ /* If the result fell out of [minuV,maxuV] range, put it back */
+ if (vsel < minuV)
+ vsel += ISL6271A_VOLTAGE_STEP;
+
+ /* Convert the microvolts to data for the chip */
+ data = (vsel - ISL6271A_VOLTAGE_MIN) / ISL6271A_VOLTAGE_STEP;
+
+ mutex_lock(&pmic->mtx);
+
+ err = i2c_smbus_write_byte(pmic->client, data);
+ if (err < 0)
+ dev_err(&pmic->client->dev, "Error setting voltage\n");
+
+ mutex_unlock(&pmic->mtx);
+ return err;
+}
+
+static int isl6271a_list_voltage(struct regulator_dev *dev, unsigned selector)
+{
+ return ISL6271A_VOLTAGE_MIN + (ISL6271A_VOLTAGE_STEP * selector);
+}
+
+static struct regulator_ops isl_core_ops = {
+ .get_voltage = isl6271a_get_voltage,
+ .set_voltage = isl6271a_set_voltage,
+ .list_voltage = isl6271a_list_voltage,
+};
+
+static int isl6271a_get_fixed_voltage(struct regulator_dev *dev)
+{
+ int id = rdev_get_id(dev);
+ return (id == 1) ? 1100000 : 1300000;
+}
+
+static int isl6271a_list_fixed_voltage(struct regulator_dev *dev, unsigned selector)
+{
+ int id = rdev_get_id(dev);
+ return (id == 1) ? 1100000 : 1300000;
+}
+
+static struct regulator_ops isl_fixed_ops = {
+ .get_voltage = isl6271a_get_fixed_voltage,
+ .list_voltage = isl6271a_list_fixed_voltage,
+};
+
+static struct regulator_desc isl_rd[] = {
+ {
+ .name = "Core Buck",
+ .id = 0,
+ .n_voltages = 16,
+ .ops = &isl_core_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO1",
+ .id = 1,
+ .n_voltages = 1,
+ .ops = &isl_fixed_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO2",
+ .id = 2,
+ .n_voltages = 1,
+ .ops = &isl_fixed_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __devinit isl6271a_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regulator_init_data *init_data = i2c->dev.platform_data;
+ struct isl_pmic *pmic;
+ int err, i;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ if (!init_data) {
+ dev_err(&i2c->dev, "no platform data supplied\n");
+ return -EIO;
+ }
+
+ pmic = kzalloc(sizeof(struct isl_pmic), GFP_KERNEL);
+ if (!pmic)
+ return -ENOMEM;
+
+ pmic->client = i2c;
+
+ mutex_init(&pmic->mtx);
+
+ for (i = 0; i < 3; i++) {
+ pmic->rdev[i] = regulator_register(&isl_rd[0], &i2c->dev,
+ init_data, pmic);
+ if (IS_ERR(pmic->rdev[i])) {
+ dev_err(&i2c->dev, "failed to register %s\n", id->name);
+ err = PTR_ERR(pmic->rdev);
+ goto error;
+ }
+ }
+
+ i2c_set_clientdata(i2c, pmic);
+
+ return 0;
+
+error:
+ while (--i >= 0)
+ regulator_unregister(pmic->rdev[i]);
+
+ kfree(pmic);
+ return err;
+}
+
+static int __devexit isl6271a_remove(struct i2c_client *i2c)
+{
+ struct isl_pmic *pmic = i2c_get_clientdata(i2c);
+ int i;
+
+ i2c_set_clientdata(i2c, NULL);
+
+ for (i = 0; i < 3; i++)
+ regulator_unregister(pmic->rdev[i]);
+
+ kfree(pmic);
+
+ return 0;
+}
+
+static const struct i2c_device_id isl6271a_id[] = {
+ {.name = "isl6271a", 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, isl6271a_id);
+
+static struct i2c_driver isl6271a_i2c_driver = {
+ .driver = {
+ .name = "isl6271a",
+ .owner = THIS_MODULE,
+ },
+ .probe = isl6271a_probe,
+ .remove = __devexit_p(isl6271a_remove),
+ .id_table = isl6271a_id,
+};
+
+static int __init isl6271a_init(void)
+{
+ return i2c_add_driver(&isl6271a_i2c_driver);
+}
+
+static void __exit isl6271a_cleanup(void)
+{
+ i2c_del_driver(&isl6271a_i2c_driver);
+}
+
+subsys_initcall(isl6271a_init);
+module_exit(isl6271a_cleanup);
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("Intersil ISL6271A voltage regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c
index 8ae3732eb24b..3bb82b624e19 100644
--- a/drivers/regulator/lp3971.c
+++ b/drivers/regulator/lp3971.c
@@ -377,7 +377,7 @@ static int lp3971_i2c_read(struct i2c_client *i2c, char reg, int count,
if (count != 1)
return -EIO;
ret = i2c_smbus_read_byte_data(i2c, reg);
- if (ret < 0 || count != 1)
+ if (ret < 0)
return -EIO;
*dest = ret;
@@ -387,15 +387,9 @@ static int lp3971_i2c_read(struct i2c_client *i2c, char reg, int count,
static int lp3971_i2c_write(struct i2c_client *i2c, char reg, int count,
const u16 *src)
{
- int ret;
-
if (count != 1)
return -EIO;
- ret = i2c_smbus_write_byte_data(i2c, reg, *src);
- if (ret >= 0)
- return 0;
-
- return ret;
+ return i2c_smbus_write_byte_data(i2c, reg, *src);
}
static u8 lp3971_reg_read(struct lp3971 *lp3971, u8 reg)
diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c
index 2b54d9d75f11..8867c2710a6d 100644
--- a/drivers/regulator/max1586.c
+++ b/drivers/regulator/max1586.c
@@ -223,7 +223,7 @@ static int __devinit max1586_pmic_probe(struct i2c_client *client,
}
}
- i2c_set_clientdata(client, rdev);
+ i2c_set_clientdata(client, max1586);
dev_info(&client->dev, "Maxim 1586 regulator driver loaded\n");
return 0;
@@ -238,13 +238,13 @@ out:
static int __devexit max1586_pmic_remove(struct i2c_client *client)
{
- struct regulator_dev **rdev = i2c_get_clientdata(client);
+ struct max1586_data *max1586 = i2c_get_clientdata(client);
int i;
for (i = 0; i <= MAX1586_V6; i++)
- if (rdev[i])
- regulator_unregister(rdev[i]);
- kfree(rdev);
+ if (max1586->rdev[i])
+ regulator_unregister(max1586->rdev[i]);
+ kfree(max1586);
return 0;
}
diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c
index d97220efae5a..c570e6eb0db2 100644
--- a/drivers/regulator/max8660.c
+++ b/drivers/regulator/max8660.c
@@ -450,7 +450,7 @@ static int __devinit max8660_probe(struct i2c_client *client,
}
}
- i2c_set_clientdata(client, rdev);
+ i2c_set_clientdata(client, max8660);
dev_info(&client->dev, "Maxim 8660/8661 regulator driver loaded\n");
return 0;
@@ -465,13 +465,13 @@ out:
static int __devexit max8660_remove(struct i2c_client *client)
{
- struct regulator_dev **rdev = i2c_get_clientdata(client);
+ struct max8660 *max8660 = i2c_get_clientdata(client);
int i;
for (i = 0; i < MAX8660_V_END; i++)
- if (rdev[i])
- regulator_unregister(rdev[i]);
- kfree(rdev);
+ if (max8660->rdev[i])
+ regulator_unregister(max8660->rdev[i]);
+ kfree(max8660);
return 0;
}
diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c
new file mode 100644
index 000000000000..ab67298799f9
--- /dev/null
+++ b/drivers/regulator/max8998.c
@@ -0,0 +1,635 @@
+/*
+ * max8998.c - Voltage regulator driver for the Maxim 8998
+ *
+ * Copyright (C) 2009-2010 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/max8998.h>
+#include <linux/mfd/max8998-private.h>
+
+struct max8998_data {
+ struct device *dev;
+ struct max8998_dev *iodev;
+ int num_regulators;
+ struct regulator_dev **rdev;
+};
+
+struct voltage_map_desc {
+ int min;
+ int max;
+ int step;
+};
+
+/* Voltage maps */
+static const struct voltage_map_desc ldo23_voltage_map_desc = {
+ .min = 800, .step = 50, .max = 1300,
+};
+static const struct voltage_map_desc ldo456711_voltage_map_desc = {
+ .min = 1600, .step = 100, .max = 3600,
+};
+static const struct voltage_map_desc ldo8_voltage_map_desc = {
+ .min = 3000, .step = 100, .max = 3600,
+};
+static const struct voltage_map_desc ldo9_voltage_map_desc = {
+ .min = 2800, .step = 100, .max = 3100,
+};
+static const struct voltage_map_desc ldo10_voltage_map_desc = {
+ .min = 950, .step = 50, .max = 1300,
+};
+static const struct voltage_map_desc ldo1213_voltage_map_desc = {
+ .min = 800, .step = 100, .max = 3300,
+};
+static const struct voltage_map_desc ldo1415_voltage_map_desc = {
+ .min = 1200, .step = 100, .max = 3300,
+};
+static const struct voltage_map_desc ldo1617_voltage_map_desc = {
+ .min = 1600, .step = 100, .max = 3600,
+};
+static const struct voltage_map_desc buck12_voltage_map_desc = {
+ .min = 750, .step = 25, .max = 1525,
+};
+static const struct voltage_map_desc buck3_voltage_map_desc = {
+ .min = 1600, .step = 100, .max = 3600,
+};
+static const struct voltage_map_desc buck4_voltage_map_desc = {
+ .min = 800, .step = 100, .max = 2300,
+};
+
+static const struct voltage_map_desc *ldo_voltage_map[] = {
+ NULL,
+ NULL,
+ &ldo23_voltage_map_desc, /* LDO2 */
+ &ldo23_voltage_map_desc, /* LDO3 */
+ &ldo456711_voltage_map_desc, /* LDO4 */
+ &ldo456711_voltage_map_desc, /* LDO5 */
+ &ldo456711_voltage_map_desc, /* LDO6 */
+ &ldo456711_voltage_map_desc, /* LDO7 */
+ &ldo8_voltage_map_desc, /* LDO8 */
+ &ldo9_voltage_map_desc, /* LDO9 */
+ &ldo10_voltage_map_desc, /* LDO10 */
+ &ldo456711_voltage_map_desc, /* LDO11 */
+ &ldo1213_voltage_map_desc, /* LDO12 */
+ &ldo1213_voltage_map_desc, /* LDO13 */
+ &ldo1415_voltage_map_desc, /* LDO14 */
+ &ldo1415_voltage_map_desc, /* LDO15 */
+ &ldo1617_voltage_map_desc, /* LDO16 */
+ &ldo1617_voltage_map_desc, /* LDO17 */
+ &buck12_voltage_map_desc, /* BUCK1 */
+ &buck12_voltage_map_desc, /* BUCK2 */
+ &buck3_voltage_map_desc, /* BUCK3 */
+ &buck4_voltage_map_desc, /* BUCK4 */
+};
+
+static inline int max8998_get_ldo(struct regulator_dev *rdev)
+{
+ return rdev_get_id(rdev);
+}
+
+static int max8998_list_voltage(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ const struct voltage_map_desc *desc;
+ int ldo = max8998_get_ldo(rdev);
+ int val;
+
+ if (ldo >= ARRAY_SIZE(ldo_voltage_map))
+ return -EINVAL;
+
+ desc = ldo_voltage_map[ldo];
+ if (desc == NULL)
+ return -EINVAL;
+
+ val = desc->min + desc->step * selector;
+ if (val > desc->max)
+ return -EINVAL;
+
+ return val * 1000;
+}
+
+static int max8998_get_enable_register(struct regulator_dev *rdev,
+ int *reg, int *shift)
+{
+ int ldo = max8998_get_ldo(rdev);
+
+ switch (ldo) {
+ case MAX8998_LDO2 ... MAX8998_LDO5:
+ *reg = MAX8998_REG_ONOFF1;
+ *shift = 3 - (ldo - MAX8998_LDO2);
+ break;
+ case MAX8998_LDO6 ... MAX8998_LDO13:
+ *reg = MAX8998_REG_ONOFF2;
+ *shift = 7 - (ldo - MAX8998_LDO6);
+ break;
+ case MAX8998_LDO14 ... MAX8998_LDO17:
+ *reg = MAX8998_REG_ONOFF3;
+ *shift = 7 - (ldo - MAX8998_LDO14);
+ break;
+ case MAX8998_BUCK1 ... MAX8998_BUCK4:
+ *reg = MAX8998_REG_ONOFF1;
+ *shift = 7 - (ldo - MAX8998_BUCK1);
+ break;
+ case MAX8998_EN32KHZ_AP ... MAX8998_ENVICHG:
+ *reg = MAX8998_REG_ONOFF4;
+ *shift = 7 - (ldo - MAX8998_EN32KHZ_AP);
+ break;
+ case MAX8998_ESAFEOUT1 ... MAX8998_ESAFEOUT2:
+ *reg = MAX8998_REG_CHGR2;
+ *shift = 7 - (ldo - MAX8998_ESAFEOUT1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int max8998_ldo_is_enabled(struct regulator_dev *rdev)
+{
+ struct max8998_data *max8998 = rdev_get_drvdata(rdev);
+ int ret, reg, shift = 8;
+ u8 val;
+
+ ret = max8998_get_enable_register(rdev, &reg, &shift);
+ if (ret)
+ return ret;
+
+ ret = max8998_read_reg(max8998->iodev, reg, &val);
+ if (ret)
+ return ret;
+
+ return val & (1 << shift);
+}
+
+static int max8998_ldo_enable(struct regulator_dev *rdev)
+{
+ struct max8998_data *max8998 = rdev_get_drvdata(rdev);
+ int reg, shift = 8, ret;
+
+ ret = max8998_get_enable_register(rdev, &reg, &shift);
+ if (ret)
+ return ret;
+
+ return max8998_update_reg(max8998->iodev, reg, 1<<shift, 1<<shift);
+}
+
+static int max8998_ldo_disable(struct regulator_dev *rdev)
+{
+ struct max8998_data *max8998 = rdev_get_drvdata(rdev);
+ int reg, shift = 8, ret;
+
+ ret = max8998_get_enable_register(rdev, &reg, &shift);
+ if (ret)
+ return ret;
+
+ return max8998_update_reg(max8998->iodev, reg, 0, 1<<shift);
+}
+
+static int max8998_get_voltage_register(struct regulator_dev *rdev,
+ int *_reg, int *_shift, int *_mask)
+{
+ int ldo = max8998_get_ldo(rdev);
+ int reg, shift = 0, mask = 0xff;
+
+ switch (ldo) {
+ case MAX8998_LDO2 ... MAX8998_LDO3:
+ reg = MAX8998_REG_LDO2_LDO3;
+ mask = 0xf;
+ if (ldo == MAX8998_LDO2)
+ shift = 4;
+ else
+ shift = 0;
+ break;
+ case MAX8998_LDO4 ... MAX8998_LDO7:
+ reg = MAX8998_REG_LDO4 + (ldo - MAX8998_LDO4);
+ break;
+ case MAX8998_LDO8 ... MAX8998_LDO9:
+ reg = MAX8998_REG_LDO8_LDO9;
+ mask = 0xf;
+ if (ldo == MAX8998_LDO8)
+ shift = 4;
+ else
+ shift = 0;
+ break;
+ case MAX8998_LDO10 ... MAX8998_LDO11:
+ reg = MAX8998_REG_LDO10_LDO11;
+ if (ldo == MAX8998_LDO10) {
+ shift = 5;
+ mask = 0x7;
+ } else {
+ shift = 0;
+ mask = 0x1f;
+ }
+ break;
+ case MAX8998_LDO12 ... MAX8998_LDO17:
+ reg = MAX8998_REG_LDO12 + (ldo - MAX8998_LDO12);
+ break;
+ case MAX8998_BUCK1:
+ reg = MAX8998_REG_BUCK1_DVSARM1;
+ break;
+ case MAX8998_BUCK2:
+ reg = MAX8998_REG_BUCK2_DVSINT1;
+ break;
+ case MAX8998_BUCK3:
+ reg = MAX8998_REG_BUCK3;
+ break;
+ case MAX8998_BUCK4:
+ reg = MAX8998_REG_BUCK4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *_reg = reg;
+ *_shift = shift;
+ *_mask = mask;
+
+ return 0;
+}
+
+static int max8998_get_voltage(struct regulator_dev *rdev)
+{
+ struct max8998_data *max8998 = rdev_get_drvdata(rdev);
+ int reg, shift = 0, mask, ret;
+ u8 val;
+
+ ret = max8998_get_voltage_register(rdev, &reg, &shift, &mask);
+ if (ret)
+ return ret;
+
+ ret = max8998_read_reg(max8998->iodev, reg, &val);
+ if (ret)
+ return ret;
+
+ val >>= shift;
+ val &= mask;
+
+ return max8998_list_voltage(rdev, val);
+}
+
+static int max8998_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct max8998_data *max8998 = rdev_get_drvdata(rdev);
+ int min_vol = min_uV / 1000, max_vol = max_uV / 1000;
+ int previous_vol = 0;
+ const struct voltage_map_desc *desc;
+ int ldo = max8998_get_ldo(rdev);
+ int reg, shift = 0, mask, ret;
+ int i = 0;
+ u8 val;
+ bool en_ramp = false;
+
+ if (ldo >= ARRAY_SIZE(ldo_voltage_map))
+ return -EINVAL;
+
+ desc = ldo_voltage_map[ldo];
+ if (desc == NULL)
+ return -EINVAL;
+
+ if (max_vol < desc->min || min_vol > desc->max)
+ return -EINVAL;
+
+ while (desc->min + desc->step*i < min_vol &&
+ desc->min + desc->step*i < desc->max)
+ i++;
+
+ if (desc->min + desc->step*i > max_vol)
+ return -EINVAL;
+
+ ret = max8998_get_voltage_register(rdev, &reg, &shift, &mask);
+ if (ret)
+ return ret;
+
+ /* wait for RAMP_UP_DELAY if rdev is BUCK1/2 and
+ * ENRAMP is ON */
+ if (ldo == MAX8998_BUCK1 || ldo == MAX8998_BUCK2) {
+ max8998_read_reg(max8998->iodev, MAX8998_REG_ONOFF4, &val);
+ if (val & (1 << 4)) {
+ en_ramp = true;
+ previous_vol = max8998_get_voltage(rdev);
+ }
+ }
+
+ ret = max8998_update_reg(max8998->iodev, reg, i<<shift, mask<<shift);
+
+ if (en_ramp == true) {
+ int difference = desc->min + desc->step*i - previous_vol/1000;
+ if (difference > 0)
+ udelay(difference / ((val & 0x0f) + 1));
+ }
+
+ return ret;
+}
+
+static struct regulator_ops max8998_ldo_ops = {
+ .list_voltage = max8998_list_voltage,
+ .is_enabled = max8998_ldo_is_enabled,
+ .enable = max8998_ldo_enable,
+ .disable = max8998_ldo_disable,
+ .get_voltage = max8998_get_voltage,
+ .set_voltage = max8998_set_voltage,
+ .set_suspend_enable = max8998_ldo_enable,
+ .set_suspend_disable = max8998_ldo_disable,
+};
+
+static struct regulator_ops max8998_buck_ops = {
+ .list_voltage = max8998_list_voltage,
+ .is_enabled = max8998_ldo_is_enabled,
+ .enable = max8998_ldo_enable,
+ .disable = max8998_ldo_disable,
+ .get_voltage = max8998_get_voltage,
+ .set_voltage = max8998_set_voltage,
+ .set_suspend_enable = max8998_ldo_enable,
+ .set_suspend_disable = max8998_ldo_disable,
+};
+
+static struct regulator_ops max8998_others_ops = {
+ .is_enabled = max8998_ldo_is_enabled,
+ .enable = max8998_ldo_enable,
+ .disable = max8998_ldo_disable,
+ .set_suspend_enable = max8998_ldo_enable,
+ .set_suspend_disable = max8998_ldo_disable,
+};
+
+static struct regulator_desc regulators[] = {
+ {
+ .name = "LDO2",
+ .id = MAX8998_LDO2,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO3",
+ .id = MAX8998_LDO3,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO4",
+ .id = MAX8998_LDO4,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO5",
+ .id = MAX8998_LDO5,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO6",
+ .id = MAX8998_LDO6,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO7",
+ .id = MAX8998_LDO7,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO8",
+ .id = MAX8998_LDO8,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO9",
+ .id = MAX8998_LDO9,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO10",
+ .id = MAX8998_LDO10,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO11",
+ .id = MAX8998_LDO11,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO12",
+ .id = MAX8998_LDO12,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO13",
+ .id = MAX8998_LDO13,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO14",
+ .id = MAX8998_LDO14,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO15",
+ .id = MAX8998_LDO15,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO16",
+ .id = MAX8998_LDO16,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "LDO17",
+ .id = MAX8998_LDO17,
+ .ops = &max8998_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "BUCK1",
+ .id = MAX8998_BUCK1,
+ .ops = &max8998_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "BUCK2",
+ .id = MAX8998_BUCK2,
+ .ops = &max8998_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "BUCK3",
+ .id = MAX8998_BUCK3,
+ .ops = &max8998_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "BUCK4",
+ .id = MAX8998_BUCK4,
+ .ops = &max8998_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "EN32KHz AP",
+ .id = MAX8998_EN32KHZ_AP,
+ .ops = &max8998_others_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "EN32KHz CP",
+ .id = MAX8998_EN32KHZ_CP,
+ .ops = &max8998_others_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "ENVICHG",
+ .id = MAX8998_ENVICHG,
+ .ops = &max8998_others_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "ESAFEOUT1",
+ .id = MAX8998_ESAFEOUT1,
+ .ops = &max8998_others_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }, {
+ .name = "ESAFEOUT2",
+ .id = MAX8998_ESAFEOUT2,
+ .ops = &max8998_others_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ }
+};
+
+static __devinit int max8998_pmic_probe(struct platform_device *pdev)
+{
+ struct max8998_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ struct max8998_platform_data *pdata = dev_get_platdata(iodev->dev);
+ struct regulator_dev **rdev;
+ struct max8998_data *max8998;
+ int i, ret, size;
+
+ if (!pdata) {
+ dev_err(pdev->dev.parent, "No platform init data supplied\n");
+ return -ENODEV;
+ }
+
+ max8998 = kzalloc(sizeof(struct max8998_data), GFP_KERNEL);
+ if (!max8998)
+ return -ENOMEM;
+
+ size = sizeof(struct regulator_dev *) * (pdata->num_regulators + 1);
+ max8998->rdev = kzalloc(size, GFP_KERNEL);
+ if (!max8998->rdev) {
+ kfree(max8998);
+ return -ENOMEM;
+ }
+
+ rdev = max8998->rdev;
+ max8998->iodev = iodev;
+ platform_set_drvdata(pdev, max8998);
+
+ for (i = 0; i < pdata->num_regulators; i++) {
+ const struct voltage_map_desc *desc;
+ int id = pdata->regulators[i].id;
+ int index = id - MAX8998_LDO2;
+
+ desc = ldo_voltage_map[id];
+ if (desc && regulators[index].ops != &max8998_others_ops) {
+ int count = (desc->max - desc->min) / desc->step + 1;
+ regulators[index].n_voltages = count;
+ }
+ rdev[i] = regulator_register(&regulators[index], max8998->dev,
+ pdata->regulators[i].initdata, max8998);
+ if (IS_ERR(rdev[i])) {
+ ret = PTR_ERR(rdev[i]);
+ dev_err(max8998->dev, "regulator init failed\n");
+ rdev[i] = NULL;
+ goto err;
+ }
+ }
+
+
+ return 0;
+err:
+ for (i = 0; i <= max8998->num_regulators; i++)
+ if (rdev[i])
+ regulator_unregister(rdev[i]);
+
+ kfree(max8998->rdev);
+ kfree(max8998);
+
+ return ret;
+}
+
+static int __devexit max8998_pmic_remove(struct platform_device *pdev)
+{
+ struct max8998_data *max8998 = platform_get_drvdata(pdev);
+ struct regulator_dev **rdev = max8998->rdev;
+ int i;
+
+ for (i = 0; i <= max8998->num_regulators; i++)
+ if (rdev[i])
+ regulator_unregister(rdev[i]);
+
+ kfree(max8998->rdev);
+ kfree(max8998);
+
+ return 0;
+}
+
+static struct platform_driver max8998_pmic_driver = {
+ .driver = {
+ .name = "max8998-pmic",
+ .owner = THIS_MODULE,
+ },
+ .probe = max8998_pmic_probe,
+ .remove = __devexit_p(max8998_pmic_remove),
+};
+
+static int __init max8998_pmic_init(void)
+{
+ return platform_driver_register(&max8998_pmic_driver);
+}
+subsys_initcall(max8998_pmic_init);
+
+static void __exit max8998_pmic_cleanup(void)
+{
+ platform_driver_unregister(&max8998_pmic_driver);
+}
+module_exit(max8998_pmic_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 8998 voltage regulator driver");
+MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c
index f50afc9f287a..cd6d4fc9d74f 100644
--- a/drivers/regulator/tps65023-regulator.c
+++ b/drivers/regulator/tps65023-regulator.c
@@ -585,6 +585,8 @@ static const struct tps_info tps65023_regs[] = {
static const struct i2c_device_id tps_65023_id[] = {
{.name = "tps65023",
.driver_data = (unsigned long) tps65023_regs,},
+ {.name = "tps65021",
+ .driver_data = (unsigned long) tps65023_regs,},
{ },
};
diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c
index 8152d65220f5..c239f42aa4a3 100644
--- a/drivers/regulator/tps6507x-regulator.c
+++ b/drivers/regulator/tps6507x-regulator.c
@@ -614,6 +614,7 @@ int tps6507x_pmic_probe(struct platform_device *pdev)
}
tps6507x_dev->pmic = tps;
+ platform_set_drvdata(pdev, tps6507x_dev);
return 0;
diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c
new file mode 100644
index 000000000000..8cff1413a147
--- /dev/null
+++ b/drivers/regulator/tps6586x-regulator.c
@@ -0,0 +1,396 @@
+/*
+ * Regulator driver for TI TPS6586x
+ *
+ * Copyright (C) 2010 Compulab Ltd.
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Based on da903x
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * Copyright (C) 2008 Compulab Ltd.
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/tps6586x.h>
+
+/* supply control and voltage setting */
+#define TPS6586X_SUPPLYENA 0x10
+#define TPS6586X_SUPPLYENB 0x11
+#define TPS6586X_SUPPLYENC 0x12
+#define TPS6586X_SUPPLYEND 0x13
+#define TPS6586X_SUPPLYENE 0x14
+#define TPS6586X_VCC1 0x20
+#define TPS6586X_VCC2 0x21
+#define TPS6586X_SM1V1 0x23
+#define TPS6586X_SM1V2 0x24
+#define TPS6586X_SM1SL 0x25
+#define TPS6586X_SM0V1 0x26
+#define TPS6586X_SM0V2 0x27
+#define TPS6586X_SM0SL 0x28
+#define TPS6586X_LDO2AV1 0x29
+#define TPS6586X_LDO2AV2 0x2A
+#define TPS6586X_LDO2BV1 0x2F
+#define TPS6586X_LDO2BV2 0x30
+#define TPS6586X_LDO4V1 0x32
+#define TPS6586X_LDO4V2 0x33
+
+/* converter settings */
+#define TPS6586X_SUPPLYV1 0x41
+#define TPS6586X_SUPPLYV2 0x42
+#define TPS6586X_SUPPLYV3 0x43
+#define TPS6586X_SUPPLYV4 0x44
+#define TPS6586X_SUPPLYV5 0x45
+#define TPS6586X_SUPPLYV6 0x46
+#define TPS6586X_SMODE1 0x47
+#define TPS6586X_SMODE2 0x48
+
+struct tps6586x_regulator {
+ struct regulator_desc desc;
+
+ int volt_reg;
+ int volt_shift;
+ int volt_nbits;
+ int enable_bit[2];
+ int enable_reg[2];
+
+ int *voltages;
+
+ /* for DVM regulators */
+ int go_reg;
+ int go_bit;
+};
+
+static inline struct device *to_tps6586x_dev(struct regulator_dev *rdev)
+{
+ return rdev_get_dev(rdev)->parent->parent;
+}
+
+static int tps6586x_ldo_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct tps6586x_regulator *info = rdev_get_drvdata(rdev);
+
+ return info->voltages[selector] * 1000;
+}
+
+
+static int __tps6586x_ldo_set_voltage(struct device *parent,
+ struct tps6586x_regulator *ri,
+ int min_uV, int max_uV)
+{
+ int val, uV;
+ uint8_t mask;
+
+ for (val = 0; val < ri->desc.n_voltages; val++) {
+ uV = ri->voltages[val] * 1000;
+
+ /* LDO0 has minimal voltage 1.2 rather than 1.25 */
+ if (ri->desc.id == TPS6586X_ID_LDO_0 && val == 0)
+ uV -= 50 * 1000;
+
+ /* use the first in-range value */
+ if (min_uV <= uV && uV <= max_uV) {
+
+ val <<= ri->volt_shift;
+ mask = ((1 << ri->volt_nbits) - 1) << ri->volt_shift;
+
+ return tps6586x_update(parent, ri->volt_reg, val, mask);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int tps6586x_ldo_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps6586x_dev(rdev);
+
+ return __tps6586x_ldo_set_voltage(parent, ri, min_uV, max_uV);
+}
+
+static int tps6586x_ldo_get_voltage(struct regulator_dev *rdev)
+{
+ struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps6586x_dev(rdev);
+ uint8_t val, mask;
+ int ret;
+
+ ret = tps6586x_read(parent, ri->volt_reg, &val);
+ if (ret)
+ return ret;
+
+ mask = ((1 << ri->volt_nbits) - 1) << ri->volt_shift;
+ val = (val & mask) >> ri->volt_shift;
+
+ if (val > ri->desc.n_voltages)
+ BUG();
+
+ return ri->voltages[val] * 1000;
+}
+
+static int tps6586x_dvm_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps6586x_dev(rdev);
+ int ret;
+
+ ret = __tps6586x_ldo_set_voltage(parent, ri, min_uV, max_uV);
+ if (ret)
+ return ret;
+
+ return tps6586x_set_bits(parent, ri->go_reg, ri->go_bit);
+}
+
+static int tps6586x_regulator_enable(struct regulator_dev *rdev)
+{
+ struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps6586x_dev(rdev);
+
+ return tps6586x_set_bits(parent, ri->enable_reg[0],
+ 1 << ri->enable_bit[0]);
+}
+
+static int tps6586x_regulator_disable(struct regulator_dev *rdev)
+{
+ struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps6586x_dev(rdev);
+
+ return tps6586x_clr_bits(parent, ri->enable_reg[0],
+ 1 << ri->enable_bit[0]);
+}
+
+static int tps6586x_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps6586x_dev(rdev);
+ uint8_t reg_val;
+ int ret;
+
+ ret = tps6586x_read(parent, ri->enable_reg[0], &reg_val);
+ if (ret)
+ return ret;
+
+ return !!(reg_val & (1 << ri->enable_bit[0]));
+}
+
+static struct regulator_ops tps6586x_regulator_ldo_ops = {
+ .list_voltage = tps6586x_ldo_list_voltage,
+ .get_voltage = tps6586x_ldo_get_voltage,
+ .set_voltage = tps6586x_ldo_set_voltage,
+
+ .is_enabled = tps6586x_regulator_is_enabled,
+ .enable = tps6586x_regulator_enable,
+ .disable = tps6586x_regulator_disable,
+};
+
+static struct regulator_ops tps6586x_regulator_dvm_ops = {
+ .list_voltage = tps6586x_ldo_list_voltage,
+ .get_voltage = tps6586x_ldo_get_voltage,
+ .set_voltage = tps6586x_dvm_set_voltage,
+
+ .is_enabled = tps6586x_regulator_is_enabled,
+ .enable = tps6586x_regulator_enable,
+ .disable = tps6586x_regulator_disable,
+};
+
+static int tps6586x_ldo_voltages[] = {
+ 1250, 1500, 1800, 2500, 2700, 2850, 3100, 3300,
+};
+
+static int tps6586x_ldo4_voltages[] = {
+ 1700, 1725, 1750, 1775, 1800, 1825, 1850, 1875,
+ 1900, 1925, 1950, 1975, 2000, 2025, 2050, 2075,
+ 2100, 2125, 2150, 2175, 2200, 2225, 2250, 2275,
+ 2300, 2325, 2350, 2375, 2400, 2425, 2450, 2475,
+};
+
+static int tps6586x_sm2_voltages[] = {
+ 3000, 3050, 3100, 3150, 3200, 3250, 3300, 3350,
+ 3400, 3450, 3500, 3550, 3600, 3650, 3700, 3750,
+ 3800, 3850, 3900, 3950, 4000, 4050, 4100, 4150,
+ 4200, 4250, 4300, 4350, 4400, 4450, 4500, 4550,
+};
+
+static int tps6586x_dvm_voltages[] = {
+ 725, 750, 775, 800, 825, 850, 875, 900,
+ 925, 950, 975, 1000, 1025, 1050, 1075, 1100,
+ 1125, 1150, 1175, 1200, 1225, 1250, 1275, 1300,
+ 1325, 1350, 1375, 1400, 1425, 1450, 1475, 1500,
+};
+
+#define TPS6586X_REGULATOR(_id, vdata, _ops, vreg, shift, nbits, \
+ ereg0, ebit0, ereg1, ebit1, goreg, gobit) \
+{ \
+ .desc = { \
+ .name = "REG-" #_id, \
+ .ops = &tps6586x_regulator_##_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = TPS6586X_ID_##_id, \
+ .n_voltages = ARRAY_SIZE(tps6586x_##vdata##_voltages), \
+ .owner = THIS_MODULE, \
+ }, \
+ .volt_reg = TPS6586X_##vreg, \
+ .volt_shift = (shift), \
+ .volt_nbits = (nbits), \
+ .enable_reg[0] = TPS6586X_SUPPLY##ereg0, \
+ .enable_bit[0] = (ebit0), \
+ .enable_reg[1] = TPS6586X_SUPPLY##ereg1, \
+ .enable_bit[1] = (ebit1), \
+ .voltages = tps6586x_##vdata##_voltages, \
+}
+
+#define TPS6586X_LDO(_id, vdata, vreg, shift, nbits, \
+ ereg0, ebit0, ereg1, ebit1) \
+ TPS6586X_REGULATOR(_id, vdata, ldo_ops, vreg, shift, nbits, \
+ ereg0, ebit0, ereg1, ebit1, 0, 0)
+
+#define TPS6586X_DVM(_id, vdata, vreg, shift, nbits, \
+ ereg0, ebit0, ereg1, ebit1, goreg, gobit) \
+ TPS6586X_REGULATOR(_id, vdata, dvm_ops, vreg, shift, nbits, \
+ ereg0, ebit0, ereg1, ebit1, goreg, gobit)
+
+static struct tps6586x_regulator tps6586x_regulator[] = {
+ TPS6586X_LDO(LDO_0, ldo, SUPPLYV1, 5, 3, ENC, 0, END, 0),
+ TPS6586X_LDO(LDO_3, ldo, SUPPLYV4, 0, 3, ENC, 2, END, 2),
+ TPS6586X_LDO(LDO_5, ldo, SUPPLYV6, 0, 3, ENE, 6, ENE, 6),
+ TPS6586X_LDO(LDO_6, ldo, SUPPLYV3, 0, 3, ENC, 4, END, 4),
+ TPS6586X_LDO(LDO_7, ldo, SUPPLYV3, 3, 3, ENC, 5, END, 5),
+ TPS6586X_LDO(LDO_8, ldo, SUPPLYV1, 5, 3, ENC, 6, END, 6),
+ TPS6586X_LDO(LDO_9, ldo, SUPPLYV6, 3, 3, ENE, 7, ENE, 7),
+ TPS6586X_LDO(LDO_RTC, ldo, SUPPLYV4, 3, 3, ENE, 7, ENE, 7),
+ TPS6586X_LDO(LDO_1, dvm, SUPPLYV1, 0, 5, ENC, 1, END, 1),
+ TPS6586X_LDO(SM_2, sm2, SUPPLYV2, 0, 5, ENC, 1, END, 1),
+
+ TPS6586X_DVM(LDO_2, dvm, LDO2BV1, 0, 5, ENA, 3, ENB, 3, VCC2, 6),
+ TPS6586X_DVM(LDO_4, ldo4, LDO4V1, 0, 5, ENC, 3, END, 3, VCC1, 6),
+ TPS6586X_DVM(SM_0, dvm, SM0V1, 0, 5, ENA, 1, ENB, 1, VCC1, 2),
+ TPS6586X_DVM(SM_1, dvm, SM1V1, 0, 5, ENA, 0, ENB, 0, VCC1, 0),
+};
+
+/*
+ * TPS6586X has 2 enable bits that are OR'ed to determine the actual
+ * regulator state. Clearing one of this bits allows switching
+ * regulator on and of with single register write.
+ */
+static inline int tps6586x_regulator_preinit(struct device *parent,
+ struct tps6586x_regulator *ri)
+{
+ uint8_t val1, val2;
+ int ret;
+
+ ret = tps6586x_read(parent, ri->enable_reg[0], &val1);
+ if (ret)
+ return ret;
+
+ ret = tps6586x_read(parent, ri->enable_reg[1], &val2);
+ if (ret)
+ return ret;
+
+ if (!(val2 & ri->enable_bit[1]))
+ return 0;
+
+ /*
+ * The regulator is on, but it's enabled with the bit we don't
+ * want to use, so we switch the enable bits
+ */
+ if (!(val1 & ri->enable_bit[0])) {
+ ret = tps6586x_set_bits(parent, ri->enable_reg[0],
+ 1 << ri->enable_bit[0]);
+ if (ret)
+ return ret;
+ }
+
+ return tps6586x_clr_bits(parent, ri->enable_reg[1],
+ 1 << ri->enable_bit[1]);
+}
+
+static inline struct tps6586x_regulator *find_regulator_info(int id)
+{
+ struct tps6586x_regulator *ri;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tps6586x_regulator); i++) {
+ ri = &tps6586x_regulator[i];
+ if (ri->desc.id == id)
+ return ri;
+ }
+ return NULL;
+}
+
+static int __devinit tps6586x_regulator_probe(struct platform_device *pdev)
+{
+ struct tps6586x_regulator *ri = NULL;
+ struct regulator_dev *rdev;
+ int id = pdev->id;
+ int err;
+
+ dev_dbg(&pdev->dev, "Probing reulator %d\n", id);
+
+ ri = find_regulator_info(id);
+ if (ri == NULL) {
+ dev_err(&pdev->dev, "invalid regulator ID specified\n");
+ return -EINVAL;
+ }
+
+ err = tps6586x_regulator_preinit(pdev->dev.parent, ri);
+ if (err)
+ return err;
+
+ rdev = regulator_register(&ri->desc, &pdev->dev,
+ pdev->dev.platform_data, ri);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ ri->desc.name);
+ return PTR_ERR(rdev);
+ }
+
+ platform_set_drvdata(pdev, rdev);
+
+ return 0;
+}
+
+static int __devexit tps6586x_regulator_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+ regulator_unregister(rdev);
+ return 0;
+}
+
+static struct platform_driver tps6586x_regulator_driver = {
+ .driver = {
+ .name = "tps6586x-regulator",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps6586x_regulator_probe,
+ .remove = __devexit_p(tps6586x_regulator_remove),
+};
+
+static int __init tps6586x_regulator_init(void)
+{
+ return platform_driver_register(&tps6586x_regulator_driver);
+}
+subsys_initcall(tps6586x_regulator_init);
+
+static void __exit tps6586x_regulator_exit(void)
+{
+ platform_driver_unregister(&tps6586x_regulator_driver);
+}
+module_exit(tps6586x_regulator_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_DESCRIPTION("Regulator Driver for TI TPS6586X PMIC");
+MODULE_ALIAS("platform:tps6586x-regulator");
diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c
index 5a1dc8a24d35..03713bc66e4a 100644
--- a/drivers/regulator/wm8994-regulator.c
+++ b/drivers/regulator/wm8994-regulator.c
@@ -219,8 +219,6 @@ static __devinit int wm8994_ldo_probe(struct platform_device *pdev)
ldo->wm8994 = wm8994;
- ldo->is_enabled = true;
-
if (pdata->ldo[id].enable && gpio_is_valid(pdata->ldo[id].enable)) {
ldo->enable = pdata->ldo[id].enable;
@@ -237,7 +235,8 @@ static __devinit int wm8994_ldo_probe(struct platform_device *pdev)
ret);
goto err_gpio;
}
- }
+ } else
+ ldo->is_enabled = true;
ldo->regulator = regulator_register(&wm8994_ldo_desc[id], &pdev->dev,
pdata->ldo[id].init_data, ldo);
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 4301a6c7ed3b..48ca7132cc05 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -166,6 +166,16 @@ config RTC_DRV_DS1672
This driver can also be built as a module. If so, the module
will be called rtc-ds1672.
+config RTC_DRV_DS3232
+ tristate "Dallas/Maxim DS3232"
+ depends on RTC_CLASS && I2C
+ help
+ If you say yes here you get support for Dallas Semiconductor
+ DS3232 real-time clock chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds3232.
+
config RTC_DRV_MAX6900
tristate "Maxim MAX6900"
help
@@ -203,6 +213,15 @@ config RTC_DRV_ISL1208
This driver can also be built as a module. If so, the module
will be called rtc-isl1208.
+config RTC_DRV_ISL12022
+ tristate "Intersil ISL12022"
+ help
+ If you say yes here you get support for the
+ Intersil ISL12022 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-isl12022.
+
config RTC_DRV_X1205
tristate "Xicor/Intersil X1205"
help
@@ -537,6 +556,16 @@ config RTC_DRV_MSM6242
This driver can also be built as a module. If so, the module
will be called rtc-msm6242.
+config RTC_DRV_IMXDI
+ tristate "Freescale IMX DryIce Real Time Clock"
+ depends on ARCH_MX25
+ depends on RTC_CLASS
+ help
+ Support for Freescale IMX DryIce RTC
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-imxdi".
+
config RTC_MXC
tristate "Freescale MXC Real Time Clock"
depends on ARCH_MXC
@@ -645,9 +674,16 @@ config RTC_DRV_OMAP
DA8xx/OMAP-L13x chips. This driver can also be built as a
module called rtc-omap.
+config HAVE_S3C_RTC
+ bool
+ help
+ This will include RTC support for Samsung SoCs. If
+ you want to include RTC support for any machine, kindly
+ select this in the respective mach-XXXX/Kconfig file.
+
config RTC_DRV_S3C
tristate "Samsung S3C series SoC RTC"
- depends on ARCH_S3C2410 || ARCH_S3C64XX
+ depends on ARCH_S3C2410 || ARCH_S3C64XX || HAVE_S3C_RTC
help
RTC (Realtime Clock) driver for the clock inbuilt into the
Samsung S3C24XX series of SoCs. This can provide periodic
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index fedf9bb36593..0f207b3b5833 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -41,12 +41,15 @@ obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o
obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
+obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
+obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
+obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o
obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o
obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index 11b8ea29d2b7..5856167a0c90 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -970,7 +970,6 @@ static inline int cmos_poweroff(struct device *dev)
#include <linux/acpi.h>
-#ifdef CONFIG_PM
static u32 rtc_handler(void *context)
{
acpi_clear_event(ACPI_EVENT_RTC);
@@ -999,11 +998,6 @@ static void rtc_wake_off(struct device *dev)
{
acpi_disable_event(ACPI_EVENT_RTC, 0);
}
-#else
-#define rtc_wake_setup() do{}while(0)
-#define rtc_wake_on NULL
-#define rtc_wake_off NULL
-#endif
/* Every ACPI platform has a mc146818 compatible "cmos rtc". Here we find
* its device node and pass extra config data. This helps its driver use
diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
new file mode 100644
index 000000000000..9daed8db83d3
--- /dev/null
+++ b/drivers/rtc/rtc-ds3232.c
@@ -0,0 +1,326 @@
+/*
+ * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2C
+ *
+ * Copyright (C) 2009-2010 Freescale Semiconductor.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+/*
+ * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
+ * recommened in .../Documentation/i2c/writing-clients section
+ * "Sending and receiving", using SMBus level communication is preferred.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+#define DS3232_REG_SECONDS 0x00
+#define DS3232_REG_MINUTES 0x01
+#define DS3232_REG_HOURS 0x02
+#define DS3232_REG_AMPM 0x02
+#define DS3232_REG_DAY 0x03
+#define DS3232_REG_DATE 0x04
+#define DS3232_REG_MONTH 0x05
+#define DS3232_REG_CENTURY 0x05
+#define DS3232_REG_YEAR 0x06
+#define DS3232_REG_ALARM1 0x07 /* Alarm 1 BASE */
+#define DS3232_REG_ALARM2 0x0B /* Alarm 2 BASE */
+#define DS3232_REG_CR 0x0E /* Control register */
+# define DS3232_REG_CR_nEOSC 0x80
+# define DS3232_REG_CR_INTCN 0x04
+# define DS3232_REG_CR_A2IE 0x02
+# define DS3232_REG_CR_A1IE 0x01
+
+#define DS3232_REG_SR 0x0F /* control/status register */
+# define DS3232_REG_SR_OSF 0x80
+# define DS3232_REG_SR_BSY 0x04
+# define DS3232_REG_SR_A2F 0x02
+# define DS3232_REG_SR_A1F 0x01
+
+struct ds3232 {
+ struct i2c_client *client;
+ struct rtc_device *rtc;
+ struct work_struct work;
+
+ /* The mutex protects alarm operations, and prevents a race
+ * between the enable_irq() in the workqueue and the free_irq()
+ * in the remove function.
+ */
+ struct mutex mutex;
+ int exiting;
+};
+
+static struct i2c_driver ds3232_driver;
+
+static int ds3232_check_rtc_status(struct i2c_client *client)
+{
+ int ret = 0;
+ int control, stat;
+
+ stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (stat < 0)
+ return stat;
+
+ if (stat & DS3232_REG_SR_OSF)
+ dev_warn(&client->dev,
+ "oscillator discontinuity flagged, "
+ "time unreliable\n");
+
+ stat &= ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
+
+ ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+ if (ret < 0)
+ return ret;
+
+ /* If the alarm is pending, clear it before requesting
+ * the interrupt, so an interrupt event isn't reported
+ * before everything is initialized.
+ */
+
+ control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (control < 0)
+ return control;
+
+ control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
+ control |= DS3232_REG_CR_INTCN;
+
+ return i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+}
+
+static int ds3232_read_time(struct device *dev, struct rtc_time *time)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+ u8 buf[7];
+ unsigned int year, month, day, hour, minute, second;
+ unsigned int week, twelve_hr, am_pm;
+ unsigned int century, add_century = 0;
+
+ ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_SECONDS, 7, buf);
+
+ if (ret < 0)
+ return ret;
+ if (ret < 7)
+ return -EIO;
+
+ second = buf[0];
+ minute = buf[1];
+ hour = buf[2];
+ week = buf[3];
+ day = buf[4];
+ month = buf[5];
+ year = buf[6];
+
+ /* Extract additional information for AM/PM and century */
+
+ twelve_hr = hour & 0x40;
+ am_pm = hour & 0x20;
+ century = month & 0x80;
+
+ /* Write to rtc_time structure */
+
+ time->tm_sec = bcd2bin(second);
+ time->tm_min = bcd2bin(minute);
+ if (twelve_hr) {
+ /* Convert to 24 hr */
+ if (am_pm)
+ time->tm_hour = bcd2bin(hour & 0x1F) + 12;
+ else
+ time->tm_hour = bcd2bin(hour & 0x1F);
+ } else {
+ time->tm_hour = bcd2bin(hour);
+ }
+
+ time->tm_wday = bcd2bin(week);
+ time->tm_mday = bcd2bin(day);
+ time->tm_mon = bcd2bin(month & 0x7F);
+ if (century)
+ add_century = 100;
+
+ time->tm_year = bcd2bin(year) + add_century;
+
+ return rtc_valid_tm(time);
+}
+
+static int ds3232_set_time(struct device *dev, struct rtc_time *time)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 buf[7];
+
+ /* Extract time from rtc_time and load into ds3232*/
+
+ buf[0] = bin2bcd(time->tm_sec);
+ buf[1] = bin2bcd(time->tm_min);
+ buf[2] = bin2bcd(time->tm_hour);
+ buf[3] = bin2bcd(time->tm_wday); /* Day of the week */
+ buf[4] = bin2bcd(time->tm_mday); /* Date */
+ buf[5] = bin2bcd(time->tm_mon);
+ if (time->tm_year >= 100) {
+ buf[5] |= 0x80;
+ buf[6] = bin2bcd(time->tm_year - 100);
+ } else {
+ buf[6] = bin2bcd(time->tm_year);
+ }
+
+ return i2c_smbus_write_i2c_block_data(client,
+ DS3232_REG_SECONDS, 7, buf);
+}
+
+static irqreturn_t ds3232_irq(int irq, void *dev_id)
+{
+ struct i2c_client *client = dev_id;
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+ disable_irq_nosync(irq);
+ schedule_work(&ds3232->work);
+ return IRQ_HANDLED;
+}
+
+static void ds3232_work(struct work_struct *work)
+{
+ struct ds3232 *ds3232 = container_of(work, struct ds3232, work);
+ struct i2c_client *client = ds3232->client;
+ int stat, control;
+
+ mutex_lock(&ds3232->mutex);
+
+ stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (stat < 0)
+ goto unlock;
+
+ if (stat & DS3232_REG_SR_A1F) {
+ control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (control < 0)
+ goto out;
+ /* disable alarm1 interrupt */
+ control &= ~(DS3232_REG_CR_A1IE);
+ i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+
+ /* clear the alarm pend flag */
+ stat &= ~DS3232_REG_SR_A1F;
+ i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+
+ rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF);
+ }
+
+out:
+ if (!ds3232->exiting)
+ enable_irq(client->irq);
+unlock:
+ mutex_unlock(&ds3232->mutex);
+}
+
+static const struct rtc_class_ops ds3232_rtc_ops = {
+ .read_time = ds3232_read_time,
+ .set_time = ds3232_set_time,
+};
+
+static int __devinit ds3232_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ds3232 *ds3232;
+ int ret;
+
+ ds3232 = kzalloc(sizeof(struct ds3232), GFP_KERNEL);
+ if (!ds3232)
+ return -ENOMEM;
+
+ ds3232->client = client;
+ i2c_set_clientdata(client, ds3232);
+
+ INIT_WORK(&ds3232->work, ds3232_work);
+ mutex_init(&ds3232->mutex);
+
+ ret = ds3232_check_rtc_status(client);
+ if (ret)
+ goto out_free;
+
+ ds3232->rtc = rtc_device_register(client->name, &client->dev,
+ &ds3232_rtc_ops, THIS_MODULE);
+ if (IS_ERR(ds3232->rtc)) {
+ ret = PTR_ERR(ds3232->rtc);
+ dev_err(&client->dev, "unable to register the class device\n");
+ goto out_irq;
+ }
+
+ if (client->irq >= 0) {
+ ret = request_irq(client->irq, ds3232_irq, 0,
+ "ds3232", client);
+ if (ret) {
+ dev_err(&client->dev, "unable to request IRQ\n");
+ goto out_free;
+ }
+ }
+
+ return 0;
+
+out_irq:
+ if (client->irq >= 0)
+ free_irq(client->irq, client);
+
+out_free:
+ i2c_set_clientdata(client, NULL);
+ kfree(ds3232);
+ return ret;
+}
+
+static int __devexit ds3232_remove(struct i2c_client *client)
+{
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+ if (client->irq >= 0) {
+ mutex_lock(&ds3232->mutex);
+ ds3232->exiting = 1;
+ mutex_unlock(&ds3232->mutex);
+
+ free_irq(client->irq, client);
+ flush_scheduled_work();
+ }
+
+ rtc_device_unregister(ds3232->rtc);
+ i2c_set_clientdata(client, NULL);
+ kfree(ds3232);
+ return 0;
+}
+
+static const struct i2c_device_id ds3232_id[] = {
+ { "ds3232", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ds3232_id);
+
+static struct i2c_driver ds3232_driver = {
+ .driver = {
+ .name = "rtc-ds3232",
+ .owner = THIS_MODULE,
+ },
+ .probe = ds3232_probe,
+ .remove = __devexit_p(ds3232_remove),
+ .id_table = ds3232_id,
+};
+
+static int __init ds3232_init(void)
+{
+ return i2c_add_driver(&ds3232_driver);
+}
+
+static void __exit ds3232_exit(void)
+{
+ i2c_del_driver(&ds3232_driver);
+}
+
+module_init(ds3232_init);
+module_exit(ds3232_exit);
+
+MODULE_AUTHOR("Srikanth Srinivasan <srikanth.srinivasan@freescale.com>");
+MODULE_DESCRIPTION("Maxim/Dallas DS3232 RTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c
index e4de8f37ae4a..4cf2e70c5078 100644
--- a/drivers/rtc/rtc-fm3130.c
+++ b/drivers/rtc/rtc-fm3130.c
@@ -52,8 +52,8 @@ struct fm3130 {
struct i2c_msg msg[4];
struct i2c_client *client;
struct rtc_device *rtc;
+ int alarm_valid;
int data_valid;
- int alarm;
};
static const struct i2c_device_id fm3130_id[] = {
{ "fm3130", 0 },
@@ -87,11 +87,7 @@ static void fm3130_rtc_mode(struct device *dev, int mode)
dev_dbg(dev, "invalid mode %d\n", mode);
break;
}
- /* Checking for alarm */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) {
- fm3130->alarm = 1;
- fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF;
- }
+
i2c_smbus_write_byte_data(fm3130->client,
FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL]);
}
@@ -208,6 +204,17 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
struct fm3130 *fm3130 = dev_get_drvdata(dev);
int tmp;
struct rtc_time *tm = &alrm->time;
+
+ if (!fm3130->alarm_valid) {
+ /*
+ * We have invalid alarm in RTC, probably due to battery faults
+ * or other problems. Return EIO for now, it will allow us to
+ * set alarm value later instead of error during probing which
+ * disables device
+ */
+ return -EIO;
+ }
+
/* read the RTC alarm registers all at once */
tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent),
&fm3130->msg[2], 2);
@@ -222,20 +229,31 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
fm3130->regs[FM3130_ALARM_DATE],
fm3130->regs[FM3130_ALARM_MONTHS]);
-
tm->tm_sec = bcd2bin(fm3130->regs[FM3130_ALARM_SECONDS] & 0x7F);
tm->tm_min = bcd2bin(fm3130->regs[FM3130_ALARM_MINUTES] & 0x7F);
tm->tm_hour = bcd2bin(fm3130->regs[FM3130_ALARM_HOURS] & 0x3F);
tm->tm_mday = bcd2bin(fm3130->regs[FM3130_ALARM_DATE] & 0x3F);
tm->tm_mon = bcd2bin(fm3130->regs[FM3130_ALARM_MONTHS] & 0x1F);
+
if (tm->tm_mon > 0)
tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */
+
dev_dbg(dev, "%s secs=%d, mins=%d, "
"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
"read alarm", tm->tm_sec, tm->tm_min,
tm->tm_hour, tm->tm_mday,
tm->tm_mon, tm->tm_year, tm->tm_wday);
+ /* check if alarm enabled */
+ fm3130->regs[FM3130_RTC_CONTROL] =
+ i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
+
+ if ((fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AEN) &&
+ (~fm3130->regs[FM3130_RTC_CONTROL] &
+ FM3130_RTC_CONTROL_BIT_CAL)) {
+ alrm->enabled = 1;
+ }
+
return 0;
}
@@ -251,25 +269,20 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
tm->tm_hour, tm->tm_mday,
tm->tm_mon, tm->tm_year, tm->tm_wday);
- if (tm->tm_sec != -1)
- fm3130->regs[FM3130_ALARM_SECONDS] =
- bin2bcd(tm->tm_sec) | 0x80;
+ fm3130->regs[FM3130_ALARM_SECONDS] =
+ (tm->tm_sec != -1) ? bin2bcd(tm->tm_sec) : 0x80;
- if (tm->tm_min != -1)
- fm3130->regs[FM3130_ALARM_MINUTES] =
- bin2bcd(tm->tm_min) | 0x80;
+ fm3130->regs[FM3130_ALARM_MINUTES] =
+ (tm->tm_min != -1) ? bin2bcd(tm->tm_min) : 0x80;
- if (tm->tm_hour != -1)
- fm3130->regs[FM3130_ALARM_HOURS] =
- bin2bcd(tm->tm_hour) | 0x80;
+ fm3130->regs[FM3130_ALARM_HOURS] =
+ (tm->tm_hour != -1) ? bin2bcd(tm->tm_hour) : 0x80;
- if (tm->tm_mday != -1)
- fm3130->regs[FM3130_ALARM_DATE] =
- bin2bcd(tm->tm_mday) | 0x80;
+ fm3130->regs[FM3130_ALARM_DATE] =
+ (tm->tm_mday != -1) ? bin2bcd(tm->tm_mday) : 0x80;
- if (tm->tm_mon != -1)
- fm3130->regs[FM3130_ALARM_MONTHS] =
- bin2bcd(tm->tm_mon + 1) | 0x80;
+ fm3130->regs[FM3130_ALARM_MONTHS] =
+ (tm->tm_mon != -1) ? bin2bcd(tm->tm_mon + 1) : 0x80;
dev_dbg(dev, "alarm write %02x %02x %02x %02x %02x\n",
fm3130->regs[FM3130_ALARM_SECONDS],
@@ -285,11 +298,8 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
}
fm3130->regs[FM3130_RTC_CONTROL] =
i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
- /* Checking for alarm */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) {
- fm3130->alarm = 1;
- fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF;
- }
+
+ /* enable or disable alarm */
if (alrm->enabled) {
i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,
(fm3130->regs[FM3130_RTC_CONTROL] &
@@ -298,16 +308,55 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
} else {
i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,
fm3130->regs[FM3130_RTC_CONTROL] &
- ~(FM3130_RTC_CONTROL_BIT_AEN));
+ ~(FM3130_RTC_CONTROL_BIT_CAL) &
+ ~(FM3130_RTC_CONTROL_BIT_AEN));
}
+
+ /* We assume here that data is valid once written */
+ if (!fm3130->alarm_valid)
+ fm3130->alarm_valid = 1;
+
return 0;
}
+static int fm3130_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct fm3130 *fm3130 = dev_get_drvdata(dev);
+ int ret = 0;
+
+ fm3130->regs[FM3130_RTC_CONTROL] =
+ i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
+
+ dev_dbg(dev, "alarm_irq_enable: enable=%d, FM3130_RTC_CONTROL=%02x\n",
+ enabled, fm3130->regs[FM3130_RTC_CONTROL]);
+
+ switch (enabled) {
+ case 0: /* alarm off */
+ ret = i2c_smbus_write_byte_data(fm3130->client,
+ FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_CAL) &
+ ~(FM3130_RTC_CONTROL_BIT_AEN));
+ break;
+ case 1: /* alarm on */
+ ret = i2c_smbus_write_byte_data(fm3130->client,
+ FM3130_RTC_CONTROL, (fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_CAL)) |
+ FM3130_RTC_CONTROL_BIT_AEN);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
static const struct rtc_class_ops fm3130_rtc_ops = {
.read_time = fm3130_get_time,
.set_time = fm3130_set_time,
.read_alarm = fm3130_read_alarm,
.set_alarm = fm3130_set_alarm,
+ .alarm_irq_enable = fm3130_alarm_irq_enable,
};
static struct i2c_driver fm3130_driver;
@@ -356,6 +405,7 @@ static int __devinit fm3130_probe(struct i2c_client *client,
fm3130->msg[3].len = FM3130_ALARM_REGS;
fm3130->msg[3].buf = &fm3130->regs[FM3130_ALARM_SECONDS];
+ fm3130->alarm_valid = 0;
fm3130->data_valid = 0;
tmp = i2c_transfer(adapter, fm3130->msg, 4);
@@ -370,12 +420,6 @@ static int __devinit fm3130_probe(struct i2c_client *client,
fm3130->regs[FM3130_CAL_CONTROL] =
i2c_smbus_read_byte_data(client, FM3130_CAL_CONTROL);
- /* Checking for alarm */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) {
- fm3130->alarm = 1;
- fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF;
- }
-
/* Disabling calibration mode */
if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_CAL) {
i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
@@ -400,44 +444,79 @@ static int __devinit fm3130_probe(struct i2c_client *client,
fm3130->regs[FM3130_CAL_CONTROL] &
~(FM3130_CAL_CONTROL_BIT_nOSCEN));
- /* oscillator fault? clear flag, and warn */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB)
+ /* low battery? clear flag, and warn */
+ if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB) {
+ i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
+ fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_LB));
dev_warn(&client->dev, "Low battery!\n");
+ }
- /* oscillator fault? clear flag, and warn */
+ /* check if Power On Reset bit is set */
if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_POR) {
i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
fm3130->regs[FM3130_RTC_CONTROL] &
~FM3130_RTC_CONTROL_BIT_POR);
- dev_warn(&client->dev, "SET TIME!\n");
+ dev_dbg(&client->dev, "POR bit is set\n");
}
/* ACS is controlled by alarm */
i2c_smbus_write_byte_data(client, FM3130_ALARM_WP_CONTROL, 0x80);
- /* TODO */
- /* TODO need to sanity check alarm */
- tmp = fm3130->regs[FM3130_RTC_SECONDS];
- tmp = bcd2bin(tmp & 0x7f);
- if (tmp > 60)
- goto exit_bad;
+ /* alarm registers sanity check */
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f);
+ if (tmp > 59)
+ goto bad_alarm;
+
tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f);
- if (tmp > 60)
- goto exit_bad;
+ if (tmp > 59)
+ goto bad_alarm;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f);
+ if (tmp > 23)
+ goto bad_alarm;
tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f);
if (tmp == 0 || tmp > 31)
- goto exit_bad;
+ goto bad_alarm;
tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f);
if (tmp == 0 || tmp > 12)
- goto exit_bad;
+ goto bad_alarm;
- tmp = fm3130->regs[FM3130_RTC_HOURS];
+ fm3130->alarm_valid = 1;
+
+bad_alarm:
+
+ /* clock registers sanity chek */
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f);
+ if (tmp > 59)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f);
+ if (tmp > 59)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f);
+ if (tmp > 23)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_DAY] & 0x7);
+ if (tmp == 0 || tmp > 7)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f);
+ if (tmp == 0 || tmp > 31)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f);
+ if (tmp == 0 || tmp > 12)
+ goto bad_clock;
fm3130->data_valid = 1;
-exit_bad:
- if (!fm3130->data_valid)
+bad_clock:
+
+ if (!fm3130->data_valid || !fm3130->alarm_valid)
dev_dbg(&client->dev,
"%s: %02x %02x %02x %02x %02x %02x %02x %02x"
"%02x %02x %02x %02x %02x %02x %02x\n",
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
new file mode 100644
index 000000000000..2dd3c0163272
--- /dev/null
+++ b/drivers/rtc/rtc-imxdi.c
@@ -0,0 +1,519 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2010 Orex Computed Radiography
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/* based on rtc-mc13892.c */
+
+/*
+ * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block
+ * to implement a Linux RTC. Times and alarms are truncated to seconds.
+ * Since the RTC framework performs API locking via rtc->ops_lock the
+ * only simultaneous accesses we need to deal with is updating DryIce
+ * registers while servicing an alarm.
+ *
+ * Note that reading the DSR (DryIce Status Register) automatically clears
+ * the WCF (Write Complete Flag). All DryIce writes are synchronized to the
+ * LP (Low Power) domain and set the WCF upon completion. Writes to the
+ * DIER (DryIce Interrupt Enable Register) are the only exception. These
+ * occur at normal bus speeds and do not set WCF. Periodic interrupts are
+ * not supported by the hardware.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/workqueue.h>
+
+/* DryIce Register Definitions */
+
+#define DTCMR 0x00 /* Time Counter MSB Reg */
+#define DTCLR 0x04 /* Time Counter LSB Reg */
+
+#define DCAMR 0x08 /* Clock Alarm MSB Reg */
+#define DCALR 0x0c /* Clock Alarm LSB Reg */
+#define DCAMR_UNSET 0xFFFFFFFF /* doomsday - 1 sec */
+
+#define DCR 0x10 /* Control Reg */
+#define DCR_TCE (1 << 3) /* Time Counter Enable */
+
+#define DSR 0x14 /* Status Reg */
+#define DSR_WBF (1 << 10) /* Write Busy Flag */
+#define DSR_WNF (1 << 9) /* Write Next Flag */
+#define DSR_WCF (1 << 8) /* Write Complete Flag */
+#define DSR_WEF (1 << 7) /* Write Error Flag */
+#define DSR_CAF (1 << 4) /* Clock Alarm Flag */
+#define DSR_NVF (1 << 1) /* Non-Valid Flag */
+#define DSR_SVF (1 << 0) /* Security Violation Flag */
+
+#define DIER 0x18 /* Interrupt Enable Reg */
+#define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */
+#define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */
+#define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */
+#define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */
+
+/**
+ * struct imxdi_dev - private imxdi rtc data
+ * @pdev: pionter to platform dev
+ * @rtc: pointer to rtc struct
+ * @ioaddr: IO registers pointer
+ * @irq: dryice normal interrupt
+ * @clk: input reference clock
+ * @dsr: copy of the DSR register
+ * @irq_lock: interrupt enable register (DIER) lock
+ * @write_wait: registers write complete queue
+ * @write_mutex: serialize registers write
+ * @work: schedule alarm work
+ */
+struct imxdi_dev {
+ struct platform_device *pdev;
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ int irq;
+ struct clk *clk;
+ u32 dsr;
+ spinlock_t irq_lock;
+ wait_queue_head_t write_wait;
+ struct mutex write_mutex;
+ struct work_struct work;
+};
+
+/*
+ * enable a dryice interrupt
+ */
+static void di_int_enable(struct imxdi_dev *imxdi, u32 intr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&imxdi->irq_lock, flags);
+ __raw_writel(__raw_readl(imxdi->ioaddr + DIER) | intr,
+ imxdi->ioaddr + DIER);
+ spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * disable a dryice interrupt
+ */
+static void di_int_disable(struct imxdi_dev *imxdi, u32 intr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&imxdi->irq_lock, flags);
+ __raw_writel(__raw_readl(imxdi->ioaddr + DIER) & ~intr,
+ imxdi->ioaddr + DIER);
+ spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * This function attempts to clear the dryice write-error flag.
+ *
+ * A dryice write error is similar to a bus fault and should not occur in
+ * normal operation. Clearing the flag requires another write, so the root
+ * cause of the problem may need to be fixed before the flag can be cleared.
+ */
+static void clear_write_error(struct imxdi_dev *imxdi)
+{
+ int cnt;
+
+ dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n");
+
+ /* clear the write error flag */
+ __raw_writel(DSR_WEF, imxdi->ioaddr + DSR);
+
+ /* wait for it to take effect */
+ for (cnt = 0; cnt < 1000; cnt++) {
+ if ((__raw_readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0)
+ return;
+ udelay(10);
+ }
+ dev_err(&imxdi->pdev->dev,
+ "ERROR: Cannot clear write-error flag!\n");
+}
+
+/*
+ * Write a dryice register and wait until it completes.
+ *
+ * This function uses interrupts to determine when the
+ * write has completed.
+ */
+static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg)
+{
+ int ret;
+ int rc = 0;
+
+ /* serialize register writes */
+ mutex_lock(&imxdi->write_mutex);
+
+ /* enable the write-complete interrupt */
+ di_int_enable(imxdi, DIER_WCIE);
+
+ imxdi->dsr = 0;
+
+ /* do the register write */
+ __raw_writel(val, imxdi->ioaddr + reg);
+
+ /* wait for the write to finish */
+ ret = wait_event_interruptible_timeout(imxdi->write_wait,
+ imxdi->dsr & (DSR_WCF | DSR_WEF), msecs_to_jiffies(1));
+ if (ret < 0) {
+ rc = ret;
+ goto out;
+ } else if (ret == 0) {
+ dev_warn(&imxdi->pdev->dev,
+ "Write-wait timeout "
+ "val = 0x%08x reg = 0x%08x\n", val, reg);
+ }
+
+ /* check for write error */
+ if (imxdi->dsr & DSR_WEF) {
+ clear_write_error(imxdi);
+ rc = -EIO;
+ }
+
+out:
+ mutex_unlock(&imxdi->write_mutex);
+
+ return rc;
+}
+
+/*
+ * read the seconds portion of the current time from the dryice time counter
+ */
+static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ unsigned long now;
+
+ now = __raw_readl(imxdi->ioaddr + DTCMR);
+ rtc_time_to_tm(now, tm);
+
+ return 0;
+}
+
+/*
+ * set the seconds portion of dryice time counter and clear the
+ * fractional part.
+ */
+static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ int rc;
+
+ /* zero the fractional part first */
+ rc = di_write_wait(imxdi, 0, DTCLR);
+ if (rc == 0)
+ rc = di_write_wait(imxdi, secs, DTCMR);
+
+ return rc;
+}
+
+static int dryice_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+
+ if (enabled)
+ di_int_enable(imxdi, DIER_CAIE);
+ else
+ di_int_disable(imxdi, DIER_CAIE);
+
+ return 0;
+}
+
+/*
+ * read the seconds portion of the alarm register.
+ * the fractional part of the alarm register is always zero.
+ */
+static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ u32 dcamr;
+
+ dcamr = __raw_readl(imxdi->ioaddr + DCAMR);
+ rtc_time_to_tm(dcamr, &alarm->time);
+
+ /* alarm is enabled if the interrupt is enabled */
+ alarm->enabled = (__raw_readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0;
+
+ /* don't allow the DSR read to mess up DSR_WCF */
+ mutex_lock(&imxdi->write_mutex);
+
+ /* alarm is pending if the alarm flag is set */
+ alarm->pending = (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) != 0;
+
+ mutex_unlock(&imxdi->write_mutex);
+
+ return 0;
+}
+
+/*
+ * set the seconds portion of dryice alarm register
+ */
+static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ unsigned long now;
+ unsigned long alarm_time;
+ int rc;
+
+ rc = rtc_tm_to_time(&alarm->time, &alarm_time);
+ if (rc)
+ return rc;
+
+ /* don't allow setting alarm in the past */
+ now = __raw_readl(imxdi->ioaddr + DTCMR);
+ if (alarm_time < now)
+ return -EINVAL;
+
+ /* write the new alarm time */
+ rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR);
+ if (rc)
+ return rc;
+
+ if (alarm->enabled)
+ di_int_enable(imxdi, DIER_CAIE); /* enable alarm intr */
+ else
+ di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */
+
+ return 0;
+}
+
+static struct rtc_class_ops dryice_rtc_ops = {
+ .read_time = dryice_rtc_read_time,
+ .set_mmss = dryice_rtc_set_mmss,
+ .alarm_irq_enable = dryice_rtc_alarm_irq_enable,
+ .read_alarm = dryice_rtc_read_alarm,
+ .set_alarm = dryice_rtc_set_alarm,
+};
+
+/*
+ * dryice "normal" interrupt handler
+ */
+static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
+{
+ struct imxdi_dev *imxdi = dev_id;
+ u32 dsr, dier;
+ irqreturn_t rc = IRQ_NONE;
+
+ dier = __raw_readl(imxdi->ioaddr + DIER);
+
+ /* handle write complete and write error cases */
+ if ((dier & DIER_WCIE)) {
+ /*If the write wait queue is empty then there is no pending
+ operations. It means the interrupt is for DryIce -Security.
+ IRQ must be returned as none.*/
+ if (list_empty_careful(&imxdi->write_wait.task_list))
+ return rc;
+
+ /* DSR_WCF clears itself on DSR read */
+ dsr = __raw_readl(imxdi->ioaddr + DSR);
+ if ((dsr & (DSR_WCF | DSR_WEF))) {
+ /* mask the interrupt */
+ di_int_disable(imxdi, DIER_WCIE);
+
+ /* save the dsr value for the wait queue */
+ imxdi->dsr |= dsr;
+
+ wake_up_interruptible(&imxdi->write_wait);
+ rc = IRQ_HANDLED;
+ }
+ }
+
+ /* handle the alarm case */
+ if ((dier & DIER_CAIE)) {
+ /* DSR_WCF clears itself on DSR read */
+ dsr = __raw_readl(imxdi->ioaddr + DSR);
+ if (dsr & DSR_CAF) {
+ /* mask the interrupt */
+ di_int_disable(imxdi, DIER_CAIE);
+
+ /* finish alarm in user context */
+ schedule_work(&imxdi->work);
+ rc = IRQ_HANDLED;
+ }
+ }
+ return rc;
+}
+
+/*
+ * post the alarm event from user context so it can sleep
+ * on the write completion.
+ */
+static void dryice_work(struct work_struct *work)
+{
+ struct imxdi_dev *imxdi = container_of(work,
+ struct imxdi_dev, work);
+
+ /* dismiss the interrupt (ignore error) */
+ di_write_wait(imxdi, DSR_CAF, DSR);
+
+ /* pass the alarm event to the rtc framework. */
+ rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF);
+}
+
+/*
+ * probe for dryice rtc device
+ */
+static int dryice_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct imxdi_dev *imxdi;
+ int rc;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ imxdi = devm_kzalloc(&pdev->dev, sizeof(*imxdi), GFP_KERNEL);
+ if (!imxdi)
+ return -ENOMEM;
+
+ imxdi->pdev = pdev;
+
+ if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
+ pdev->name))
+ return -EBUSY;
+
+ imxdi->ioaddr = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (imxdi->ioaddr == NULL)
+ return -ENOMEM;
+
+ imxdi->irq = platform_get_irq(pdev, 0);
+ if (imxdi->irq < 0)
+ return imxdi->irq;
+
+ init_waitqueue_head(&imxdi->write_wait);
+
+ INIT_WORK(&imxdi->work, dryice_work);
+
+ mutex_init(&imxdi->write_mutex);
+
+ imxdi->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(imxdi->clk))
+ return PTR_ERR(imxdi->clk);
+ clk_enable(imxdi->clk);
+
+ /*
+ * Initialize dryice hardware
+ */
+
+ /* mask all interrupts */
+ __raw_writel(0, imxdi->ioaddr + DIER);
+
+ rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq,
+ IRQF_SHARED, pdev->name, imxdi);
+ if (rc) {
+ dev_warn(&pdev->dev, "interrupt not available.\n");
+ goto err;
+ }
+
+ /* put dryice into valid state */
+ if (__raw_readl(imxdi->ioaddr + DSR) & DSR_NVF) {
+ rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR);
+ if (rc)
+ goto err;
+ }
+
+ /* initialize alarm */
+ rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR);
+ if (rc)
+ goto err;
+ rc = di_write_wait(imxdi, 0, DCALR);
+ if (rc)
+ goto err;
+
+ /* clear alarm flag */
+ if (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) {
+ rc = di_write_wait(imxdi, DSR_CAF, DSR);
+ if (rc)
+ goto err;
+ }
+
+ /* the timer won't count if it has never been written to */
+ if (__raw_readl(imxdi->ioaddr + DTCMR) == 0) {
+ rc = di_write_wait(imxdi, 0, DTCMR);
+ if (rc)
+ goto err;
+ }
+
+ /* start keeping time */
+ if (!(__raw_readl(imxdi->ioaddr + DCR) & DCR_TCE)) {
+ rc = di_write_wait(imxdi,
+ __raw_readl(imxdi->ioaddr + DCR) | DCR_TCE,
+ DCR);
+ if (rc)
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, imxdi);
+ imxdi->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &dryice_rtc_ops, THIS_MODULE);
+ if (IS_ERR(imxdi->rtc)) {
+ rc = PTR_ERR(imxdi->rtc);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ clk_disable(imxdi->clk);
+ clk_put(imxdi->clk);
+
+ return rc;
+}
+
+static int __devexit dryice_rtc_remove(struct platform_device *pdev)
+{
+ struct imxdi_dev *imxdi = platform_get_drvdata(pdev);
+
+ flush_work(&imxdi->work);
+
+ /* mask all interrupts */
+ __raw_writel(0, imxdi->ioaddr + DIER);
+
+ rtc_device_unregister(imxdi->rtc);
+
+ clk_disable(imxdi->clk);
+ clk_put(imxdi->clk);
+
+ return 0;
+}
+
+static struct platform_driver dryice_rtc_driver = {
+ .driver = {
+ .name = "imxdi_rtc",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(dryice_rtc_remove),
+};
+
+static int __init dryice_rtc_init(void)
+{
+ return platform_driver_probe(&dryice_rtc_driver, dryice_rtc_probe);
+}
+
+static void __exit dryice_rtc_exit(void)
+{
+ platform_driver_unregister(&dryice_rtc_driver);
+}
+
+module_init(dryice_rtc_init);
+module_exit(dryice_rtc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("IMX DryIce Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c
new file mode 100644
index 000000000000..ddbc797ea6cd
--- /dev/null
+++ b/drivers/rtc/rtc-isl12022.c
@@ -0,0 +1,327 @@
+/*
+ * An I2C driver for the Intersil ISL 12022
+ *
+ * Author: Roman Fietze <roman.fietze@telemotive.de>
+ *
+ * Based on the Philips PCF8563 RTC
+ * by Alessandro Zummo <a.zummo@towertech.it>.
+ *
+ * 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/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+#define DRV_VERSION "0.1"
+
+/* ISL register offsets */
+#define ISL12022_REG_SC 0x00
+#define ISL12022_REG_MN 0x01
+#define ISL12022_REG_HR 0x02
+#define ISL12022_REG_DT 0x03
+#define ISL12022_REG_MO 0x04
+#define ISL12022_REG_YR 0x05
+#define ISL12022_REG_DW 0x06
+
+#define ISL12022_REG_SR 0x07
+#define ISL12022_REG_INT 0x08
+
+/* ISL register bits */
+#define ISL12022_HR_MIL (1 << 7) /* military or 24 hour time */
+
+#define ISL12022_SR_LBAT85 (1 << 2)
+#define ISL12022_SR_LBAT75 (1 << 1)
+
+#define ISL12022_INT_WRTC (1 << 6)
+
+
+static struct i2c_driver isl12022_driver;
+
+struct isl12022 {
+ struct rtc_device *rtc;
+
+ bool write_enabled; /* true if write enable is set */
+};
+
+
+static int isl12022_read_regs(struct i2c_client *client, uint8_t reg,
+ uint8_t *data, size_t n)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = data
+ }, /* setup read ptr */
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = n,
+ .buf = data
+ }
+ };
+
+ int ret;
+
+ data[0] = reg;
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs)) {
+ dev_err(&client->dev, "%s: read error, ret=%d\n",
+ __func__, ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+static int isl12022_write_reg(struct i2c_client *client,
+ uint8_t reg, uint8_t val)
+{
+ uint8_t data[2] = { reg, val };
+ int err;
+
+ err = i2c_master_send(client, data, sizeof(data));
+ if (err != sizeof(data)) {
+ dev_err(&client->dev,
+ "%s: err=%d addr=%02x, data=%02x\n",
+ __func__, err, data[0], data[1]);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+/*
+ * In the routines that deal directly with the isl12022 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
+ */
+static int isl12022_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ uint8_t buf[ISL12022_REG_INT + 1];
+ int ret;
+
+ ret = isl12022_read_regs(client, ISL12022_REG_SC, buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ if (buf[ISL12022_REG_SR] & (ISL12022_SR_LBAT85 | ISL12022_SR_LBAT75)) {
+ dev_warn(&client->dev,
+ "voltage dropped below %u%%, "
+ "date and time is not reliable.\n",
+ buf[ISL12022_REG_SR] & ISL12022_SR_LBAT85 ? 85 : 75);
+ }
+
+ dev_dbg(&client->dev,
+ "%s: raw data is sec=%02x, min=%02x, hr=%02x, "
+ "mday=%02x, mon=%02x, year=%02x, wday=%02x, "
+ "sr=%02x, int=%02x",
+ __func__,
+ buf[ISL12022_REG_SC],
+ buf[ISL12022_REG_MN],
+ buf[ISL12022_REG_HR],
+ buf[ISL12022_REG_DT],
+ buf[ISL12022_REG_MO],
+ buf[ISL12022_REG_YR],
+ buf[ISL12022_REG_DW],
+ buf[ISL12022_REG_SR],
+ buf[ISL12022_REG_INT]);
+
+ tm->tm_sec = bcd2bin(buf[ISL12022_REG_SC] & 0x7F);
+ tm->tm_min = bcd2bin(buf[ISL12022_REG_MN] & 0x7F);
+ tm->tm_hour = bcd2bin(buf[ISL12022_REG_HR] & 0x3F);
+ tm->tm_mday = bcd2bin(buf[ISL12022_REG_DT] & 0x3F);
+ tm->tm_wday = buf[ISL12022_REG_DW] & 0x07;
+ tm->tm_mon = bcd2bin(buf[ISL12022_REG_MO] & 0x1F) - 1;
+ tm->tm_year = bcd2bin(buf[ISL12022_REG_YR]) + 100;
+
+ dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ /* The clock can give out invalid datetime, but we cannot return
+ * -EINVAL otherwise hwclock will refuse to set the time on bootup. */
+ if (rtc_valid_tm(tm) < 0)
+ dev_err(&client->dev, "retrieved date and time is invalid.\n");
+
+ return 0;
+}
+
+static int isl12022_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct isl12022 *isl12022 = i2c_get_clientdata(client);
+ size_t i;
+ int ret;
+ uint8_t buf[ISL12022_REG_DW + 1];
+
+ dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ if (!isl12022->write_enabled) {
+
+ ret = isl12022_read_regs(client, ISL12022_REG_INT, buf, 1);
+ if (ret)
+ return ret;
+
+ /* Check if WRTC (write rtc enable) is set factory default is
+ * 0 (not set) */
+ if (!(buf[0] & ISL12022_INT_WRTC)) {
+ dev_info(&client->dev,
+ "init write enable and 24 hour format\n");
+
+ /* Set the write enable bit. */
+ ret = isl12022_write_reg(client,
+ ISL12022_REG_INT,
+ buf[0] | ISL12022_INT_WRTC);
+ if (ret)
+ return ret;
+
+ /* Write to any RTC register to start RTC, we use the
+ * HR register, setting the MIL bit to use the 24 hour
+ * format. */
+ ret = isl12022_read_regs(client, ISL12022_REG_HR,
+ buf, 1);
+ if (ret)
+ return ret;
+
+ ret = isl12022_write_reg(client,
+ ISL12022_REG_HR,
+ buf[0] | ISL12022_HR_MIL);
+ if (ret)
+ return ret;
+ }
+
+ isl12022->write_enabled = 1;
+ }
+
+ /* hours, minutes and seconds */
+ buf[ISL12022_REG_SC] = bin2bcd(tm->tm_sec);
+ buf[ISL12022_REG_MN] = bin2bcd(tm->tm_min);
+ buf[ISL12022_REG_HR] = bin2bcd(tm->tm_hour) | ISL12022_HR_MIL;
+
+ buf[ISL12022_REG_DT] = bin2bcd(tm->tm_mday);
+
+ /* month, 1 - 12 */
+ buf[ISL12022_REG_MO] = bin2bcd(tm->tm_mon + 1);
+
+ /* year and century */
+ buf[ISL12022_REG_YR] = bin2bcd(tm->tm_year % 100);
+
+ buf[ISL12022_REG_DW] = tm->tm_wday & 0x07;
+
+ /* write register's data */
+ for (i = 0; i < ARRAY_SIZE(buf); i++) {
+ ret = isl12022_write_reg(client, ISL12022_REG_SC + i,
+ buf[ISL12022_REG_SC + i]);
+ if (ret)
+ return -EIO;
+ };
+
+ return 0;
+}
+
+static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return isl12022_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return isl12022_set_datetime(to_i2c_client(dev), tm);
+}
+
+static const struct rtc_class_ops isl12022_rtc_ops = {
+ .read_time = isl12022_rtc_read_time,
+ .set_time = isl12022_rtc_set_time,
+};
+
+static int isl12022_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct isl12022 *isl12022;
+
+ int ret = 0;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ isl12022 = kzalloc(sizeof(struct isl12022), GFP_KERNEL);
+ if (!isl12022)
+ return -ENOMEM;
+
+ dev_dbg(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+ i2c_set_clientdata(client, isl12022);
+
+ isl12022->rtc = rtc_device_register(isl12022_driver.driver.name,
+ &client->dev,
+ &isl12022_rtc_ops,
+ THIS_MODULE);
+
+ if (IS_ERR(isl12022->rtc)) {
+ ret = PTR_ERR(isl12022->rtc);
+ goto exit_kfree;
+ }
+
+ return 0;
+
+exit_kfree:
+ kfree(isl12022);
+
+ return ret;
+}
+
+static int isl12022_remove(struct i2c_client *client)
+{
+ struct isl12022 *isl12022 = i2c_get_clientdata(client);
+
+ rtc_device_unregister(isl12022->rtc);
+ kfree(isl12022);
+
+ return 0;
+}
+
+static const struct i2c_device_id isl12022_id[] = {
+ { "isl12022", 0 },
+ { "rtc8564", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, isl12022_id);
+
+static struct i2c_driver isl12022_driver = {
+ .driver = {
+ .name = "rtc-isl12022",
+ },
+ .probe = isl12022_probe,
+ .remove = isl12022_remove,
+ .id_table = isl12022_id,
+};
+
+static int __init isl12022_init(void)
+{
+ return i2c_add_driver(&isl12022_driver);
+}
+
+static void __exit isl12022_exit(void)
+{
+ i2c_del_driver(&isl12022_driver);
+}
+
+module_init(isl12022_init);
+module_exit(isl12022_exit);
+
+MODULE_AUTHOR("roman.fietze@telemotive.de");
+MODULE_DESCRIPTION("ISL 12022 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 6dc4e6241418..66377f3e28b8 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -121,7 +121,7 @@ static int m41t80_get_datetime(struct i2c_client *client,
/* assume 20YY not 19YY, and ignore the Century Bit */
tm->tm_year = bcd2bin(buf[M41T80_REG_YEAR]) + 100;
- return 0;
+ return rtc_valid_tm(tm);
}
/* Sets the given date and time to the real time clock. */
@@ -364,7 +364,7 @@ static int m41t80_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *t)
t->time.tm_isdst = -1;
t->enabled = !!(reg[M41T80_REG_ALARM_MON] & M41T80_ALMON_AFE);
t->pending = !!(reg[M41T80_REG_FLAGS] & M41T80_FLAGS_AF);
- return 0;
+ return rtc_valid_tm(t);
}
static struct rtc_class_ops m41t80_rtc_ops = {
diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c
index be8359fdb65a..a99a0b554eb8 100644
--- a/drivers/rtc/rtc-m48t59.c
+++ b/drivers/rtc/rtc-m48t59.c
@@ -105,7 +105,7 @@ static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm)
dev_dbg(dev, "RTC read time %04d-%02d-%02d %02d/%02d/%02d\n",
tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
- return 0;
+ return rtc_valid_tm(tm);
}
static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -196,7 +196,7 @@ static int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
dev_dbg(dev, "RTC read alarm time %04d-%02d-%02d %02d/%02d/%02d\n",
tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
- return 0;
+ return rtc_valid_tm(tm);
}
/*
@@ -506,7 +506,6 @@ out:
free_irq(m48t59->irq, &pdev->dev);
if (m48t59->ioaddr)
iounmap(m48t59->ioaddr);
- if (m48t59)
kfree(m48t59);
return ret;
}
diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c
index 7c045cffa9ff..f981287d582b 100644
--- a/drivers/rtc/rtc-m48t86.c
+++ b/drivers/rtc/rtc-m48t86.c
@@ -77,7 +77,7 @@ static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm)
if (ops->readbyte(M48T86_REG_HOUR) & 0x80)
tm->tm_hour += 12;
- return 0;
+ return rtc_valid_tm(tm);
}
static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c
index a4f6665ab3c5..486142c2637a 100644
--- a/drivers/rtc/rtc-max6900.c
+++ b/drivers/rtc/rtc-max6900.c
@@ -159,7 +159,7 @@ static int max6900_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
bcd2bin(regs[MAX6900_REG_CENTURY]) * 100 - 1900;
tm->tm_wday = bcd2bin(regs[MAX6900_REG_DW]);
- return 0;
+ return rtc_valid_tm(tm);
}
static int max6900_i2c_clear_write_protect(struct i2c_client *client)
diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c
index db5d8c416d26..dfcdf0901d21 100644
--- a/drivers/rtc/rtc-mpc5121.c
+++ b/drivers/rtc/rtc-mpc5121.c
@@ -268,7 +268,7 @@ static const struct rtc_class_ops mpc5121_rtc_ops = {
.update_irq_enable = mpc5121_rtc_update_irq_enable,
};
-static int __devinit mpc5121_rtc_probe(struct of_device *op,
+static int __devinit mpc5121_rtc_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct mpc5121_rtc_data *rtc;
@@ -338,7 +338,7 @@ out_free:
return err;
}
-static int __devexit mpc5121_rtc_remove(struct of_device *op)
+static int __devexit mpc5121_rtc_remove(struct platform_device *op)
{
struct mpc5121_rtc_data *rtc = dev_get_drvdata(&op->dev);
struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c
index 25ec921db07c..0b06c1e03fd5 100644
--- a/drivers/rtc/rtc-mxc.c
+++ b/drivers/rtc/rtc-mxc.c
@@ -83,12 +83,6 @@ struct rtc_plat_data {
void __iomem *ioaddr;
int irq;
struct clk *clk;
- unsigned int irqen;
- int alrm_sec;
- int alrm_min;
- int alrm_hour;
- int alrm_mday;
- struct timespec mxc_rtc_delta;
struct rtc_time g_rtc_alarm;
};
diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c
index a351bd5d8176..62de66af0a68 100644
--- a/drivers/rtc/rtc-nuc900.c
+++ b/drivers/rtc/rtc-nuc900.c
@@ -85,25 +85,24 @@ static irqreturn_t nuc900_rtc_interrupt(int irq, void *_rtc)
static int *check_rtc_access_enable(struct nuc900_rtc *nuc900_rtc)
{
- unsigned int i;
+ unsigned int timeout = 0x1000;
__raw_writel(INIRRESET, nuc900_rtc->rtc_reg + REG_RTC_INIR);
mdelay(10);
__raw_writel(AERPOWERON, nuc900_rtc->rtc_reg + REG_RTC_AER);
- for (i = 0; i < 1000; i++) {
- if (__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
- return 0;
- }
+ while (!(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
+ && timeout--)
+ mdelay(1);
- if ((__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB) == 0x0)
- return ERR_PTR(-ENODEV);
+ if (!timeout)
+ return ERR_PTR(-EPERM);
- return ERR_PTR(-EPERM);
+ return 0;
}
-static void nuc900_rtc_bcd2bin(unsigned int timereg,
+static int nuc900_rtc_bcd2bin(unsigned int timereg,
unsigned int calreg, struct rtc_time *tm)
{
tm->tm_mday = bcd2bin(calreg >> 0);
@@ -114,15 +113,21 @@ static void nuc900_rtc_bcd2bin(unsigned int timereg,
tm->tm_min = bcd2bin(timereg >> 8);
tm->tm_hour = bcd2bin(timereg >> 16);
- rtc_valid_tm(tm);
+ return rtc_valid_tm(tm);
}
-static void nuc900_rtc_bin2bcd(struct rtc_time *settm,
+static void nuc900_rtc_bin2bcd(struct device *dev, struct rtc_time *settm,
struct nuc900_bcd_time *gettm)
{
gettm->bcd_mday = bin2bcd(settm->tm_mday) << 0;
gettm->bcd_mon = bin2bcd(settm->tm_mon) << 8;
- gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16;
+
+ if (settm->tm_year < 100) {
+ dev_warn(dev, "The year will be between 1970-1999, right?\n");
+ gettm->bcd_year = bin2bcd(settm->tm_year) << 16;
+ } else {
+ gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16;
+ }
gettm->bcd_sec = bin2bcd(settm->tm_sec) << 0;
gettm->bcd_min = bin2bcd(settm->tm_min) << 8;
@@ -165,9 +170,7 @@ static int nuc900_rtc_read_time(struct device *dev, struct rtc_time *tm)
timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TLR);
clrval = __raw_readl(rtc->rtc_reg + REG_RTC_CLR);
- nuc900_rtc_bcd2bin(timeval, clrval, tm);
-
- return 0;
+ return nuc900_rtc_bcd2bin(timeval, clrval, tm);
}
static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm)
@@ -177,7 +180,7 @@ static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm)
unsigned long val;
int *err;
- nuc900_rtc_bin2bcd(tm, &gettm);
+ nuc900_rtc_bin2bcd(dev, tm, &gettm);
err = check_rtc_access_enable(rtc);
if (IS_ERR(err))
@@ -200,9 +203,7 @@ static int nuc900_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TAR);
carval = __raw_readl(rtc->rtc_reg + REG_RTC_CAR);
- nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
-
- return 0;
+ return nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
}
static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
@@ -212,7 +213,7 @@ static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
unsigned long val;
int *err;
- nuc900_rtc_bin2bcd(&alrm->time, &tm);
+ nuc900_rtc_bin2bcd(dev, &alrm->time, &tm);
err = check_rtc_access_enable(rtc);
if (IS_ERR(err))
@@ -268,29 +269,30 @@ static int __devinit nuc900_rtc_probe(struct platform_device *pdev)
goto fail2;
}
- nuc900_rtc->irq_num = platform_get_irq(pdev, 0);
- if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt,
- IRQF_DISABLED, "nuc900rtc", nuc900_rtc)) {
- dev_err(&pdev->dev, "NUC900 RTC request irq failed\n");
- err = -EBUSY;
- goto fail3;
- }
+ platform_set_drvdata(pdev, nuc900_rtc);
nuc900_rtc->rtcdev = rtc_device_register(pdev->name, &pdev->dev,
&nuc900_rtc_ops, THIS_MODULE);
if (IS_ERR(nuc900_rtc->rtcdev)) {
dev_err(&pdev->dev, "rtc device register faild\n");
err = PTR_ERR(nuc900_rtc->rtcdev);
- goto fail4;
+ goto fail3;
}
- platform_set_drvdata(pdev, nuc900_rtc);
__raw_writel(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_TSSR) | MODE24,
nuc900_rtc->rtc_reg + REG_RTC_TSSR);
+ nuc900_rtc->irq_num = platform_get_irq(pdev, 0);
+ if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt,
+ IRQF_DISABLED, "nuc900rtc", nuc900_rtc)) {
+ dev_err(&pdev->dev, "NUC900 RTC request irq failed\n");
+ err = -EBUSY;
+ goto fail4;
+ }
+
return 0;
-fail4: free_irq(nuc900_rtc->irq_num, nuc900_rtc);
+fail4: rtc_device_unregister(nuc900_rtc->rtcdev);
fail3: iounmap(nuc900_rtc->rtc_reg);
fail2: release_mem_region(res->start, resource_size(res));
fail1: kfree(nuc900_rtc);
@@ -302,8 +304,8 @@ static int __devexit nuc900_rtc_remove(struct platform_device *pdev)
struct nuc900_rtc *nuc900_rtc = platform_get_drvdata(pdev);
struct resource *res;
- rtc_device_unregister(nuc900_rtc->rtcdev);
free_irq(nuc900_rtc->irq_num, nuc900_rtc);
+ rtc_device_unregister(nuc900_rtc->rtcdev);
iounmap(nuc900_rtc->rtc_reg);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index 1af42b4a6f59..b42c0c679266 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -172,14 +172,6 @@ static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
return 0;
}
-struct pcf8563_limit
-{
- unsigned char reg;
- unsigned char mask;
- unsigned char min;
- unsigned char max;
-};
-
static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
return pcf8563_get_datetime(to_i2c_client(dev), tm);
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index 71bbefc3544e..6c418fe7f288 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -23,7 +23,6 @@
#include <linux/io.h>
#include <linux/bcd.h>
#include <linux/delay.h>
-#include <linux/version.h>
#include <linux/slab.h>
/*
diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c
index e9c6fa035989..29e867a1aaa8 100644
--- a/drivers/rtc/rtc-pxa.c
+++ b/drivers/rtc/rtc-pxa.c
@@ -87,7 +87,6 @@ struct pxa_rtc {
int irq_Alrm;
struct rtc_device *rtc;
spinlock_t lock; /* Protects this structure */
- struct rtc_time rtc_alarm;
};
static u32 ryxr_calc(struct rtc_time *tm)
@@ -236,32 +235,34 @@ static int pxa_periodic_irq_set_state(struct device *dev, int enabled)
return 0;
}
-static int pxa_rtc_ioctl(struct device *dev, unsigned int cmd,
- unsigned long arg)
+static int pxa_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
- int ret = 0;
spin_lock_irq(&pxa_rtc->lock);
- switch (cmd) {
- case RTC_AIE_OFF:
- rtsr_clear_bits(pxa_rtc, RTSR_RDALE1);
- break;
- case RTC_AIE_ON:
+
+ if (enabled)
rtsr_set_bits(pxa_rtc, RTSR_RDALE1);
- break;
- case RTC_UIE_OFF:
- rtsr_clear_bits(pxa_rtc, RTSR_HZE);
- break;
- case RTC_UIE_ON:
+ else
+ rtsr_clear_bits(pxa_rtc, RTSR_RDALE1);
+
+ spin_unlock_irq(&pxa_rtc->lock);
+ return 0;
+}
+
+static int pxa_update_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
+
+ spin_lock_irq(&pxa_rtc->lock);
+
+ if (enabled)
rtsr_set_bits(pxa_rtc, RTSR_HZE);
- break;
- default:
- ret = -ENOIOCTLCMD;
- }
+ else
+ rtsr_clear_bits(pxa_rtc, RTSR_HZE);
spin_unlock_irq(&pxa_rtc->lock);
- return ret;
+ return 0;
}
static int pxa_rtc_read_time(struct device *dev, struct rtc_time *tm)
@@ -340,11 +341,12 @@ static int pxa_rtc_proc(struct device *dev, struct seq_file *seq)
static const struct rtc_class_ops pxa_rtc_ops = {
.open = pxa_rtc_open,
.release = pxa_rtc_release,
- .ioctl = pxa_rtc_ioctl,
.read_time = pxa_rtc_read_time,
.set_time = pxa_rtc_set_time,
.read_alarm = pxa_rtc_read_alarm,
.set_alarm = pxa_rtc_set_alarm,
+ .alarm_irq_enable = pxa_alarm_irq_enable,
+ .update_irq_enable = pxa_update_irq_enable,
.proc = pxa_rtc_proc,
.irq_set_state = pxa_periodic_irq_set_state,
.irq_set_freq = pxa_periodic_irq_set_freq,
diff --git a/drivers/rtc/rtc-rp5c01.c b/drivers/rtc/rtc-rp5c01.c
index a95f733bb15a..36eb66184461 100644
--- a/drivers/rtc/rtc-rp5c01.c
+++ b/drivers/rtc/rtc-rp5c01.c
@@ -63,6 +63,8 @@ enum {
struct rp5c01_priv {
u32 __iomem *regs;
struct rtc_device *rtc;
+ spinlock_t lock; /* against concurrent RTC/NVRAM access */
+ struct bin_attribute nvram_attr;
};
static inline unsigned int rp5c01_read(struct rp5c01_priv *priv,
@@ -92,6 +94,7 @@ static int rp5c01_read_time(struct device *dev, struct rtc_time *tm)
{
struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ spin_lock_irq(&priv->lock);
rp5c01_lock(priv);
tm->tm_sec = rp5c01_read(priv, RP5C01_10_SECOND) * 10 +
@@ -111,6 +114,7 @@ static int rp5c01_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_year += 100;
rp5c01_unlock(priv);
+ spin_unlock_irq(&priv->lock);
return rtc_valid_tm(tm);
}
@@ -119,6 +123,7 @@ static int rp5c01_set_time(struct device *dev, struct rtc_time *tm)
{
struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ spin_lock_irq(&priv->lock);
rp5c01_lock(priv);
rp5c01_write(priv, tm->tm_sec / 10, RP5C01_10_SECOND);
@@ -139,6 +144,7 @@ static int rp5c01_set_time(struct device *dev, struct rtc_time *tm)
rp5c01_write(priv, tm->tm_year % 10, RP5C01_1_YEAR);
rp5c01_unlock(priv);
+ spin_unlock_irq(&priv->lock);
return 0;
}
@@ -147,6 +153,72 @@ static const struct rtc_class_ops rp5c01_rtc_ops = {
.set_time = rp5c01_set_time,
};
+
+/*
+ * The NVRAM is organized as 2 blocks of 13 nibbles of 4 bits.
+ * We provide access to them like AmigaOS does: the high nibble of each 8-bit
+ * byte is stored in BLOCK10, the low nibble in BLOCK11.
+ */
+
+static ssize_t rp5c01_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ ssize_t count;
+
+ spin_lock_irq(&priv->lock);
+
+ for (count = 0; size > 0 && pos < RP5C01_MODE; count++, size--) {
+ u8 data;
+
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10,
+ RP5C01_MODE);
+ data = rp5c01_read(priv, pos) << 4;
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11,
+ RP5C01_MODE);
+ data |= rp5c01_read(priv, pos++);
+ rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01,
+ RP5C01_MODE);
+ *buf++ = data;
+ }
+
+ spin_unlock_irq(&priv->lock);
+ return count;
+}
+
+static ssize_t rp5c01_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ ssize_t count;
+
+ spin_lock_irq(&priv->lock);
+
+ for (count = 0; size > 0 && pos < RP5C01_MODE; count++, size--) {
+ u8 data = *buf++;
+
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10,
+ RP5C01_MODE);
+ rp5c01_write(priv, data >> 4, pos);
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11,
+ RP5C01_MODE);
+ rp5c01_write(priv, data & 0xf, pos++);
+ rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01,
+ RP5C01_MODE);
+ }
+
+ spin_unlock_irq(&priv->lock);
+ return count;
+}
+
static int __init rp5c01_rtc_probe(struct platform_device *dev)
{
struct resource *res;
@@ -168,6 +240,15 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev)
goto out_free_priv;
}
+ sysfs_bin_attr_init(&priv->nvram_attr);
+ priv->nvram_attr.attr.name = "nvram";
+ priv->nvram_attr.attr.mode = S_IRUGO | S_IWUSR;
+ priv->nvram_attr.read = rp5c01_nvram_read;
+ priv->nvram_attr.write = rp5c01_nvram_write;
+ priv->nvram_attr.size = RP5C01_MODE;
+
+ spin_lock_init(&priv->lock);
+
rtc = rtc_device_register("rtc-rp5c01", &dev->dev, &rp5c01_rtc_ops,
THIS_MODULE);
if (IS_ERR(rtc)) {
@@ -177,8 +258,15 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev)
priv->rtc = rtc;
platform_set_drvdata(dev, priv);
+
+ error = sysfs_create_bin_file(&dev->dev.kobj, &priv->nvram_attr);
+ if (error)
+ goto out_unregister;
+
return 0;
+out_unregister:
+ rtc_device_unregister(rtc);
out_unmap:
iounmap(priv->regs);
out_free_priv:
@@ -190,6 +278,7 @@ static int __exit rp5c01_rtc_remove(struct platform_device *dev)
{
struct rp5c01_priv *priv = platform_get_drvdata(dev);
+ sysfs_remove_bin_file(&dev->dev.kobj, &priv->nvram_attr);
rtc_device_unregister(priv->rtc);
iounmap(priv->regs);
kfree(priv);
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 70b68d35f969..a0d3ec89d412 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -1,5 +1,8 @@
/* drivers/rtc/rtc-s3c.c
*
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
* Copyright (c) 2004,2006 Simtec Electronics
* Ben Dooks, <ben@simtec.co.uk>
* http://armlinux.simtec.co.uk/
@@ -39,6 +42,7 @@ enum s3c_cpu_type {
static struct resource *s3c_rtc_mem;
+static struct clk *rtc_clk;
static void __iomem *s3c_rtc_base;
static int s3c_rtc_alarmno = NO_IRQ;
static int s3c_rtc_tickno = NO_IRQ;
@@ -53,6 +57,10 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
struct rtc_device *rdev = id;
rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
+
+ if (s3c_rtc_cpu_type == TYPE_S3C64XX)
+ writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP);
+
return IRQ_HANDLED;
}
@@ -61,6 +69,10 @@ static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
struct rtc_device *rdev = id;
rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
+
+ if (s3c_rtc_cpu_type == TYPE_S3C64XX)
+ writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP);
+
return IRQ_HANDLED;
}
@@ -94,7 +106,7 @@ static int s3c_rtc_setpie(struct device *dev, int enabled)
if (enabled)
tmp |= S3C64XX_RTCCON_TICEN;
- writeb(tmp, s3c_rtc_base + S3C2410_RTCCON);
+ writew(tmp, s3c_rtc_base + S3C2410_RTCCON);
} else {
tmp = readb(s3c_rtc_base + S3C2410_TICNT);
tmp &= ~S3C2410_TICNT_ENABLE;
@@ -128,7 +140,7 @@ static int s3c_rtc_setfreq(struct device *dev, int freq)
tmp |= (rtc_dev->max_user_freq / freq)-1;
- writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
+ writel(tmp, s3c_rtc_base + S3C2410_TICNT);
spin_unlock_irq(&s3c_rtc_pie_lock);
return 0;
@@ -431,6 +443,10 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev)
s3c_rtc_setpie(&dev->dev, 0);
s3c_rtc_setaie(0);
+ clk_disable(rtc_clk);
+ clk_put(rtc_clk);
+ rtc_clk = NULL;
+
iounmap(s3c_rtc_base);
release_resource(s3c_rtc_mem);
kfree(s3c_rtc_mem);
@@ -442,6 +458,7 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
struct resource *res;
+ unsigned int tmp, i;
int ret;
pr_debug("%s: probe=%p\n", __func__, pdev);
@@ -488,6 +505,16 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
goto err_nomap;
}
+ rtc_clk = clk_get(&pdev->dev, "rtc");
+ if (IS_ERR(rtc_clk)) {
+ dev_err(&pdev->dev, "failed to find rtc clock source\n");
+ ret = PTR_ERR(rtc_clk);
+ rtc_clk = NULL;
+ goto err_clk;
+ }
+
+ clk_enable(rtc_clk);
+
/* check to see if everything is setup correctly */
s3c_rtc_enable(pdev, 1);
@@ -510,6 +537,15 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data;
+ /* Check RTC Time */
+
+ for (i = S3C2410_RTCSEC; i <= S3C2410_RTCYEAR; i += 0x4) {
+ tmp = readb(s3c_rtc_base + i);
+
+ if ((tmp & 0xf) > 0x9 || ((tmp >> 4) & 0xf) > 0x9)
+ writeb(0, s3c_rtc_base + i);
+ }
+
if (s3c_rtc_cpu_type == TYPE_S3C64XX)
rtc->max_user_freq = 32768;
else
@@ -523,6 +559,10 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
err_nortc:
s3c_rtc_enable(pdev, 0);
+ clk_disable(rtc_clk);
+ clk_put(rtc_clk);
+
+ err_clk:
iounmap(s3c_rtc_base);
err_nomap:
diff --git a/drivers/sbus/char/bbc_envctrl.c b/drivers/sbus/char/bbc_envctrl.c
index 103fdf6b0b89..160e7510aca6 100644
--- a/drivers/sbus/char/bbc_envctrl.c
+++ b/drivers/sbus/char/bbc_envctrl.c
@@ -443,7 +443,7 @@ static int kenvctrld(void *__unused)
return 0;
}
-static void attach_one_temp(struct bbc_i2c_bus *bp, struct of_device *op,
+static void attach_one_temp(struct bbc_i2c_bus *bp, struct platform_device *op,
int temp_idx)
{
struct bbc_cpu_temperature *tp;
@@ -488,7 +488,7 @@ static void attach_one_temp(struct bbc_i2c_bus *bp, struct of_device *op,
tp->fan_todo[FAN_CPU] = FAN_SAME;
}
-static void attach_one_fan(struct bbc_i2c_bus *bp, struct of_device *op,
+static void attach_one_fan(struct bbc_i2c_bus *bp, struct platform_device *op,
int fan_idx)
{
struct bbc_fan_control *fp;
@@ -559,7 +559,7 @@ static void destroy_all_fans(struct bbc_i2c_bus *bp)
int bbc_envctrl_init(struct bbc_i2c_bus *bp)
{
- struct of_device *op;
+ struct platform_device *op;
int temp_index = 0;
int fan_index = 0;
int devidx = 0;
diff --git a/drivers/sbus/char/bbc_i2c.c b/drivers/sbus/char/bbc_i2c.c
index 3e89c313e98d..614a5e114a19 100644
--- a/drivers/sbus/char/bbc_i2c.c
+++ b/drivers/sbus/char/bbc_i2c.c
@@ -51,7 +51,7 @@
* The second controller also connects to the smartcard reader, if present.
*/
-static void set_device_claimage(struct bbc_i2c_bus *bp, struct of_device *op, int val)
+static void set_device_claimage(struct bbc_i2c_bus *bp, struct platform_device *op, int val)
{
int i;
@@ -66,9 +66,9 @@ static void set_device_claimage(struct bbc_i2c_bus *bp, struct of_device *op, in
#define claim_device(BP,ECHILD) set_device_claimage(BP,ECHILD,1)
#define release_device(BP,ECHILD) set_device_claimage(BP,ECHILD,0)
-struct of_device *bbc_i2c_getdev(struct bbc_i2c_bus *bp, int index)
+struct platform_device *bbc_i2c_getdev(struct bbc_i2c_bus *bp, int index)
{
- struct of_device *op = NULL;
+ struct platform_device *op = NULL;
int curidx = 0, i;
for (i = 0; i < NUM_CHILDREN; i++) {
@@ -86,7 +86,7 @@ out:
return NULL;
}
-struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct of_device *op)
+struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct platform_device *op)
{
struct bbc_i2c_client *client;
const u32 *reg;
@@ -114,7 +114,7 @@ struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct of_device *
void bbc_i2c_detach(struct bbc_i2c_client *client)
{
struct bbc_i2c_bus *bp = client->bp;
- struct of_device *op = client->op;
+ struct platform_device *op = client->op;
release_device(bp, op);
kfree(client);
@@ -297,7 +297,7 @@ static void __init reset_one_i2c(struct bbc_i2c_bus *bp)
writeb(I2C_PCF_IDLE, bp->i2c_control_regs + 0x0);
}
-static struct bbc_i2c_bus * __init attach_one_i2c(struct of_device *op, int index)
+static struct bbc_i2c_bus * __init attach_one_i2c(struct platform_device *op, int index)
{
struct bbc_i2c_bus *bp;
struct device_node *dp;
@@ -330,7 +330,7 @@ static struct bbc_i2c_bus * __init attach_one_i2c(struct of_device *op, int inde
for (dp = op->dev.of_node->child;
dp && entry < 8;
dp = dp->sibling, entry++) {
- struct of_device *child_op;
+ struct platform_device *child_op;
child_op = of_find_device_by_node(dp);
bp->devs[entry].device = child_op;
@@ -361,7 +361,7 @@ fail:
extern int bbc_envctrl_init(struct bbc_i2c_bus *bp);
extern void bbc_envctrl_cleanup(struct bbc_i2c_bus *bp);
-static int __devinit bbc_i2c_probe(struct of_device *op,
+static int __devinit bbc_i2c_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct bbc_i2c_bus *bp;
@@ -386,7 +386,7 @@ static int __devinit bbc_i2c_probe(struct of_device *op,
return err;
}
-static int __devexit bbc_i2c_remove(struct of_device *op)
+static int __devexit bbc_i2c_remove(struct platform_device *op)
{
struct bbc_i2c_bus *bp = dev_get_drvdata(&op->dev);
diff --git a/drivers/sbus/char/bbc_i2c.h b/drivers/sbus/char/bbc_i2c.h
index 83c4811b7b5e..4b4531066e75 100644
--- a/drivers/sbus/char/bbc_i2c.h
+++ b/drivers/sbus/char/bbc_i2c.h
@@ -7,7 +7,7 @@
struct bbc_i2c_client {
struct bbc_i2c_bus *bp;
- struct of_device *op;
+ struct platform_device *op;
int bus;
int address;
};
@@ -64,16 +64,16 @@ struct bbc_i2c_bus {
struct list_head temps;
struct list_head fans;
- struct of_device *op;
+ struct platform_device *op;
struct {
- struct of_device *device;
+ struct platform_device *device;
int client_claimed;
} devs[NUM_CHILDREN];
};
/* Probing and attachment. */
-extern struct of_device *bbc_i2c_getdev(struct bbc_i2c_bus *, int);
-extern struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct of_device *);
+extern struct platform_device *bbc_i2c_getdev(struct bbc_i2c_bus *, int);
+extern struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct platform_device *);
extern void bbc_i2c_detach(struct bbc_i2c_client *);
/* Register read/write. NOTE: Blocking! */
diff --git a/drivers/sbus/char/display7seg.c b/drivers/sbus/char/display7seg.c
index 47db97583ea7..1690e53fb84a 100644
--- a/drivers/sbus/char/display7seg.c
+++ b/drivers/sbus/char/display7seg.c
@@ -170,7 +170,7 @@ static struct miscdevice d7s_miscdev = {
.fops = &d7s_fops
};
-static int __devinit d7s_probe(struct of_device *op,
+static int __devinit d7s_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *opts;
@@ -236,7 +236,7 @@ out_free:
goto out;
}
-static int __devexit d7s_remove(struct of_device *op)
+static int __devexit d7s_remove(struct platform_device *op)
{
struct d7s *p = dev_get_drvdata(&op->dev);
u8 regs = readb(p->regs);
diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c
index 3c27f45e2b6d..078e5f4520ef 100644
--- a/drivers/sbus/char/envctrl.c
+++ b/drivers/sbus/char/envctrl.c
@@ -1027,7 +1027,7 @@ static int kenvctrld(void *__unused)
return 0;
}
-static int __devinit envctrl_probe(struct of_device *op,
+static int __devinit envctrl_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dp;
@@ -1104,7 +1104,7 @@ out_iounmap:
return err;
}
-static int __devexit envctrl_remove(struct of_device *op)
+static int __devexit envctrl_remove(struct platform_device *op)
{
int index;
diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c
index 8bb31c584b64..2b4b4b613c48 100644
--- a/drivers/sbus/char/flash.c
+++ b/drivers/sbus/char/flash.c
@@ -160,7 +160,7 @@ static const struct file_operations flash_fops = {
static struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops };
-static int __devinit flash_probe(struct of_device *op,
+static int __devinit flash_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -192,7 +192,7 @@ static int __devinit flash_probe(struct of_device *op,
return misc_register(&flash_dev);
}
-static int __devexit flash_remove(struct of_device *op)
+static int __devexit flash_remove(struct platform_device *op)
{
misc_deregister(&flash_dev);
diff --git a/drivers/sbus/char/uctrl.c b/drivers/sbus/char/uctrl.c
index 41eb6725ff5f..1b345be5cc02 100644
--- a/drivers/sbus/char/uctrl.c
+++ b/drivers/sbus/char/uctrl.c
@@ -348,7 +348,7 @@ static void uctrl_get_external_status(struct uctrl_driver *driver)
}
-static int __devinit uctrl_probe(struct of_device *op,
+static int __devinit uctrl_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct uctrl_driver *p;
@@ -404,7 +404,7 @@ out_free:
goto out;
}
-static int __devexit uctrl_remove(struct of_device *op)
+static int __devexit uctrl_remove(struct platform_device *op)
{
struct uctrl_driver *p = dev_get_drvdata(&op->dev);
diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c
index 80dc3ac12cde..89fc1c8af86b 100644
--- a/drivers/scsi/53c700.c
+++ b/drivers/scsi/53c700.c
@@ -309,9 +309,6 @@ NCR_700_detect(struct scsi_host_template *tpnt,
hostdata->msgin = memory + MSGIN_OFFSET;
hostdata->msgout = memory + MSGOUT_OFFSET;
hostdata->status = memory + STATUS_OFFSET;
- /* all of these offsets are L1_CACHE_BYTES separated. It is fatal
- * if this isn't sufficient separation to avoid dma flushing issues */
- BUG_ON(!dma_is_consistent(hostdata->dev, pScript) && L1_CACHE_BYTES < dma_get_cache_alignment());
hostdata->slots = (struct NCR_700_command_slot *)(memory + SLOTS_OFFSET);
hostdata->dev = dev;
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 158284f05732..6466231f338b 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -1853,7 +1853,7 @@ config ZFCP_DIF
config SCSI_PMCRAID
tristate "PMC SIERRA Linux MaxRAID adapter support"
- depends on PCI && SCSI
+ depends on PCI && SCSI && NET
---help---
This driver supports the PMC SIERRA MaxRAID adapters.
diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c
index f92da9fd5f20..5d2f148889ad 100644
--- a/drivers/scsi/NCR5380.c
+++ b/drivers/scsi/NCR5380.c
@@ -1857,7 +1857,9 @@ static int NCR5380_transfer_dma(struct Scsi_Host *instance, unsigned char *phase
#endif
/* KLL May need eop and parity in 53c400 */
if (hostdata->flags & FLAG_NCR53C400)
- NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_PAR_CHECK | MR_ENABLE_PAR_INTR | MR_ENABLE_EOP_INTR | MR_DMA_MODE | MR_MONITOR_BSY);
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE |
+ MR_ENABLE_PAR_CHECK | MR_ENABLE_PAR_INTR |
+ MR_ENABLE_EOP_INTR | MR_MONITOR_BSY);
else
NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
#endif /* def REAL_DMA */
diff --git a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c
index 04057ab72a8b..84d77fd86e5b 100644
--- a/drivers/scsi/aacraid/rx.c
+++ b/drivers/scsi/aacraid/rx.c
@@ -352,9 +352,8 @@ static int aac_rx_check_health(struct aac_dev *dev)
pci_free_consistent(dev->pdev, sizeof(struct POSTSTATUS),
post, paddr);
if (likely((buffer[0] == '0') && ((buffer[1] == 'x') || (buffer[1] == 'X')))) {
- ret = (buffer[2] <= '9') ? (buffer[2] - '0') : (buffer[2] - 'A' + 10);
- ret <<= 4;
- ret += (buffer[3] <= '9') ? (buffer[3] - '0') : (buffer[3] - 'A' + 10);
+ ret = (hex_to_bin(buffer[2]) << 4) +
+ hex_to_bin(buffer[3]);
}
pci_free_consistent(dev->pdev, 512, buffer, baddr);
return ret;
diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c
index 24ac2315c5c7..3b7e83d2dab4 100644
--- a/drivers/scsi/aic94xx/aic94xx_init.c
+++ b/drivers/scsi/aic94xx/aic94xx_init.c
@@ -688,9 +688,9 @@ static int asd_register_sas_ha(struct asd_ha_struct *asd_ha)
{
int i;
struct asd_sas_phy **sas_phys =
- kmalloc(ASD_MAX_PHYS * sizeof(struct asd_sas_phy), GFP_KERNEL);
+ kcalloc(ASD_MAX_PHYS, sizeof(*sas_phys), GFP_KERNEL);
struct asd_sas_port **sas_ports =
- kmalloc(ASD_MAX_PHYS * sizeof(struct asd_sas_port), GFP_KERNEL);
+ kcalloc(ASD_MAX_PHYS, sizeof(*sas_ports), GFP_KERNEL);
if (!sas_phys || !sas_ports) {
kfree(sas_phys);
diff --git a/drivers/scsi/bfa/bfa_fcport.c b/drivers/scsi/bfa/bfa_fcport.c
index f0933d8d1eda..76867b5577fa 100644
--- a/drivers/scsi/bfa/bfa_fcport.c
+++ b/drivers/scsi/bfa/bfa_fcport.c
@@ -1310,7 +1310,7 @@ bfa_fcport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg)
break;
case BFI_FCPORT_I2H_DISABLE_RSP:
- if (fcport->msgtag == i2hmsg.penable_rsp->msgtag)
+ if (fcport->msgtag == i2hmsg.pdisable_rsp->msgtag)
bfa_sm_send_event(fcport, BFA_FCPORT_SM_FWRSP);
break;
diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c
index 678120b70460..6ef87f6fcdbb 100644
--- a/drivers/scsi/bfa/bfad_im.c
+++ b/drivers/scsi/bfa/bfad_im.c
@@ -291,7 +291,7 @@ bfad_im_reset_lun_handler(struct scsi_cmnd *cmnd)
struct bfa_tskim_s *tskim;
struct bfad_itnim_s *itnim;
struct bfa_itnim_s *bfa_itnim;
- DECLARE_WAIT_QUEUE_HEAD(wq);
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
int rc = SUCCESS;
unsigned long flags;
enum bfi_tskim_status task_status;
@@ -353,7 +353,7 @@ bfad_im_reset_bus_handler(struct scsi_cmnd *cmnd)
struct bfad_itnim_s *itnim;
unsigned long flags;
u32 i, rc, err_cnt = 0;
- DECLARE_WAIT_QUEUE_HEAD(wq);
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
enum bfi_tskim_status task_status;
spin_lock_irqsave(&bfad->bfad_lock, flags);
diff --git a/drivers/scsi/bfa/include/protocol/fcp.h b/drivers/scsi/bfa/include/protocol/fcp.h
index 9ade68ad2853..74ea63ce84b7 100644
--- a/drivers/scsi/bfa/include/protocol/fcp.h
+++ b/drivers/scsi/bfa/include/protocol/fcp.h
@@ -18,6 +18,7 @@
#ifndef __FCPPROTO_H__
#define __FCPPROTO_H__
+#include <linux/bitops.h>
#include <protocol/scsi.h>
#pragma pack(1)
@@ -102,9 +103,6 @@ enum {
/*
* Task management flags field - only one bit shall be set
*/
-#ifndef BIT
-#define BIT(_x) (1 << (_x))
-#endif
enum fcp_tm_cmnd{
FCP_TM_ABORT_TASK_SET = BIT(1),
FCP_TM_CLEAR_TASK_SET = BIT(2),
diff --git a/drivers/scsi/bnx2i/Kconfig b/drivers/scsi/bnx2i/Kconfig
index 1e9f7141102b..45a6154ce972 100644
--- a/drivers/scsi/bnx2i/Kconfig
+++ b/drivers/scsi/bnx2i/Kconfig
@@ -1,10 +1,11 @@
config SCSI_BNX2_ISCSI
tristate "Broadcom NetXtreme II iSCSI support"
+ depends on NET
+ depends on PCI
select SCSI_ISCSI_ATTRS
select NETDEVICES
select NETDEV_1000
select CNIC
- depends on PCI
---help---
This driver supports iSCSI offload for the Broadcom NetXtreme II
devices.
diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c
index 4799d4391203..d6532187f616 100644
--- a/drivers/scsi/ch.c
+++ b/drivers/scsi/ch.c
@@ -84,10 +84,16 @@ static const char * vendor_labels[CH_TYPES-4] = {
};
// module_param_string_array(vendor_labels, NULL, 0444);
-#define dprintk(fmt, arg...) if (debug) \
- printk(KERN_DEBUG "%s: " fmt, ch->name , ## arg)
-#define vprintk(fmt, arg...) if (verbose) \
- printk(KERN_INFO "%s: " fmt, ch->name , ## arg)
+#define DPRINTK(fmt, arg...) \
+do { \
+ if (debug) \
+ printk(KERN_DEBUG "%s: " fmt, ch->name, ##arg); \
+} while (0)
+#define VPRINTK(level, fmt, arg...) \
+do { \
+ if (verbose) \
+ printk(level "%s: " fmt, ch->name, ##arg); \
+} while (0)
/* ------------------------------------------------------------------- */
@@ -186,7 +192,7 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd,
retry:
errno = 0;
if (debug) {
- dprintk("command: ");
+ DPRINTK("command: ");
__scsi_print_command(cmd);
}
@@ -194,7 +200,7 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd,
buflength, &sshdr, timeout * HZ,
MAX_RETRIES, NULL);
- dprintk("result: 0x%x\n",result);
+ DPRINTK("result: 0x%x\n",result);
if (driver_byte(result) & DRIVER_SENSE) {
if (debug)
scsi_print_sense_hdr(ch->name, &sshdr);
@@ -250,7 +256,7 @@ ch_read_element_status(scsi_changer *ch, u_int elem, char *data)
cmd[9] = 255;
if (0 == (result = ch_do_scsi(ch, cmd, buffer, 256, DMA_FROM_DEVICE))) {
if (((buffer[16] << 8) | buffer[17]) != elem) {
- dprintk("asked for element 0x%02x, got 0x%02x\n",
+ DPRINTK("asked for element 0x%02x, got 0x%02x\n",
elem,(buffer[16] << 8) | buffer[17]);
kfree(buffer);
return -EIO;
@@ -259,10 +265,10 @@ ch_read_element_status(scsi_changer *ch, u_int elem, char *data)
} else {
if (ch->voltags) {
ch->voltags = 0;
- vprintk("device has no volume tag support\n");
+ VPRINTK(KERN_INFO, "device has no volume tag support\n");
goto retry;
}
- dprintk("READ ELEMENT STATUS for element 0x%x failed\n",elem);
+ DPRINTK("READ ELEMENT STATUS for element 0x%x failed\n",elem);
}
kfree(buffer);
return result;
@@ -274,12 +280,12 @@ ch_init_elem(scsi_changer *ch)
int err;
u_char cmd[6];
- vprintk("INITIALIZE ELEMENT STATUS, may take some time ...\n");
+ VPRINTK(KERN_INFO, "INITIALIZE ELEMENT STATUS, may take some time ...\n");
memset(cmd,0,sizeof(cmd));
cmd[0] = INITIALIZE_ELEMENT_STATUS;
cmd[1] = ch->device->lun << 5;
err = ch_do_scsi(ch, cmd, NULL, 0, DMA_NONE);
- vprintk("... finished\n");
+ VPRINTK(KERN_INFO, "... finished\n");
return err;
}
@@ -322,20 +328,20 @@ ch_readconfig(scsi_changer *ch)
(buffer[buffer[3]+18] << 8) | buffer[buffer[3]+19];
ch->counts[CHET_DT] =
(buffer[buffer[3]+20] << 8) | buffer[buffer[3]+21];
- vprintk("type #1 (mt): 0x%x+%d [medium transport]\n",
+ VPRINTK(KERN_INFO, "type #1 (mt): 0x%x+%d [medium transport]\n",
ch->firsts[CHET_MT],
ch->counts[CHET_MT]);
- vprintk("type #2 (st): 0x%x+%d [storage]\n",
+ VPRINTK(KERN_INFO, "type #2 (st): 0x%x+%d [storage]\n",
ch->firsts[CHET_ST],
ch->counts[CHET_ST]);
- vprintk("type #3 (ie): 0x%x+%d [import/export]\n",
+ VPRINTK(KERN_INFO, "type #3 (ie): 0x%x+%d [import/export]\n",
ch->firsts[CHET_IE],
ch->counts[CHET_IE]);
- vprintk("type #4 (dt): 0x%x+%d [data transfer]\n",
+ VPRINTK(KERN_INFO, "type #4 (dt): 0x%x+%d [data transfer]\n",
ch->firsts[CHET_DT],
ch->counts[CHET_DT]);
} else {
- vprintk("reading element address assigment page failed!\n");
+ VPRINTK(KERN_INFO, "reading element address assigment page failed!\n");
}
/* vendor specific element types */
@@ -346,13 +352,13 @@ ch_readconfig(scsi_changer *ch)
continue;
ch->firsts[CHET_V1+i] = vendor_firsts[i];
ch->counts[CHET_V1+i] = vendor_counts[i];
- vprintk("type #%d (v%d): 0x%x+%d [%s, vendor specific]\n",
+ VPRINTK(KERN_INFO, "type #%d (v%d): 0x%x+%d [%s, vendor specific]\n",
i+5,i+1,vendor_firsts[i],vendor_counts[i],
vendor_labels[i]);
}
/* look up the devices of the data transfer elements */
- ch->dt = kmalloc(ch->counts[CHET_DT]*sizeof(struct scsi_device),
+ ch->dt = kcalloc(ch->counts[CHET_DT], sizeof(*ch->dt),
GFP_KERNEL);
if (!ch->dt) {
@@ -366,21 +372,19 @@ ch_readconfig(scsi_changer *ch)
if (elem < CH_DT_MAX && -1 != dt_id[elem]) {
id = dt_id[elem];
lun = dt_lun[elem];
- vprintk("dt 0x%x: [insmod option] ",
+ VPRINTK(KERN_INFO, "dt 0x%x: [insmod option] ",
elem+ch->firsts[CHET_DT]);
} else if (0 != ch_read_element_status
(ch,elem+ch->firsts[CHET_DT],data)) {
- vprintk("dt 0x%x: READ ELEMENT STATUS failed\n",
+ VPRINTK(KERN_INFO, "dt 0x%x: READ ELEMENT STATUS failed\n",
elem+ch->firsts[CHET_DT]);
} else {
- vprintk("dt 0x%x: ",elem+ch->firsts[CHET_DT]);
+ VPRINTK(KERN_INFO, "dt 0x%x: ",elem+ch->firsts[CHET_DT]);
if (data[6] & 0x80) {
- if (verbose)
- printk("not this SCSI bus\n");
+ VPRINTK(KERN_CONT, "not this SCSI bus\n");
ch->dt[elem] = NULL;
} else if (0 == (data[6] & 0x30)) {
- if (verbose)
- printk("ID/LUN unknown\n");
+ VPRINTK(KERN_CONT, "ID/LUN unknown\n");
ch->dt[elem] = NULL;
} else {
id = ch->device->id;
@@ -390,22 +394,19 @@ ch_readconfig(scsi_changer *ch)
}
}
if (-1 != id) {
- if (verbose)
- printk("ID %i, LUN %i, ",id,lun);
+ VPRINTK(KERN_CONT, "ID %i, LUN %i, ",id,lun);
ch->dt[elem] =
scsi_device_lookup(ch->device->host,
ch->device->channel,
id,lun);
if (!ch->dt[elem]) {
/* should not happen */
- if (verbose)
- printk("Huh? device not found!\n");
+ VPRINTK(KERN_CONT, "Huh? device not found!\n");
} else {
- if (verbose)
- printk("name: %8.8s %16.16s %4.4s\n",
- ch->dt[elem]->vendor,
- ch->dt[elem]->model,
- ch->dt[elem]->rev);
+ VPRINTK(KERN_CONT, "name: %8.8s %16.16s %4.4s\n",
+ ch->dt[elem]->vendor,
+ ch->dt[elem]->model,
+ ch->dt[elem]->rev);
}
}
}
@@ -422,7 +423,7 @@ ch_position(scsi_changer *ch, u_int trans, u_int elem, int rotate)
{
u_char cmd[10];
- dprintk("position: 0x%x\n",elem);
+ DPRINTK("position: 0x%x\n",elem);
if (0 == trans)
trans = ch->firsts[CHET_MT];
memset(cmd,0,sizeof(cmd));
@@ -441,7 +442,7 @@ ch_move(scsi_changer *ch, u_int trans, u_int src, u_int dest, int rotate)
{
u_char cmd[12];
- dprintk("move: 0x%x => 0x%x\n",src,dest);
+ DPRINTK("move: 0x%x => 0x%x\n",src,dest);
if (0 == trans)
trans = ch->firsts[CHET_MT];
memset(cmd,0,sizeof(cmd));
@@ -463,7 +464,7 @@ ch_exchange(scsi_changer *ch, u_int trans, u_int src,
{
u_char cmd[12];
- dprintk("exchange: 0x%x => 0x%x => 0x%x\n",
+ DPRINTK("exchange: 0x%x => 0x%x => 0x%x\n",
src,dest1,dest2);
if (0 == trans)
trans = ch->firsts[CHET_MT];
@@ -511,7 +512,7 @@ ch_set_voltag(scsi_changer *ch, u_int elem,
if (!buffer)
return -ENOMEM;
- dprintk("%s %s voltag: 0x%x => \"%s\"\n",
+ DPRINTK("%s %s voltag: 0x%x => \"%s\"\n",
clear ? "clear" : "set",
alternate ? "alternate" : "primary",
elem, tag);
@@ -550,7 +551,7 @@ static int ch_gstatus(scsi_changer *ch, int type, unsigned char __user *dest)
}
put_user(data[2], dest+i);
if (data[2] & CESTATUS_EXCEPT)
- vprintk("element 0x%x: asc=0x%x, ascq=0x%x\n",
+ VPRINTK(KERN_INFO, "element 0x%x: asc=0x%x, ascq=0x%x\n",
ch->firsts[type]+i,
(int)data[4],(int)data[5]);
retval = ch_read_element_status
@@ -660,7 +661,7 @@ static long ch_ioctl(struct file *file,
return -EFAULT;
if (0 != ch_checkrange(ch, pos.cp_type, pos.cp_unit)) {
- dprintk("CHIOPOSITION: invalid parameter\n");
+ DPRINTK("CHIOPOSITION: invalid parameter\n");
return -EBADSLT;
}
mutex_lock(&ch->lock);
@@ -680,7 +681,7 @@ static long ch_ioctl(struct file *file,
if (0 != ch_checkrange(ch, mv.cm_fromtype, mv.cm_fromunit) ||
0 != ch_checkrange(ch, mv.cm_totype, mv.cm_tounit )) {
- dprintk("CHIOMOVE: invalid parameter\n");
+ DPRINTK("CHIOMOVE: invalid parameter\n");
return -EBADSLT;
}
@@ -703,7 +704,7 @@ static long ch_ioctl(struct file *file,
if (0 != ch_checkrange(ch, mv.ce_srctype, mv.ce_srcunit ) ||
0 != ch_checkrange(ch, mv.ce_fdsttype, mv.ce_fdstunit) ||
0 != ch_checkrange(ch, mv.ce_sdsttype, mv.ce_sdstunit)) {
- dprintk("CHIOEXCHANGE: invalid parameter\n");
+ DPRINTK("CHIOEXCHANGE: invalid parameter\n");
return -EBADSLT;
}
@@ -796,7 +797,7 @@ static long ch_ioctl(struct file *file,
}
} else if (ch->voltags) {
ch->voltags = 0;
- vprintk("device has no volume tag support\n");
+ VPRINTK(KERN_INFO, "device has no volume tag support\n");
goto voltag_retry;
}
kfree(buffer);
@@ -824,7 +825,7 @@ static long ch_ioctl(struct file *file,
return -EFAULT;
if (0 != ch_checkrange(ch, csv.csv_type, csv.csv_unit)) {
- dprintk("CHIOSVOLTAG: invalid parameter\n");
+ DPRINTK("CHIOSVOLTAG: invalid parameter\n");
return -EBADSLT;
}
elem = ch->firsts[csv.csv_type] + csv.csv_unit;
diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c
index bd977be7544e..54f50b07dac7 100644
--- a/drivers/scsi/dc395x.c
+++ b/drivers/scsi/dc395x.c
@@ -1597,7 +1597,7 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
u32 tag_mask = 1;
u8 tag_number = 0;
while (tag_mask & dcb->tag_mask
- && tag_number <= dcb->max_command) {
+ && tag_number < dcb->max_command) {
tag_mask = tag_mask << 1;
tag_number++;
}
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index 19338e0ba2c5..cbb20b13b228 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -21,6 +21,7 @@
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/workqueue.h>
+#include <linux/bitops.h>
#include <scsi/libfc.h>
#include <scsi/libfcoe.h>
#include "fnic_io.h"
@@ -49,7 +50,6 @@
/*
* Tag bits used for special requests.
*/
-#define BIT(nr) (1UL << (nr))
#define FNIC_TAG_ABORT BIT(30) /* tag bit indicating abort */
#define FNIC_TAG_DEV_RST BIT(29) /* indicates device reset */
#define FNIC_TAG_MASK (BIT(24) - 1) /* mask for lookup */
diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c
index 75585a52c88b..427a56d3117e 100644
--- a/drivers/scsi/g_NCR5380.c
+++ b/drivers/scsi/g_NCR5380.c
@@ -285,9 +285,12 @@ static int __init do_DTC3181E_setup(char *str)
int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
{
static int current_override = 0;
- int count, i;
+ int count;
unsigned int *ports;
+#ifndef SCSI_G_NCR5380_MEM
+ int i;
unsigned long region_size = 16;
+#endif
static unsigned int __initdata ncr_53c400a_ports[] = {
0x280, 0x290, 0x300, 0x310, 0x330, 0x340, 0x348, 0x350, 0
};
@@ -296,7 +299,7 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
};
int flags = 0;
struct Scsi_Host *instance;
-#ifdef CONFIG_SCSI_G_NCR5380_MEM
+#ifdef SCSI_G_NCR5380_MEM
unsigned long base;
void __iomem *iomem;
#endif
@@ -315,17 +318,15 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
overrides[0].board = BOARD_NCR53C400A;
else if (dtc_3181e != NCR_NOT_SET)
overrides[0].board = BOARD_DTC3181E;
-
+#ifndef SCSI_G_NCR5380_MEM
if (!current_override && isapnp_present()) {
struct pnp_dev *dev = NULL;
count = 0;
while ((dev = pnp_find_dev(NULL, ISAPNP_VENDOR('D', 'T', 'C'), ISAPNP_FUNCTION(0x436e), dev))) {
if (count >= NO_OVERRIDES)
break;
- if (pnp_device_attach(dev) < 0) {
- printk(KERN_ERR "dtc436e probe: attach failed\n");
+ if (pnp_device_attach(dev) < 0)
continue;
- }
if (pnp_activate_dev(dev) < 0) {
printk(KERN_ERR "dtc436e probe: activate failed\n");
pnp_device_detach(dev);
@@ -349,7 +350,7 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
count++;
}
}
-
+#endif
tpnt->proc_name = "g_NCR5380";
for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
@@ -374,7 +375,7 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
break;
}
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
if (ports) {
/* wakeup sequence for the NCR53C400A and DTC3181E */
@@ -436,7 +437,7 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
#endif
instance = scsi_register(tpnt, sizeof(struct NCR5380_hostdata));
if (instance == NULL) {
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
release_region(overrides[current_override].NCR5380_map_name, region_size);
#else
iounmap(iomem);
@@ -446,10 +447,10 @@ int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
}
instance->NCR5380_instance_name = overrides[current_override].NCR5380_map_name;
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
instance->n_io_port = region_size;
#else
- ((struct NCR5380_hostdata *)instance->hostdata).iomem = iomem;
+ ((struct NCR5380_hostdata *)instance->hostdata)->iomem = iomem;
#endif
NCR5380_init(instance, flags);
@@ -517,10 +518,10 @@ int generic_NCR5380_release_resources(struct Scsi_Host *instance)
free_irq(instance->irq, instance);
NCR5380_exit(instance);
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
release_region(instance->NCR5380_instance_name, instance->n_io_port);
#else
- iounmap(((struct NCR5380_hostdata *)instance->hostdata).iomem);
+ iounmap(((struct NCR5380_hostdata *)instance->hostdata)->iomem);
release_mem_region(instance->NCR5380_instance_name, NCR5380_region_size);
#endif
@@ -590,14 +591,14 @@ static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *dst,
}
while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY);
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
{
int i;
for (i = 0; i < 128; i++)
dst[start + i] = NCR5380_read(C400_HOST_BUFFER);
}
#else
- /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ /* implies SCSI_G_NCR5380_MEM */
memcpy_fromio(dst + start, iomem + NCR53C400_host_buffer, 128);
#endif
start += 128;
@@ -610,14 +611,14 @@ static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *dst,
// FIXME - no timeout
}
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
{
int i;
for (i = 0; i < 128; i++)
dst[start + i] = NCR5380_read(C400_HOST_BUFFER);
}
#else
- /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ /* implies SCSI_G_NCR5380_MEM */
memcpy_fromio(dst + start, iomem + NCR53C400_host_buffer, 128);
#endif
start += 128;
@@ -676,13 +677,13 @@ static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *src,
}
while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
; // FIXME - timeout
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
{
for (i = 0; i < 128; i++)
NCR5380_write(C400_HOST_BUFFER, src[start + i]);
}
#else
- /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ /* implies SCSI_G_NCR5380_MEM */
memcpy_toio(iomem + NCR53C400_host_buffer, src + start, 128);
#endif
start += 128;
@@ -692,13 +693,13 @@ static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *src,
while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
; // FIXME - no timeout
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
{
for (i = 0; i < 128; i++)
NCR5380_write(C400_HOST_BUFFER, src[start + i]);
}
#else
- /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ /* implies SCSI_G_NCR5380_MEM */
memcpy_toio(iomem + NCR53C400_host_buffer, src + start, 128);
#endif
start += 128;
@@ -938,7 +939,7 @@ module_param(ncr_53c400a, int, 0);
module_param(dtc_3181e, int, 0);
MODULE_LICENSE("GPL");
-
+#ifndef SCSI_G_NCR5380_MEM
static struct isapnp_device_id id_table[] __devinitdata = {
{
ISAPNP_ANY_ID, ISAPNP_ANY_ID,
@@ -948,7 +949,7 @@ static struct isapnp_device_id id_table[] __devinitdata = {
};
MODULE_DEVICE_TABLE(isapnp, id_table);
-
+#endif
__setup("ncr5380=", do_NCR5380_setup);
__setup("ncr53c400=", do_NCR53C400_setup);
diff --git a/drivers/scsi/g_NCR5380.h b/drivers/scsi/g_NCR5380.h
index df0b3f69ef63..921764c9ab24 100644
--- a/drivers/scsi/g_NCR5380.h
+++ b/drivers/scsi/g_NCR5380.h
@@ -63,7 +63,7 @@ static const char* generic_NCR5380_info(struct Scsi_Host *);
#define __STRVAL(x) #x
#define STRVAL(x) __STRVAL(x)
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
#define NCR5380_map_config port
#define NCR5380_map_type int
@@ -91,7 +91,7 @@ static const char* generic_NCR5380_info(struct Scsi_Host *);
NCR5380_map_name = (NCR5380_map_type)((instance)->NCR5380_instance_name)
#else
-/* therefore CONFIG_SCSI_G_NCR5380_MEM */
+/* therefore SCSI_G_NCR5380_MEM */
#define NCR5380_map_config memory
#define NCR5380_map_type unsigned long
@@ -114,7 +114,7 @@ static const char* generic_NCR5380_info(struct Scsi_Host *);
register void __iomem *iomem
#define NCR5380_setup(instance) \
- iomem = (((struct NCR5380_hostdata *)(instance)->hostdata).iomem)
+ iomem = (((struct NCR5380_hostdata *)(instance)->hostdata)->iomem)
#endif
diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c
index f672d6213eea..b860d650a563 100644
--- a/drivers/scsi/gdth.c
+++ b/drivers/scsi/gdth.c
@@ -4914,7 +4914,7 @@ static int __init gdth_eisa_probe_one(u16 eisa_slot)
error = scsi_add_host(shp, NULL);
if (error)
- goto out_free_coal_stat;
+ goto out_free_ccb_phys;
list_add_tail(&ha->list, &gdth_instances);
gdth_timer_init();
diff --git a/drivers/scsi/initio.c b/drivers/scsi/initio.c
index a7714160fbc3..108797761b95 100644
--- a/drivers/scsi/initio.c
+++ b/drivers/scsi/initio.c
@@ -2817,7 +2817,6 @@ static void i91uSCBPost(u8 * host_mem, u8 * cblk_mem)
}
cmnd->result = cblk->tastat | (cblk->hastat << 16);
- WARN_ON(cmnd == NULL);
i91u_unmap_scb(host->pci_dev, cmnd);
cmnd->scsi_done(cmnd); /* Notify system DONE */
initio_release_scb(host, cblk); /* Release SCB for current channel */
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index 162704cf6a96..ad05b266e950 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -25,6 +25,7 @@
#include <linux/interrupt.h>
#include <linux/aer.h>
#include <linux/gfp.h>
+#include <linux/kernel.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
@@ -1795,12 +1796,11 @@ lpfc_soft_wwpn_store(struct device *dev, struct device_attribute *attr,
/* Validate and store the new name */
for (i=0, j=0; i < 16; i++) {
- if ((*buf >= 'a') && (*buf <= 'f'))
- j = ((j << 4) | ((*buf++ -'a') + 10));
- else if ((*buf >= 'A') && (*buf <= 'F'))
- j = ((j << 4) | ((*buf++ -'A') + 10));
- else if ((*buf >= '0') && (*buf <= '9'))
- j = ((j << 4) | (*buf++ -'0'));
+ int value;
+
+ value = hex_to_bin(*buf++);
+ if (value >= 0)
+ j = (j << 4) | value;
else
return -EINVAL;
if (i % 2) {
@@ -1888,12 +1888,11 @@ lpfc_soft_wwnn_store(struct device *dev, struct device_attribute *attr,
/* Validate and store the new name */
for (i=0, j=0; i < 16; i++) {
- if ((*buf >= 'a') && (*buf <= 'f'))
- j = ((j << 4) | ((*buf++ -'a') + 10));
- else if ((*buf >= 'A') && (*buf <= 'F'))
- j = ((j << 4) | ((*buf++ -'A') + 10));
- else if ((*buf >= '0') && (*buf <= '9'))
- j = ((j << 4) | (*buf++ -'0'));
+ int value;
+
+ value = hex_to_bin(*buf++);
+ if (value >= 0)
+ j = (j << 4) | value;
else
return -EINVAL;
if (i % 2) {
diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c
index d64b7178fa08..278b352ae78d 100644
--- a/drivers/scsi/osst.c
+++ b/drivers/scsi/osst.c
@@ -5868,7 +5868,8 @@ static int osst_probe(struct device *dev)
}
/* find a free minor number */
- for (i=0; os_scsi_tapes[i] && i<osst_max_dev; i++);
+ for (i = 0; i < osst_max_dev && os_scsi_tapes[i]; i++)
+ ;
if(i >= osst_max_dev) panic ("Scsi_devices corrupt (osst)");
dev_num = i;
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index ff2172da7c19..8c80b49ac1c4 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -115,8 +115,8 @@ int ql2xmaxqueues = 1;
module_param(ql2xmaxqueues, int, S_IRUGO|S_IRUSR);
MODULE_PARM_DESC(ql2xmaxqueues,
"Enables MQ settings "
- "Default is 1 for single queue. Set it to number \
- of queues in MQ mode.");
+ "Default is 1 for single queue. Set it to number "
+ "of queues in MQ mode.");
int ql2xmultique_tag;
module_param(ql2xmultique_tag, int, S_IRUGO|S_IRUSR);
diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c
index 53d7ed0dc169..f8c561cf751e 100644
--- a/drivers/scsi/qlogicpti.c
+++ b/drivers/scsi/qlogicpti.c
@@ -704,7 +704,7 @@ static void __devexit qpti_chain_del(struct qlogicpti *qpti)
static int __devinit qpti_map_regs(struct qlogicpti *qpti)
{
- struct of_device *op = qpti->op;
+ struct platform_device *op = qpti->op;
qpti->qregs = of_ioremap(&op->resource[0], 0,
resource_size(&op->resource[0]),
@@ -727,7 +727,7 @@ static int __devinit qpti_map_regs(struct qlogicpti *qpti)
static int __devinit qpti_register_irq(struct qlogicpti *qpti)
{
- struct of_device *op = qpti->op;
+ struct platform_device *op = qpti->op;
qpti->qhost->irq = qpti->irq = op->archdata.irqs[0];
@@ -752,7 +752,7 @@ fail:
static void __devinit qpti_get_scsi_id(struct qlogicpti *qpti)
{
- struct of_device *op = qpti->op;
+ struct platform_device *op = qpti->op;
struct device_node *dp;
dp = op->dev.of_node;
@@ -773,7 +773,7 @@ static void __devinit qpti_get_scsi_id(struct qlogicpti *qpti)
static void qpti_get_bursts(struct qlogicpti *qpti)
{
- struct of_device *op = qpti->op;
+ struct platform_device *op = qpti->op;
u8 bursts, bmask;
bursts = of_getintprop_default(op->dev.of_node, "burst-sizes", 0xff);
@@ -806,7 +806,7 @@ static void qpti_get_clock(struct qlogicpti *qpti)
*/
static int __devinit qpti_map_queues(struct qlogicpti *qpti)
{
- struct of_device *op = qpti->op;
+ struct platform_device *op = qpti->op;
#define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN)
qpti->res_cpu = dma_alloc_coherent(&op->dev,
@@ -1290,7 +1290,7 @@ static struct scsi_host_template qpti_template = {
.use_clustering = ENABLE_CLUSTERING,
};
-static int __devinit qpti_sbus_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit qpti_sbus_probe(struct platform_device *op, const struct of_device_id *match)
{
struct scsi_host_template *tpnt = match->data;
struct device_node *dp = op->dev.of_node;
@@ -1401,7 +1401,7 @@ fail_unlink:
return -ENODEV;
}
-static int __devexit qpti_sbus_remove(struct of_device *op)
+static int __devexit qpti_sbus_remove(struct platform_device *op)
{
struct qlogicpti *qpti = dev_get_drvdata(&op->dev);
diff --git a/drivers/scsi/qlogicpti.h b/drivers/scsi/qlogicpti.h
index e3c74d1ee2db..4377e87ee79c 100644
--- a/drivers/scsi/qlogicpti.h
+++ b/drivers/scsi/qlogicpti.h
@@ -342,7 +342,7 @@ struct qlogicpti {
u_int req_in_ptr; /* index of next request slot */
u_int res_out_ptr; /* index of next result slot */
long send_marker; /* must we send a marker? */
- struct of_device *op;
+ struct platform_device *op;
unsigned long __pad;
int cmd_count[MAX_TARGETS];
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index b8de389636f8..9ade720422c6 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -1370,12 +1370,6 @@ static void scsi_kill_request(struct request *req, struct request_queue *q)
blk_start_request(req);
- if (unlikely(cmd == NULL)) {
- printk(KERN_CRIT "impossible request in %s.\n",
- __func__);
- BUG();
- }
-
sdev = cmd->device;
starget = scsi_target(sdev);
shost = sdev->host;
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index edb6b362a8fa..d7e470a06180 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -29,6 +29,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/kernel.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
@@ -1730,12 +1731,11 @@ fc_parse_wwn(const char *ns, u64 *nm)
/* Validate and store the new name */
for (i=0, j=0; i < 16; i++) {
- if ((*ns >= 'a') && (*ns <= 'f'))
- j = ((j << 4) | ((*ns++ -'a') + 10));
- else if ((*ns >= 'A') && (*ns <= 'F'))
- j = ((j << 4) | ((*ns++ -'A') + 10));
- else if ((*ns >= '0') && (*ns <= '9'))
- j = ((j << 4) | (*ns++ -'0'));
+ int value;
+
+ value = hex_to_bin(*ns++);
+ if (value >= 0)
+ j = (j << 4) | value;
else
return -EINVAL;
if (i % 2) {
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 8e2e893db9e7..2714becc2eaf 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -119,8 +119,8 @@ static DEFINE_IDA(sd_index_ida);
* object after last put) */
static DEFINE_MUTEX(sd_ref_mutex);
-struct kmem_cache *sd_cdb_cache;
-mempool_t *sd_cdb_pool;
+static struct kmem_cache *sd_cdb_cache;
+static mempool_t *sd_cdb_pool;
static const char *sd_cache_types[] = {
"write through", "none", "write back",
@@ -147,7 +147,7 @@ sd_store_cache_type(struct device *dev, struct device_attribute *attr,
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(sd_cache_types); i++) {
- const int len = strlen(sd_cache_types[i]);
+ len = strlen(sd_cache_types[i]);
if (strncmp(sd_cache_types[i], buf, len) == 0 &&
buf[len] == '\n') {
ct = i;
@@ -1423,7 +1423,7 @@ sd_spinup_disk(struct scsi_disk *sdkp)
/*
* Determine whether disk supports Data Integrity Field.
*/
-void sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer)
+static void sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer)
{
struct scsi_device *sdp = sdkp->device;
u8 type;
@@ -1969,7 +1969,7 @@ defaults:
* The ATO bit indicates whether the DIF application tag is available
* for use by the operating system.
*/
-void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer)
+static void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer)
{
int res, offset;
struct scsi_device *sdp = sdkp->device;
@@ -2315,7 +2315,7 @@ static int sd_probe(struct device *dev)
struct scsi_device *sdp = to_scsi_device(dev);
struct scsi_disk *sdkp;
struct gendisk *gd;
- u32 index;
+ int index;
int error;
error = -ENODEV;
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 2968c6b83ddb..78d616315d8e 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -1686,14 +1686,9 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd)
int len, size = sizeof(struct sg_iovec) * iov_count;
struct iovec *iov;
- iov = kmalloc(size, GFP_ATOMIC);
- if (!iov)
- return -ENOMEM;
-
- if (copy_from_user(iov, hp->dxferp, size)) {
- kfree(iov);
- return -EFAULT;
- }
+ iov = memdup_user(hp->dxferp, size);
+ if (IS_ERR(iov))
+ return PTR_ERR(iov);
len = iov_length(iov, iov_count);
if (hp->dxfer_len < len) {
diff --git a/drivers/scsi/sun_esp.c b/drivers/scsi/sun_esp.c
index 89ba6fe02f80..193b37ba1834 100644
--- a/drivers/scsi/sun_esp.c
+++ b/drivers/scsi/sun_esp.c
@@ -44,7 +44,7 @@ enum dvma_rev {
};
static int __devinit esp_sbus_setup_dma(struct esp *esp,
- struct of_device *dma_of)
+ struct platform_device *dma_of)
{
esp->dma = dma_of;
@@ -81,7 +81,7 @@ static int __devinit esp_sbus_setup_dma(struct esp *esp,
static int __devinit esp_sbus_map_regs(struct esp *esp, int hme)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
struct resource *res;
/* On HME, two reg sets exist, first is DVMA,
@@ -101,7 +101,7 @@ static int __devinit esp_sbus_map_regs(struct esp *esp, int hme)
static int __devinit esp_sbus_map_command_block(struct esp *esp)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
esp->command_block = dma_alloc_coherent(&op->dev, 16,
&esp->command_block_dma,
@@ -114,15 +114,15 @@ static int __devinit esp_sbus_map_command_block(struct esp *esp)
static int __devinit esp_sbus_register_irq(struct esp *esp)
{
struct Scsi_Host *host = esp->host;
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
host->irq = op->archdata.irqs[0];
return request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "ESP", esp);
}
-static void __devinit esp_get_scsi_id(struct esp *esp, struct of_device *espdma)
+static void __devinit esp_get_scsi_id(struct esp *esp, struct platform_device *espdma)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
struct device_node *dp;
dp = op->dev.of_node;
@@ -144,7 +144,7 @@ done:
static void __devinit esp_get_differential(struct esp *esp)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
struct device_node *dp;
dp = op->dev.of_node;
@@ -156,7 +156,7 @@ static void __devinit esp_get_differential(struct esp *esp)
static void __devinit esp_get_clock_params(struct esp *esp)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
struct device_node *bus_dp, *dp;
int fmhz;
@@ -170,10 +170,10 @@ static void __devinit esp_get_clock_params(struct esp *esp)
esp->cfreq = fmhz;
}
-static void __devinit esp_get_bursts(struct esp *esp, struct of_device *dma_of)
+static void __devinit esp_get_bursts(struct esp *esp, struct platform_device *dma_of)
{
struct device_node *dma_dp = dma_of->dev.of_node;
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
struct device_node *dp;
u8 bursts, val;
@@ -195,7 +195,7 @@ static void __devinit esp_get_bursts(struct esp *esp, struct of_device *dma_of)
esp->bursts = bursts;
}
-static void __devinit esp_sbus_get_props(struct esp *esp, struct of_device *espdma)
+static void __devinit esp_sbus_get_props(struct esp *esp, struct platform_device *espdma)
{
esp_get_scsi_id(esp, espdma);
esp_get_differential(esp);
@@ -216,7 +216,7 @@ static u8 sbus_esp_read8(struct esp *esp, unsigned long reg)
static dma_addr_t sbus_esp_map_single(struct esp *esp, void *buf,
size_t sz, int dir)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
return dma_map_single(&op->dev, buf, sz, dir);
}
@@ -224,7 +224,7 @@ static dma_addr_t sbus_esp_map_single(struct esp *esp, void *buf,
static int sbus_esp_map_sg(struct esp *esp, struct scatterlist *sg,
int num_sg, int dir)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
return dma_map_sg(&op->dev, sg, num_sg, dir);
}
@@ -232,7 +232,7 @@ static int sbus_esp_map_sg(struct esp *esp, struct scatterlist *sg,
static void sbus_esp_unmap_single(struct esp *esp, dma_addr_t addr,
size_t sz, int dir)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
dma_unmap_single(&op->dev, addr, sz, dir);
}
@@ -240,7 +240,7 @@ static void sbus_esp_unmap_single(struct esp *esp, dma_addr_t addr,
static void sbus_esp_unmap_sg(struct esp *esp, struct scatterlist *sg,
int num_sg, int dir)
{
- struct of_device *op = esp->dev;
+ struct platform_device *op = esp->dev;
dma_unmap_sg(&op->dev, sg, num_sg, dir);
}
@@ -256,7 +256,7 @@ static void sbus_esp_reset_dma(struct esp *esp)
{
int can_do_burst16, can_do_burst32, can_do_burst64;
int can_do_sbus64, lim;
- struct of_device *op;
+ struct platform_device *op;
u32 val;
can_do_burst16 = (esp->bursts & DMA_BURST16) != 0;
@@ -487,8 +487,8 @@ static const struct esp_driver_ops sbus_esp_ops = {
.dma_error = sbus_esp_dma_error,
};
-static int __devinit esp_sbus_probe_one(struct of_device *op,
- struct of_device *espdma,
+static int __devinit esp_sbus_probe_one(struct platform_device *op,
+ struct platform_device *espdma,
int hme)
{
struct scsi_host_template *tpnt = &scsi_esp_template;
@@ -562,11 +562,11 @@ fail:
return err;
}
-static int __devinit esp_sbus_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit esp_sbus_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dma_node = NULL;
struct device_node *dp = op->dev.of_node;
- struct of_device *dma_of = NULL;
+ struct platform_device *dma_of = NULL;
int hme = 0;
if (dp->parent &&
@@ -585,10 +585,10 @@ static int __devinit esp_sbus_probe(struct of_device *op, const struct of_device
return esp_sbus_probe_one(op, dma_of, hme);
}
-static int __devexit esp_sbus_remove(struct of_device *op)
+static int __devexit esp_sbus_remove(struct platform_device *op)
{
struct esp *esp = dev_get_drvdata(&op->dev);
- struct of_device *dma_of = esp->dma;
+ struct platform_device *dma_of = esp->dma;
unsigned int irq = esp->host->irq;
bool is_hme;
u32 val;
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index a22e60c06f48..12900f7083b0 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -536,8 +536,8 @@ config SERIAL_S3C6400
config SERIAL_S5PV210
tristate "Samsung S5PV210 Serial port support"
- depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_S5P6442)
- select SERIAL_SAMSUNG_UARTS_4 if CPU_S5PV210
+ depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_S5P6442 || CPU_S5PV310)
+ select SERIAL_SAMSUNG_UARTS_4 if (CPU_S5PV210 || CPU_S5PV310)
default y
help
Serial port support for Samsung's S5P Family of SoC's
diff --git a/drivers/serial/apbuart.c b/drivers/serial/apbuart.c
index 0099b8692b60..cc01c650a144 100644
--- a/drivers/serial/apbuart.c
+++ b/drivers/serial/apbuart.c
@@ -551,7 +551,7 @@ static struct uart_driver grlib_apbuart_driver = {
/* OF Platform Driver */
/* ======================================================================== */
-static int __devinit apbuart_probe(struct of_device *op,
+static int __devinit apbuart_probe(struct platform_device *op,
const struct of_device_id *match)
{
int i = -1;
diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c
index 6016179db533..f2b8adcc6c92 100644
--- a/drivers/serial/cpm_uart/cpm_uart_core.c
+++ b/drivers/serial/cpm_uart/cpm_uart_core.c
@@ -1340,7 +1340,7 @@ static struct uart_driver cpm_reg = {
static int probe_index;
-static int __devinit cpm_uart_probe(struct of_device *ofdev,
+static int __devinit cpm_uart_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
int index = probe_index++;
@@ -1364,7 +1364,7 @@ static int __devinit cpm_uart_probe(struct of_device *ofdev,
return uart_add_one_port(&cpm_reg, &pinfo->port);
}
-static int __devexit cpm_uart_remove(struct of_device *ofdev)
+static int __devexit cpm_uart_remove(struct platform_device *ofdev)
{
struct uart_cpm_port *pinfo = dev_get_drvdata(&ofdev->dev);
return uart_remove_one_port(&cpm_reg, &pinfo->port);
diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c
index 1a88b363005c..8dedb266f143 100644
--- a/drivers/serial/mpc52xx_uart.c
+++ b/drivers/serial/mpc52xx_uart.c
@@ -1298,7 +1298,7 @@ static struct of_device_id mpc52xx_uart_of_match[] = {
};
static int __devinit
-mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match)
+mpc52xx_uart_of_probe(struct platform_device *op, const struct of_device_id *match)
{
int idx = -1;
unsigned int uartclk;
@@ -1369,7 +1369,7 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match)
}
static int
-mpc52xx_uart_of_remove(struct of_device *op)
+mpc52xx_uart_of_remove(struct platform_device *op)
{
struct uart_port *port = dev_get_drvdata(&op->dev);
dev_set_drvdata(&op->dev, NULL);
@@ -1382,7 +1382,7 @@ mpc52xx_uart_of_remove(struct of_device *op)
#ifdef CONFIG_PM
static int
-mpc52xx_uart_of_suspend(struct of_device *op, pm_message_t state)
+mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state)
{
struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev);
@@ -1393,7 +1393,7 @@ mpc52xx_uart_of_suspend(struct of_device *op, pm_message_t state)
}
static int
-mpc52xx_uart_of_resume(struct of_device *op)
+mpc52xx_uart_of_resume(struct platform_device *op)
{
struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev);
diff --git a/drivers/serial/nwpserial.c b/drivers/serial/nwpserial.c
index e65b0d9202a5..de173671e3d0 100644
--- a/drivers/serial/nwpserial.c
+++ b/drivers/serial/nwpserial.c
@@ -344,7 +344,7 @@ int nwpserial_register_port(struct uart_port *port)
mutex_lock(&nwpserial_mutex);
- dn = to_of_device(port->dev)->dev.of_node;
+ dn = port->dev->of_node;
if (dn == NULL)
goto out;
diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c
index a48d9080f552..659a695bdad6 100644
--- a/drivers/serial/of_serial.c
+++ b/drivers/serial/of_serial.c
@@ -27,7 +27,7 @@ struct of_serial_info {
/*
* Fill a struct uart_port for a given device node
*/
-static int __devinit of_platform_serial_setup(struct of_device *ofdev,
+static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
int type, struct uart_port *port)
{
struct resource resource;
@@ -80,7 +80,7 @@ static int __devinit of_platform_serial_setup(struct of_device *ofdev,
/*
* Try to register a serial port
*/
-static int __devinit of_platform_serial_probe(struct of_device *ofdev,
+static int __devinit of_platform_serial_probe(struct platform_device *ofdev,
const struct of_device_id *id)
{
struct of_serial_info *info;
@@ -134,7 +134,7 @@ out:
/*
* Release a line
*/
-static int of_platform_serial_remove(struct of_device *ofdev)
+static int of_platform_serial_remove(struct platform_device *ofdev)
{
struct of_serial_info *info = dev_get_drvdata(&ofdev->dev);
switch (info->type) {
diff --git a/drivers/serial/s5pv210.c b/drivers/serial/s5pv210.c
index 4a789e5361a4..6ebccd70a707 100644
--- a/drivers/serial/s5pv210.c
+++ b/drivers/serial/s5pv210.c
@@ -28,8 +28,12 @@
static int s5pv210_serial_setsource(struct uart_port *port,
struct s3c24xx_uart_clksrc *clk)
{
+ struct s3c2410_uartcfg *cfg = port->dev->platform_data;
unsigned long ucon = rd_regl(port, S3C2410_UCON);
+ if ((cfg->clocks_size) == 1)
+ return 0;
+
if (strcmp(clk->name, "pclk") == 0)
ucon &= ~S5PV210_UCON_CLKMASK;
else if (strcmp(clk->name, "uclk1") == 0)
@@ -47,10 +51,14 @@ static int s5pv210_serial_setsource(struct uart_port *port,
static int s5pv210_serial_getsource(struct uart_port *port,
struct s3c24xx_uart_clksrc *clk)
{
+ struct s3c2410_uartcfg *cfg = port->dev->platform_data;
u32 ucon = rd_regl(port, S3C2410_UCON);
clk->divisor = 1;
+ if ((cfg->clocks_size) == 1)
+ return 0;
+
switch (ucon & S5PV210_UCON_CLKMASK) {
case S5PV210_UCON_PCLK:
clk->name = "pclk";
diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c
index a9d6c5626a0a..b1156ba8ad14 100644
--- a/drivers/serial/samsung.c
+++ b/drivers/serial/samsung.c
@@ -705,8 +705,13 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
if (ourport->info->has_divslot) {
unsigned int div = ourport->baudclk_rate / baud;
- udivslot = udivslot_table[div & 15];
- dbg("udivslot = %04x (div %d)\n", udivslot, div & 15);
+ if (cfg->has_fracval) {
+ udivslot = (div & 15);
+ dbg("fracval = %04x\n", udivslot);
+ } else {
+ udivslot = udivslot_table[div & 15];
+ dbg("udivslot = %04x (div %d)\n", udivslot, div & 15);
+ }
}
switch (termios->c_cflag & CSIZE) {
diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c
index 5f90fcd7d107..c291b3add1d2 100644
--- a/drivers/serial/sh-sci.c
+++ b/drivers/serial/sh-sci.c
@@ -346,6 +346,27 @@ static int scif_rxfill(struct uart_port *port)
return sci_in(port, SCFDR) & SCIF2_RFDC_MASK;
}
}
+#elif defined(CONFIG_ARCH_SH7372)
+static int scif_txfill(struct uart_port *port)
+{
+ if (port->type == PORT_SCIFA)
+ return sci_in(port, SCFDR) >> 8;
+ else
+ return sci_in(port, SCTFDR);
+}
+
+static int scif_txroom(struct uart_port *port)
+{
+ return port->fifosize - scif_txfill(port);
+}
+
+static int scif_rxfill(struct uart_port *port)
+{
+ if (port->type == PORT_SCIFA)
+ return sci_in(port, SCFDR) & SCIF_RFDC_MASK;
+ else
+ return sci_in(port, SCRFDR);
+}
#else
static int scif_txfill(struct uart_port *port)
{
@@ -683,7 +704,7 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
u16 ssr = sci_in(port, SCxSR);
/* Disable future Rx interrupts */
- if (port->type == PORT_SCIFA) {
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
disable_irq_nosync(irq);
scr |= 0x4000;
} else {
@@ -928,7 +949,7 @@ static void sci_dma_tx_complete(void *arg)
if (!uart_circ_empty(xmit)) {
schedule_work(&s->work_tx);
- } else if (port->type == PORT_SCIFA) {
+ } else if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
u16 ctrl = sci_in(port, SCSCR);
sci_out(port, SCSCR, ctrl & ~SCI_CTRL_FLAGS_TIE);
}
@@ -1184,7 +1205,7 @@ static void sci_start_tx(struct uart_port *port)
unsigned short ctrl;
#ifdef CONFIG_SERIAL_SH_SCI_DMA
- if (port->type == PORT_SCIFA) {
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
u16 new, scr = sci_in(port, SCSCR);
if (s->chan_tx)
new = scr | 0x8000;
@@ -1197,7 +1218,7 @@ static void sci_start_tx(struct uart_port *port)
s->cookie_tx < 0)
schedule_work(&s->work_tx);
#endif
- if (!s->chan_tx || port->type == PORT_SCIFA) {
+ if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
/* Set TIE (Transmit Interrupt Enable) bit in SCSCR */
ctrl = sci_in(port, SCSCR);
sci_out(port, SCSCR, ctrl | SCI_CTRL_FLAGS_TIE);
@@ -1210,7 +1231,7 @@ static void sci_stop_tx(struct uart_port *port)
/* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */
ctrl = sci_in(port, SCSCR);
- if (port->type == PORT_SCIFA)
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
ctrl &= ~0x8000;
ctrl &= ~SCI_CTRL_FLAGS_TIE;
sci_out(port, SCSCR, ctrl);
@@ -1222,7 +1243,7 @@ static void sci_start_rx(struct uart_port *port)
/* Set RIE (Receive Interrupt Enable) bit in SCSCR */
ctrl |= sci_in(port, SCSCR);
- if (port->type == PORT_SCIFA)
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
ctrl &= ~0x4000;
sci_out(port, SCSCR, ctrl);
}
@@ -1233,7 +1254,7 @@ static void sci_stop_rx(struct uart_port *port)
/* Clear RIE (Receive Interrupt Enable) bit in SCSCR */
ctrl = sci_in(port, SCSCR);
- if (port->type == PORT_SCIFA)
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
ctrl &= ~0x4000;
ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE);
sci_out(port, SCSCR, ctrl);
@@ -1271,7 +1292,7 @@ static void rx_timer_fn(unsigned long arg)
struct uart_port *port = &s->port;
u16 scr = sci_in(port, SCSCR);
- if (port->type == PORT_SCIFA) {
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
scr &= ~0x4000;
enable_irq(s->irqs[1]);
}
@@ -1524,6 +1545,8 @@ static const char *sci_type(struct uart_port *port)
return "scif";
case PORT_SCIFA:
return "scifa";
+ case PORT_SCIFB:
+ return "scifb";
}
return NULL;
@@ -1612,6 +1635,9 @@ static int __devinit sci_init_single(struct platform_device *dev,
port->line = index;
switch (p->type) {
+ case PORT_SCIFB:
+ port->fifosize = 256;
+ break;
case PORT_SCIFA:
port->fifosize = 64;
break;
diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h
index f70c49f915fa..9b52f77a9305 100644
--- a/drivers/serial/sh-sci.h
+++ b/drivers/serial/sh-sci.h
@@ -322,7 +322,7 @@
#define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\
static inline unsigned int sci_##name##_in(struct uart_port *port) \
{ \
- if (port->type == PORT_SCIF) { \
+ if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \
SCI_IN(scif_size, scif_offset) \
} else { /* PORT_SCI or PORT_SCIFA */ \
SCI_IN(sci_size, sci_offset); \
@@ -330,7 +330,7 @@
} \
static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \
{ \
- if (port->type == PORT_SCIF) { \
+ if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \
SCI_OUT(scif_size, scif_offset, value) \
} else { /* PORT_SCI or PORT_SCIFA */ \
SCI_OUT(sci_size, sci_offset, value); \
@@ -384,8 +384,12 @@
defined(CONFIG_CPU_SUBTYPE_SH7720) || \
defined(CONFIG_CPU_SUBTYPE_SH7721) || \
defined(CONFIG_ARCH_SH7367) || \
- defined(CONFIG_ARCH_SH7377) || \
- defined(CONFIG_ARCH_SH7372)
+ defined(CONFIG_ARCH_SH7377)
+#define SCIF_FNS(name, scif_offset, scif_size) \
+ CPU_SCIF_FNS(name, scif_offset, scif_size)
+#elif defined(CONFIG_ARCH_SH7372)
+#define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) \
+ CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size)
#define SCIF_FNS(name, scif_offset, scif_size) \
CPU_SCIF_FNS(name, scif_offset, scif_size)
#else
@@ -422,8 +426,7 @@
defined(CONFIG_CPU_SUBTYPE_SH7720) || \
defined(CONFIG_CPU_SUBTYPE_SH7721) || \
defined(CONFIG_ARCH_SH7367) || \
- defined(CONFIG_ARCH_SH7377) || \
- defined(CONFIG_ARCH_SH7372)
+ defined(CONFIG_ARCH_SH7377)
SCIF_FNS(SCSMR, 0x00, 16)
SCIF_FNS(SCBRR, 0x04, 8)
@@ -436,6 +439,20 @@ SCIF_FNS(SCFDR, 0x1c, 16)
SCIF_FNS(SCxTDR, 0x20, 8)
SCIF_FNS(SCxRDR, 0x24, 8)
SCIF_FNS(SCLSR, 0x00, 0)
+#elif defined(CONFIG_ARCH_SH7372)
+SCIF_FNS(SCSMR, 0x00, 16)
+SCIF_FNS(SCBRR, 0x04, 8)
+SCIF_FNS(SCSCR, 0x08, 16)
+SCIF_FNS(SCTDSR, 0x0c, 16)
+SCIF_FNS(SCFER, 0x10, 16)
+SCIF_FNS(SCxSR, 0x14, 16)
+SCIF_FNS(SCFCR, 0x18, 16)
+SCIF_FNS(SCFDR, 0x1c, 16)
+SCIF_FNS(SCTFDR, 0x38, 16)
+SCIF_FNS(SCRFDR, 0x3c, 16)
+SCIx_FNS(SCxTDR, 0x20, 8, 0x40, 8)
+SCIx_FNS(SCxRDR, 0x24, 8, 0x60, 8)
+SCIF_FNS(SCLSR, 0x00, 0)
#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\
defined(CONFIG_CPU_SUBTYPE_SH7724)
SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16)
diff --git a/drivers/serial/sunhv.c b/drivers/serial/sunhv.c
index a779e22d213e..c9014868297d 100644
--- a/drivers/serial/sunhv.c
+++ b/drivers/serial/sunhv.c
@@ -519,7 +519,7 @@ static struct console sunhv_console = {
.data = &sunhv_reg,
};
-static int __devinit hv_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit hv_probe(struct platform_device *op, const struct of_device_id *match)
{
struct uart_port *port;
unsigned long minor;
@@ -598,7 +598,7 @@ out_free_port:
return err;
}
-static int __devexit hv_remove(struct of_device *dev)
+static int __devexit hv_remove(struct platform_device *dev)
{
struct uart_port *port = dev_get_drvdata(&dev->dev);
diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c
index 9845fb1cfb1f..5b246b18f42f 100644
--- a/drivers/serial/sunsab.c
+++ b/drivers/serial/sunsab.c
@@ -883,7 +883,7 @@ static int sunsab_console_setup(struct console *con, char *options)
printk("Console: ttyS%d (SAB82532)\n",
(sunsab_reg.minor - 64) + con->index);
- sunserial_console_termios(con, to_of_device(up->port.dev)->dev.of_node);
+ sunserial_console_termios(con, up->port.dev->of_node);
switch (con->cflag & CBAUD) {
case B150: baud = 150; break;
@@ -954,7 +954,7 @@ static inline struct console *SUNSAB_CONSOLE(void)
#endif
static int __devinit sunsab_init_one(struct uart_sunsab_port *up,
- struct of_device *op,
+ struct platform_device *op,
unsigned long offset,
int line)
{
@@ -1006,7 +1006,7 @@ static int __devinit sunsab_init_one(struct uart_sunsab_port *up,
return 0;
}
-static int __devinit sab_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit sab_probe(struct platform_device *op, const struct of_device_id *match)
{
static int inst;
struct uart_sunsab_port *up;
@@ -1062,7 +1062,7 @@ out:
return err;
}
-static int __devexit sab_remove(struct of_device *op)
+static int __devexit sab_remove(struct platform_device *op)
{
struct uart_sunsab_port *up = dev_get_drvdata(&op->dev);
diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c
index 3cdf74822db5..551ebfe3ccbb 100644
--- a/drivers/serial/sunsu.c
+++ b/drivers/serial/sunsu.c
@@ -1200,7 +1200,7 @@ static int __devinit sunsu_kbd_ms_init(struct uart_sunsu_port *up)
return -ENODEV;
printk("%s: %s port at %llx, irq %u\n",
- to_of_device(up->port.dev)->dev.of_node->full_name,
+ up->port.dev->of_node->full_name,
(up->su_type == SU_PORT_KBD) ? "Keyboard" : "Mouse",
(unsigned long long) up->port.mapbase,
up->port.irq);
@@ -1352,7 +1352,7 @@ static int __init sunsu_console_setup(struct console *co, char *options)
spin_lock_init(&port->lock);
/* Get firmware console settings. */
- sunserial_console_termios(co, to_of_device(port->dev)->dev.of_node);
+ sunserial_console_termios(co, port->dev->of_node);
memset(&termios, 0, sizeof(struct ktermios));
termios.c_cflag = co->cflag;
@@ -1406,7 +1406,7 @@ static enum su_type __devinit su_get_type(struct device_node *dp)
return SU_PORT_PORT;
}
-static int __devinit su_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit su_probe(struct platform_device *op, const struct of_device_id *match)
{
static int inst;
struct device_node *dp = op->dev.of_node;
@@ -1497,7 +1497,7 @@ out_unmap:
return err;
}
-static int __devexit su_remove(struct of_device *op)
+static int __devexit su_remove(struct platform_device *op)
{
struct uart_sunsu_port *up = dev_get_drvdata(&op->dev);
bool kbdms = false;
diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c
index d1e6bcb59546..c1967ac1c07f 100644
--- a/drivers/serial/sunzilog.c
+++ b/drivers/serial/sunzilog.c
@@ -1230,7 +1230,7 @@ static int __init sunzilog_console_setup(struct console *con, char *options)
(sunzilog_reg.minor - 64) + con->index, con->index);
/* Get firmware console settings. */
- sunserial_console_termios(con, to_of_device(up->port.dev)->dev.of_node);
+ sunserial_console_termios(con, up->port.dev->of_node);
/* Firmware console speed is limited to 150-->38400 baud so
* this hackish cflag thing is OK.
@@ -1399,7 +1399,7 @@ static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up)
static int zilog_irq = -1;
-static int __devinit zs_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit zs_probe(struct platform_device *op, const struct of_device_id *match)
{
static int kbm_inst, uart_inst;
int inst;
@@ -1516,7 +1516,7 @@ static void __devexit zs_remove_one(struct uart_sunzilog_port *up)
uart_remove_one_port(&sunzilog_reg, &up->port);
}
-static int __devexit zs_remove(struct of_device *op)
+static int __devexit zs_remove(struct platform_device *op)
{
struct uart_sunzilog_port *up = dev_get_drvdata(&op->dev);
struct zilog_layout __iomem *regs;
diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c
index caf085d3a76a..9b03d7b3e456 100644
--- a/drivers/serial/uartlite.c
+++ b/drivers/serial/uartlite.c
@@ -584,7 +584,7 @@ static struct platform_driver ulite_platform_driver = {
*/
#if defined(CONFIG_OF) && (defined(CONFIG_PPC32) || defined(CONFIG_MICROBLAZE))
static int __devinit
-ulite_of_probe(struct of_device *op, const struct of_device_id *match)
+ulite_of_probe(struct platform_device *op, const struct of_device_id *match)
{
struct resource res;
const unsigned int *id;
@@ -605,7 +605,7 @@ ulite_of_probe(struct of_device *op, const struct of_device_id *match)
return ulite_assign(&op->dev, id ? *id : -1, res.start, irq);
}
-static int __devexit ulite_of_remove(struct of_device *op)
+static int __devexit ulite_of_remove(struct platform_device *op)
{
return ulite_release(&op->dev);
}
diff --git a/drivers/serial/ucc_uart.c b/drivers/serial/ucc_uart.c
index 907b06f5c447..3f4848e2174a 100644
--- a/drivers/serial/ucc_uart.c
+++ b/drivers/serial/ucc_uart.c
@@ -1194,7 +1194,7 @@ static void uart_firmware_cont(const struct firmware *fw, void *context)
release_firmware(fw);
}
-static int ucc_uart_probe(struct of_device *ofdev,
+static int ucc_uart_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -1462,7 +1462,7 @@ static int ucc_uart_probe(struct of_device *ofdev,
return 0;
}
-static int ucc_uart_remove(struct of_device *ofdev)
+static int ucc_uart_remove(struct platform_device *ofdev)
{
struct uart_qe_port *qe_port = dev_get_drvdata(&ofdev->dev);
diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile
index 78bb5127abd0..08fc653a825c 100644
--- a/drivers/sh/Makefile
+++ b/drivers/sh/Makefile
@@ -1,9 +1,10 @@
#
# Makefile for the SuperH specific drivers.
#
+obj-y := clk.o intc.o
+
obj-$(CONFIG_SUPERHYWAY) += superhyway/
obj-$(CONFIG_MAPLE) += maple/
+
obj-$(CONFIG_GENERIC_GPIO) += pfc.o
-obj-$(CONFIG_SUPERH) += clk.o
obj-$(CONFIG_SH_CLK_CPG) += clk-cpg.o
-obj-y += intc.o
diff --git a/drivers/sh/clk-cpg.c b/drivers/sh/clk-cpg.c
index f5c80ba9ab1c..8c024b984ed8 100644
--- a/drivers/sh/clk-cpg.c
+++ b/drivers/sh/clk-cpg.c
@@ -68,6 +68,39 @@ static unsigned long sh_clk_div6_recalc(struct clk *clk)
return clk->freq_table[idx].frequency;
}
+static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct clk_div_mult_table *table = &sh_clk_div6_table;
+ u32 value;
+ int ret, i;
+
+ if (!clk->parent_table || !clk->parent_num)
+ return -EINVAL;
+
+ /* Search the parent */
+ for (i = 0; i < clk->parent_num; i++)
+ if (clk->parent_table[i] == parent)
+ break;
+
+ if (i == clk->parent_num)
+ return -ENODEV;
+
+ ret = clk_reparent(clk, parent);
+ if (ret < 0)
+ return ret;
+
+ value = __raw_readl(clk->enable_reg) &
+ ~(((1 << clk->src_width) - 1) << clk->src_shift);
+
+ __raw_writel(value | (i << clk->src_shift), clk->enable_reg);
+
+ /* Rebuild the frequency table */
+ clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
+ table, &clk->arch_flags);
+
+ return 0;
+}
+
static int sh_clk_div6_set_rate(struct clk *clk,
unsigned long rate, int algo_id)
{
@@ -117,7 +150,17 @@ static struct clk_ops sh_clk_div6_clk_ops = {
.disable = sh_clk_div6_disable,
};
-int __init sh_clk_div6_register(struct clk *clks, int nr)
+static struct clk_ops sh_clk_div6_reparent_clk_ops = {
+ .recalc = sh_clk_div6_recalc,
+ .round_rate = sh_clk_div_round_rate,
+ .set_rate = sh_clk_div6_set_rate,
+ .enable = sh_clk_div6_enable,
+ .disable = sh_clk_div6_disable,
+ .set_parent = sh_clk_div6_set_parent,
+};
+
+static int __init sh_clk_div6_register_ops(struct clk *clks, int nr,
+ struct clk_ops *ops)
{
struct clk *clkp;
void *freq_table;
@@ -136,7 +179,7 @@ int __init sh_clk_div6_register(struct clk *clks, int nr)
for (k = 0; !ret && (k < nr); k++) {
clkp = clks + k;
- clkp->ops = &sh_clk_div6_clk_ops;
+ clkp->ops = ops;
clkp->id = -1;
clkp->freq_table = freq_table + (k * freq_table_size);
clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END;
@@ -147,6 +190,17 @@ int __init sh_clk_div6_register(struct clk *clks, int nr)
return ret;
}
+int __init sh_clk_div6_register(struct clk *clks, int nr)
+{
+ return sh_clk_div6_register_ops(clks, nr, &sh_clk_div6_clk_ops);
+}
+
+int __init sh_clk_div6_reparent_register(struct clk *clks, int nr)
+{
+ return sh_clk_div6_register_ops(clks, nr,
+ &sh_clk_div6_reparent_clk_ops);
+}
+
static unsigned long sh_clk_div4_recalc(struct clk *clk)
{
struct clk_div4_table *d4t = clk->priv;
diff --git a/drivers/spi/mpc512x_psc_spi.c b/drivers/spi/mpc512x_psc_spi.c
index 10baac3f8ea5..cddbfceb324f 100644
--- a/drivers/spi/mpc512x_psc_spi.c
+++ b/drivers/spi/mpc512x_psc_spi.c
@@ -507,7 +507,7 @@ static int __exit mpc512x_psc_spi_do_remove(struct device *dev)
return 0;
}
-static int __init mpc512x_psc_spi_of_probe(struct of_device *op,
+static int __init mpc512x_psc_spi_of_probe(struct platform_device *op,
const struct of_device_id *match)
{
const u32 *regaddr_p;
@@ -539,7 +539,7 @@ static int __init mpc512x_psc_spi_of_probe(struct of_device *op,
irq_of_parse_and_map(op->dev.of_node, 0), id);
}
-static int __exit mpc512x_psc_spi_of_remove(struct of_device *op)
+static int __exit mpc512x_psc_spi_of_remove(struct platform_device *op)
{
return mpc512x_psc_spi_do_remove(&op->dev);
}
diff --git a/drivers/spi/mpc52xx_psc_spi.c b/drivers/spi/mpc52xx_psc_spi.c
index 66d170147dcc..983fbbfce76e 100644
--- a/drivers/spi/mpc52xx_psc_spi.c
+++ b/drivers/spi/mpc52xx_psc_spi.c
@@ -465,7 +465,7 @@ static int __exit mpc52xx_psc_spi_do_remove(struct device *dev)
return 0;
}
-static int __init mpc52xx_psc_spi_of_probe(struct of_device *op,
+static int __init mpc52xx_psc_spi_of_probe(struct platform_device *op,
const struct of_device_id *match)
{
const u32 *regaddr_p;
@@ -495,7 +495,7 @@ static int __init mpc52xx_psc_spi_of_probe(struct of_device *op,
irq_of_parse_and_map(op->dev.of_node, 0), id);
}
-static int __exit mpc52xx_psc_spi_of_remove(struct of_device *op)
+static int __exit mpc52xx_psc_spi_of_remove(struct platform_device *op)
{
return mpc52xx_psc_spi_do_remove(&op->dev);
}
diff --git a/drivers/spi/mpc52xx_spi.c b/drivers/spi/mpc52xx_spi.c
index 56136ff00e01..ec9f0b1bf864 100644
--- a/drivers/spi/mpc52xx_spi.c
+++ b/drivers/spi/mpc52xx_spi.c
@@ -390,7 +390,7 @@ static int mpc52xx_spi_transfer(struct spi_device *spi, struct spi_message *m)
/*
* OF Platform Bus Binding
*/
-static int __devinit mpc52xx_spi_probe(struct of_device *op,
+static int __devinit mpc52xx_spi_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct spi_master *master;
@@ -530,7 +530,7 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
return rc;
}
-static int __devexit mpc52xx_spi_remove(struct of_device *op)
+static int __devexit mpc52xx_spi_remove(struct platform_device *op)
{
struct spi_master *master = dev_get_drvdata(&op->dev);
struct mpc52xx_spi *ms = spi_master_get_devdata(master);
diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index aad9ae1b9c69..d31b57f7baaf 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -1236,7 +1236,7 @@ static int of_mpc8xxx_spi_free_chipselects(struct device *dev)
return 0;
}
-static int __devinit of_mpc8xxx_spi_probe(struct of_device *ofdev,
+static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
const struct of_device_id *ofid)
{
struct device *dev = &ofdev->dev;
@@ -1308,7 +1308,7 @@ err_clk:
return ret;
}
-static int __devexit of_mpc8xxx_spi_remove(struct of_device *ofdev)
+static int __devexit of_mpc8xxx_spi_remove(struct platform_device *ofdev)
{
int ret;
diff --git a/drivers/spi/spi_ppc4xx.c b/drivers/spi/spi_ppc4xx.c
index 0f5fa7e2a550..80e172d3e72a 100644
--- a/drivers/spi/spi_ppc4xx.c
+++ b/drivers/spi/spi_ppc4xx.c
@@ -388,9 +388,9 @@ static void free_gpios(struct ppc4xx_spi *hw)
}
/*
- * of_device layer stuff...
+ * platform_device layer stuff...
*/
-static int __init spi_ppc4xx_of_probe(struct of_device *op,
+static int __init spi_ppc4xx_of_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct ppc4xx_spi *hw;
@@ -565,7 +565,7 @@ free_master:
return ret;
}
-static int __exit spi_ppc4xx_of_remove(struct of_device *op)
+static int __exit spi_ppc4xx_of_remove(struct platform_device *op)
{
struct spi_master *master = dev_get_drvdata(&op->dev);
struct ppc4xx_spi *hw = spi_master_get_devdata(master);
diff --git a/drivers/spi/xilinx_spi_of.c b/drivers/spi/xilinx_spi_of.c
index f53d3f6b9f61..b66c2dbf20a5 100644
--- a/drivers/spi/xilinx_spi_of.c
+++ b/drivers/spi/xilinx_spi_of.c
@@ -38,7 +38,7 @@
#include "xilinx_spi.h"
-static int __devinit xilinx_spi_of_probe(struct of_device *ofdev,
+static int __devinit xilinx_spi_of_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct spi_master *master;
@@ -84,7 +84,7 @@ static int __devinit xilinx_spi_of_probe(struct of_device *ofdev,
return 0;
}
-static int __devexit xilinx_spi_remove(struct of_device *ofdev)
+static int __devexit xilinx_spi_remove(struct platform_device *ofdev)
{
xilinx_spi_deinit(dev_get_drvdata(&ofdev->dev));
dev_set_drvdata(&ofdev->dev, 0);
@@ -93,7 +93,7 @@ static int __devexit xilinx_spi_remove(struct of_device *ofdev)
return 0;
}
-static int __exit xilinx_spi_of_remove(struct of_device *op)
+static int __exit xilinx_spi_of_remove(struct platform_device *op)
{
return xilinx_spi_remove(op);
}
diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c
index 9648b75f0283..a5ea2c1d8c93 100644
--- a/drivers/usb/gadget/fsl_qe_udc.c
+++ b/drivers/usb/gadget/fsl_qe_udc.c
@@ -2398,7 +2398,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
EXPORT_SYMBOL(usb_gadget_unregister_driver);
/* udc structure's alloc and setup, include ep-param alloc */
-static struct qe_udc __devinit *qe_udc_config(struct of_device *ofdev)
+static struct qe_udc __devinit *qe_udc_config(struct platform_device *ofdev)
{
struct qe_udc *udc;
struct device_node *np = ofdev->dev.of_node;
@@ -2523,7 +2523,7 @@ static void qe_udc_release(struct device *dev)
}
/* Driver probe functions */
-static int __devinit qe_udc_probe(struct of_device *ofdev,
+static int __devinit qe_udc_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -2679,18 +2679,18 @@ err1:
}
#ifdef CONFIG_PM
-static int qe_udc_suspend(struct of_device *dev, pm_message_t state)
+static int qe_udc_suspend(struct platform_device *dev, pm_message_t state)
{
return -ENOTSUPP;
}
-static int qe_udc_resume(struct of_device *dev)
+static int qe_udc_resume(struct platform_device *dev)
{
return -ENOTSUPP;
}
#endif
-static int __devexit qe_udc_remove(struct of_device *ofdev)
+static int __devexit qe_udc_remove(struct platform_device *ofdev)
{
struct qe_ep *ep;
unsigned int size;
diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c
index 5aec92866ab3..335ee699fd85 100644
--- a/drivers/usb/host/ehci-ppc-of.c
+++ b/drivers/usb/host/ehci-ppc-of.c
@@ -106,7 +106,7 @@ ppc44x_enable_bmt(struct device_node *dn)
static int __devinit
-ehci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match)
+ehci_hcd_ppc_of_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dn = op->dev.of_node;
struct usb_hcd *hcd;
@@ -210,7 +210,7 @@ err_rmr:
}
-static int ehci_hcd_ppc_of_remove(struct of_device *op)
+static int ehci_hcd_ppc_of_remove(struct platform_device *op)
{
struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
@@ -253,7 +253,7 @@ static int ehci_hcd_ppc_of_remove(struct of_device *op)
}
-static int ehci_hcd_ppc_of_shutdown(struct of_device *op)
+static int ehci_hcd_ppc_of_shutdown(struct platform_device *op)
{
struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c
index 4899f451add9..6c8076ad821d 100644
--- a/drivers/usb/host/ehci-xilinx-of.c
+++ b/drivers/usb/host/ehci-xilinx-of.c
@@ -140,7 +140,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = {
/**
* ehci_hcd_xilinx_of_probe - Probe method for the USB host controller
- * @op: pointer to the of_device to which the host controller bound
+ * @op: pointer to the platform_device bound to the host controller
* @match: pointer to of_device_id structure, not used
*
* This function requests resources and sets up appropriate properties for the
@@ -149,7 +149,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = {
* entry, and sets an appropriate value for hcd->has_tt.
*/
static int __devinit
-ehci_hcd_xilinx_of_probe(struct of_device *op, const struct of_device_id *match)
+ehci_hcd_xilinx_of_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dn = op->dev.of_node;
struct usb_hcd *hcd;
@@ -242,12 +242,12 @@ err_rmr:
/**
* ehci_hcd_xilinx_of_remove - shutdown hcd and release resources
- * @op: pointer to of_device structure that is to be removed
+ * @op: pointer to platform_device structure that is to be removed
*
* Remove the hcd structure, and release resources that has been requested
* during probe.
*/
-static int ehci_hcd_xilinx_of_remove(struct of_device *op)
+static int ehci_hcd_xilinx_of_remove(struct platform_device *op)
{
struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
dev_set_drvdata(&op->dev, NULL);
@@ -266,11 +266,11 @@ static int ehci_hcd_xilinx_of_remove(struct of_device *op)
/**
* ehci_hcd_xilinx_of_shutdown - shutdown the hcd
- * @op: pointer to of_device structure that is to be removed
+ * @op: pointer to platform_device structure that is to be removed
*
* Properly shutdown the hcd, call driver's shutdown routine.
*/
-static int ehci_hcd_xilinx_of_shutdown(struct of_device *op)
+static int ehci_hcd_xilinx_of_shutdown(struct platform_device *op)
{
struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c
index c7c8392a88b9..20092a27a1e8 100644
--- a/drivers/usb/host/fhci-hcd.c
+++ b/drivers/usb/host/fhci-hcd.c
@@ -561,7 +561,7 @@ static const struct hc_driver fhci_driver = {
.hub_control = fhci_hub_control,
};
-static int __devinit of_fhci_probe(struct of_device *ofdev,
+static int __devinit of_fhci_probe(struct platform_device *ofdev,
const struct of_device_id *ofid)
{
struct device *dev = &ofdev->dev;
@@ -801,7 +801,7 @@ static int __devexit fhci_remove(struct device *dev)
return 0;
}
-static int __devexit of_fhci_remove(struct of_device *ofdev)
+static int __devexit of_fhci_remove(struct platform_device *ofdev)
{
return fhci_remove(&ofdev->dev);
}
diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c
index ec85d0c3cc3e..3b28dbfca058 100644
--- a/drivers/usb/host/isp1760-if.c
+++ b/drivers/usb/host/isp1760-if.c
@@ -27,7 +27,7 @@
#endif
#ifdef CONFIG_PPC_OF
-static int of_isp1760_probe(struct of_device *dev,
+static int of_isp1760_probe(struct platform_device *dev,
const struct of_device_id *match)
{
struct usb_hcd *hcd;
@@ -95,7 +95,7 @@ release_reg:
return ret;
}
-static int of_isp1760_remove(struct of_device *dev)
+static int of_isp1760_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(&dev->dev);
diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c
index df165917412a..b2c2dbf08766 100644
--- a/drivers/usb/host/ohci-ppc-of.c
+++ b/drivers/usb/host/ohci-ppc-of.c
@@ -81,7 +81,7 @@ static const struct hc_driver ohci_ppc_of_hc_driver = {
static int __devinit
-ohci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match)
+ohci_hcd_ppc_of_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dn = op->dev.of_node;
struct usb_hcd *hcd;
@@ -183,7 +183,7 @@ err_rmr:
return rv;
}
-static int ohci_hcd_ppc_of_remove(struct of_device *op)
+static int ohci_hcd_ppc_of_remove(struct platform_device *op)
{
struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
dev_set_drvdata(&op->dev, NULL);
@@ -201,7 +201,7 @@ static int ohci_hcd_ppc_of_remove(struct of_device *op)
return 0;
}
-static int ohci_hcd_ppc_of_shutdown(struct of_device *op)
+static int ohci_hcd_ppc_of_shutdown(struct platform_device *op)
{
struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index a9ca72f301bf..8b31fdfefc98 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1896,6 +1896,13 @@ config FB_W100
If unsure, say N.
+config SH_MIPI_DSI
+ tristate
+ depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK
+
+config SH_LCD_MIPI_DSI
+ bool
+
config FB_SH_MOBILE_LCDC
tristate "SuperH Mobile LCDC framebuffer support"
depends on FB && (SUPERH || ARCH_SHMOBILE) && HAVE_CLK
@@ -1904,9 +1911,17 @@ config FB_SH_MOBILE_LCDC
select FB_SYS_IMAGEBLIT
select FB_SYS_FOPS
select FB_DEFERRED_IO
+ select SH_MIPI_DSI if SH_LCD_MIPI_DSI
---help---
Frame buffer driver for the on-chip SH-Mobile LCD controller.
+config FB_SH_MOBILE_HDMI
+ tristate "SuperH Mobile HDMI controller support"
+ depends on FB_SH_MOBILE_LCDC
+ select FB_MODE_HELPERS
+ ---help---
+ Driver for the on-chip SH-Mobile HDMI controller.
+
config FB_TMIO
tristate "Toshiba Mobile IO FrameBuffer support"
depends on FB && MFD_CORE
@@ -1931,7 +1946,7 @@ config FB_TMIO_ACCELL
config FB_S3C
tristate "Samsung S3C framebuffer support"
- depends on FB && ARCH_S3C64XX
+ depends on FB && S3C_DEV_FB
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index f56a9cae2157..485e8ed1318c 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -123,6 +123,8 @@ obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o
obj-$(CONFIG_FB_PS3) += ps3fb.o
obj-$(CONFIG_FB_SM501) += sm501fb.o
obj-$(CONFIG_FB_XILINX) += xilinxfb.o
+obj-$(CONFIG_SH_MIPI_DSI) += sh_mipi_dsi.o
+obj-$(CONFIG_FB_SH_MOBILE_HDMI) += sh_mobile_hdmi.o
obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o
obj-$(CONFIG_FB_OMAP) += omap/
obj-y += omap2/
diff --git a/drivers/video/bw2.c b/drivers/video/bw2.c
index c7796637bafd..4dc13467281d 100644
--- a/drivers/video/bw2.c
+++ b/drivers/video/bw2.c
@@ -273,7 +273,7 @@ static int __devinit bw2_do_default_mode(struct bw2_par *par,
return 0;
}
-static int __devinit bw2_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit bw2_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
struct fb_info *info;
@@ -350,7 +350,7 @@ out_err:
return err;
}
-static int __devexit bw2_remove(struct of_device *op)
+static int __devexit bw2_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct bw2_par *par = info->par;
diff --git a/drivers/video/cg14.c b/drivers/video/cg14.c
index d09fde8beb69..24249535ac86 100644
--- a/drivers/video/cg14.c
+++ b/drivers/video/cg14.c
@@ -446,7 +446,7 @@ static struct sbus_mmap_map __cg14_mmap_map[CG14_MMAP_ENTRIES] __devinitdata = {
{ .size = 0 }
};
-static void cg14_unmap_regs(struct of_device *op, struct fb_info *info,
+static void cg14_unmap_regs(struct platform_device *op, struct fb_info *info,
struct cg14_par *par)
{
if (par->regs)
@@ -463,7 +463,7 @@ static void cg14_unmap_regs(struct of_device *op, struct fb_info *info,
info->screen_base, info->fix.smem_len);
}
-static int __devinit cg14_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit cg14_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
struct fb_info *info;
@@ -570,7 +570,7 @@ out_err:
return err;
}
-static int __devexit cg14_remove(struct of_device *op)
+static int __devexit cg14_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct cg14_par *par = info->par;
diff --git a/drivers/video/cg3.c b/drivers/video/cg3.c
index 64aa29809fb9..09c0c3c42482 100644
--- a/drivers/video/cg3.c
+++ b/drivers/video/cg3.c
@@ -346,7 +346,7 @@ static int __devinit cg3_do_default_mode(struct cg3_par *par)
return 0;
}
-static int __devinit cg3_probe(struct of_device *op,
+static int __devinit cg3_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -433,7 +433,7 @@ out_err:
return err;
}
-static int __devexit cg3_remove(struct of_device *op)
+static int __devexit cg3_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct cg3_par *par = info->par;
diff --git a/drivers/video/cg6.c b/drivers/video/cg6.c
index 2389a719dcc7..2b5a97058b08 100644
--- a/drivers/video/cg6.c
+++ b/drivers/video/cg6.c
@@ -718,7 +718,7 @@ static void __devinit cg6_chip_init(struct fb_info *info)
sbus_writel(info->var.yres - 1, &fbc->clipmaxy);
}
-static void cg6_unmap_regs(struct of_device *op, struct fb_info *info,
+static void cg6_unmap_regs(struct platform_device *op, struct fb_info *info,
struct cg6_par *par)
{
if (par->fbc)
@@ -737,7 +737,7 @@ static void cg6_unmap_regs(struct of_device *op, struct fb_info *info,
info->fix.smem_len);
}
-static int __devinit cg6_probe(struct of_device *op,
+static int __devinit cg6_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -827,7 +827,7 @@ out_err:
return err;
}
-static int __devexit cg6_remove(struct of_device *op)
+static int __devexit cg6_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct cg6_par *par = info->par;
diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c
index af88651b0735..28b1a834906b 100644
--- a/drivers/video/console/bitblit.c
+++ b/drivers/video/console/bitblit.c
@@ -22,7 +22,7 @@
/*
* Accelerated handlers.
*/
-static inline void update_attr(u8 *dst, u8 *src, int attribute,
+static void update_attr(u8 *dst, u8 *src, int attribute,
struct vc_data *vc)
{
int i, offset = (vc->vc_font.height < 10) ? 1 : 2;
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 26bf7cbfecc2..84f842331dfa 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -287,7 +287,7 @@ static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info)
!vt_force_oops_output(vc);
}
-static inline int get_color(struct vc_data *vc, struct fb_info *info,
+static int get_color(struct vc_data *vc, struct fb_info *info,
u16 c, int is_fg)
{
int depth = fb_get_color_depth(&info->var, &info->fix);
diff --git a/drivers/video/console/fbcon_ccw.c b/drivers/video/console/fbcon_ccw.c
index d135671d9961..41b32ae23dac 100644
--- a/drivers/video/console/fbcon_ccw.c
+++ b/drivers/video/console/fbcon_ccw.c
@@ -22,7 +22,7 @@
* Rotation 270 degrees
*/
-static inline void ccw_update_attr(u8 *dst, u8 *src, int attribute,
+static void ccw_update_attr(u8 *dst, u8 *src, int attribute,
struct vc_data *vc)
{
int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2;
diff --git a/drivers/video/console/fbcon_cw.c b/drivers/video/console/fbcon_cw.c
index 126110f8454f..6a737827beb1 100644
--- a/drivers/video/console/fbcon_cw.c
+++ b/drivers/video/console/fbcon_cw.c
@@ -22,7 +22,7 @@
* Rotation 90 degrees
*/
-static inline void cw_update_attr(u8 *dst, u8 *src, int attribute,
+static void cw_update_attr(u8 *dst, u8 *src, int attribute,
struct vc_data *vc)
{
int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2;
diff --git a/drivers/video/console/fbcon_ud.c b/drivers/video/console/fbcon_ud.c
index 93a3e7381b50..ff0872c0498b 100644
--- a/drivers/video/console/fbcon_ud.c
+++ b/drivers/video/console/fbcon_ud.c
@@ -22,7 +22,7 @@
* Rotation 180 degrees
*/
-static inline void ud_update_attr(u8 *dst, u8 *src, int attribute,
+static void ud_update_attr(u8 *dst, u8 *src, int attribute,
struct vc_data *vc)
{
int i, offset = (vc->vc_font.height < 10) ? 1 : 2;
diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c
index 4a56f46af40a..815f84b07933 100644
--- a/drivers/video/efifb.c
+++ b/drivers/video/efifb.c
@@ -16,7 +16,7 @@
#include <video/vga.h>
-static struct fb_var_screeninfo efifb_defined __initdata = {
+static struct fb_var_screeninfo efifb_defined __devinitdata = {
.activate = FB_ACTIVATE_NOW,
.height = -1,
.width = -1,
@@ -27,7 +27,7 @@ static struct fb_var_screeninfo efifb_defined __initdata = {
.vmode = FB_VMODE_NONINTERLACED,
};
-static struct fb_fix_screeninfo efifb_fix __initdata = {
+static struct fb_fix_screeninfo efifb_fix __devinitdata = {
.id = "EFI VGA",
.type = FB_TYPE_PACKED_PIXELS,
.accel = FB_ACCEL_NONE,
@@ -59,7 +59,7 @@ static struct efifb_dmi_info {
int stride;
int width;
int height;
-} dmi_list[] = {
+} dmi_list[] __initdata = {
[M_I17] = { "i17", 0x80010000, 1472 * 4, 1440, 900 },
[M_I20] = { "i20", 0x80010000, 1728 * 4, 1680, 1050 }, /* guess */
[M_I20_SR] = { "imac7", 0x40010000, 1728 * 4, 1680, 1050 },
@@ -83,7 +83,7 @@ static int set_system(const struct dmi_system_id *id);
DMI_MATCH(DMI_PRODUCT_NAME, name) }, \
&dmi_list[enumid] }
-static struct dmi_system_id __initdata dmi_system_table[] = {
+static const struct dmi_system_id dmi_system_table[] __initconst = {
EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac4,1", M_I17),
/* At least one of these two will be right; maybe both? */
EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac5,1", M_I20),
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 731fce64df9d..b06647517c0e 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1362,6 +1362,7 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
vma->vm_pgoff = off >> PAGE_SHIFT;
/* This is an IO map - tell maydump to skip this VMA */
vma->vm_flags |= VM_IO | VM_RESERVED;
+ vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
fb_pgprotect(file, vma, off);
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot))
@@ -1786,7 +1787,7 @@ static int ofonly __read_mostly;
int fb_get_options(char *name, char **option)
{
char *opt, *options = NULL;
- int opt_len, retval = 0;
+ int retval = 0;
int name_len = strlen(name), i;
if (name_len && ofonly && strncmp(name, "offb", 4))
@@ -1796,8 +1797,7 @@ int fb_get_options(char *name, char **option)
for (i = 0; i < FB_MAX; i++) {
if (video_options[i] == NULL)
continue;
- opt_len = strlen(video_options[i]);
- if (!opt_len)
+ if (!video_options[i][0])
continue;
opt = video_options[i];
if (!strncmp(name, opt, name_len) &&
diff --git a/drivers/video/ffb.c b/drivers/video/ffb.c
index f6ecfab296d3..6739b2af3bc0 100644
--- a/drivers/video/ffb.c
+++ b/drivers/video/ffb.c
@@ -893,7 +893,7 @@ static void ffb_init_fix(struct fb_info *info)
info->fix.accel = FB_ACCEL_SUN_CREATOR;
}
-static int __devinit ffb_probe(struct of_device *op,
+static int __devinit ffb_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -1023,7 +1023,7 @@ out_err:
return err;
}
-static int __devexit ffb_remove(struct of_device *op)
+static int __devexit ffb_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct ffb_par *par = info->par;
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
index e38ad2224540..8bbbf08fa3ce 100644
--- a/drivers/video/fsl-diu-fb.c
+++ b/drivers/video/fsl-diu-fb.c
@@ -1393,7 +1393,7 @@ static void free_irq_local(int irq)
* Power management hooks. Note that we won't be called from IRQ context,
* unlike the blank functions above, so we may sleep.
*/
-static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state)
+static int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state)
{
struct fsl_diu_data *machine_data;
@@ -1403,7 +1403,7 @@ static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state)
return 0;
}
-static int fsl_diu_resume(struct of_device *ofdev)
+static int fsl_diu_resume(struct platform_device *ofdev)
{
struct fsl_diu_data *machine_data;
@@ -1487,7 +1487,7 @@ static ssize_t show_monitor(struct device *device,
return diu_ops.show_monitor_port(machine_data->monitor_port, buf);
}
-static int __devinit fsl_diu_probe(struct of_device *ofdev,
+static int __devinit fsl_diu_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->dev.of_node;
@@ -1667,7 +1667,7 @@ error2:
}
-static int fsl_diu_remove(struct of_device *ofdev)
+static int fsl_diu_remove(struct platform_device *ofdev)
{
struct fsl_diu_data *machine_data;
int i;
diff --git a/drivers/video/igafb.c b/drivers/video/igafb.c
index 15d200109446..d885c770eb84 100644
--- a/drivers/video/igafb.c
+++ b/drivers/video/igafb.c
@@ -368,7 +368,7 @@ static int __init iga_init(struct fb_info *info, struct iga_par *par)
return 1;
}
-int __init igafb_init(void)
+static int __init igafb_init(void)
{
struct fb_info *info;
struct pci_dev *pdev;
@@ -531,6 +531,7 @@ int __init igafb_init(void)
iounmap(info->screen_base);
kfree(par->mmap_map);
kfree(info);
+ return -ENODEV;
}
#ifdef CONFIG_SPARC
@@ -556,7 +557,7 @@ int __init igafb_init(void)
return 0;
}
-int __init igafb_setup(char *options)
+static int __init igafb_setup(char *options)
{
char *this_opt;
diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c
index 43f0639b1c10..5c363d026f64 100644
--- a/drivers/video/imxfb.c
+++ b/drivers/video/imxfb.c
@@ -40,6 +40,12 @@
*/
#define DEBUG_VAR 1
+#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || \
+ (defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) && \
+ defined(CONFIG_FB_IMX_MODULE))
+#define PWMR_BACKLIGHT_AVAILABLE
+#endif
+
#define DRIVER_NAME "imx-fb"
#define LCDC_SSA 0x00
@@ -175,7 +181,9 @@ struct imxfb_info {
struct imx_fb_videomode *mode;
int num_modes;
+#ifdef PWMR_BACKLIGHT_AVAILABLE
struct backlight_device *bl;
+#endif
void (*lcd_power)(int);
void (*backlight_power)(int);
@@ -450,8 +458,7 @@ static int imxfb_set_par(struct fb_info *info)
return 0;
}
-
-
+#ifdef PWMR_BACKLIGHT_AVAILABLE
static int imxfb_bl_get_brightness(struct backlight_device *bl)
{
struct imxfb_info *fbi = bl_get_data(bl);
@@ -516,6 +523,7 @@ static void imxfb_exit_backlight(struct imxfb_info *fbi)
if (fbi->bl)
backlight_device_unregister(fbi->bl);
}
+#endif
static void imxfb_enable_controller(struct imxfb_info *fbi)
{
@@ -647,6 +655,9 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
fbi->regs + LCDC_SIZE);
writel(fbi->pcr, fbi->regs + LCDC_PCR);
+#ifndef PWMR_BACKLIGHT_AVAILABLE
+ writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
+#endif
writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
@@ -847,7 +858,9 @@ static int __init imxfb_probe(struct platform_device *pdev)
imxfb_enable_controller(fbi);
fbi->pdev = pdev;
+#ifdef PWMR_BACKLIGHT_AVAILABLE
imxfb_init_backlight(fbi);
+#endif
return 0;
@@ -885,7 +898,9 @@ static int __devexit imxfb_remove(struct platform_device *pdev)
imxfb_disable_controller(fbi);
+#ifdef PWMR_BACKLIGHT_AVAILABLE
imxfb_exit_backlight(fbi);
+#endif
unregister_framebuffer(info);
pdata = pdev->dev.platform_data;
diff --git a/drivers/video/leo.c b/drivers/video/leo.c
index ad677637ffbb..b599e5e36ced 100644
--- a/drivers/video/leo.c
+++ b/drivers/video/leo.c
@@ -529,7 +529,7 @@ static void leo_fixup_var_rgb(struct fb_var_screeninfo *var)
var->transp.length = 0;
}
-static void leo_unmap_regs(struct of_device *op, struct fb_info *info,
+static void leo_unmap_regs(struct platform_device *op, struct fb_info *info,
struct leo_par *par)
{
if (par->lc_ss0_usr)
@@ -547,7 +547,7 @@ static void leo_unmap_regs(struct of_device *op, struct fb_info *info,
of_iounmap(&op->resource[0], info->screen_base, 0x800000);
}
-static int __devinit leo_probe(struct of_device *op,
+static int __devinit leo_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -637,7 +637,7 @@ out_err:
return err;
}
-static int __devexit leo_remove(struct of_device *op)
+static int __devexit leo_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct leo_par *par = info->par;
diff --git a/drivers/video/mb862xx/mb862xxfb.c b/drivers/video/mb862xx/mb862xxfb.c
index 4e2b8cc3d460..b1c4374cf940 100644
--- a/drivers/video/mb862xx/mb862xxfb.c
+++ b/drivers/video/mb862xx/mb862xxfb.c
@@ -550,7 +550,7 @@ static int mb862xx_gdc_init(struct mb862xxfb_par *par)
return 0;
}
-static int __devinit of_platform_mb862xx_probe(struct of_device *ofdev,
+static int __devinit of_platform_mb862xx_probe(struct platform_device *ofdev,
const struct of_device_id *id)
{
struct device_node *np = ofdev->dev.of_node;
@@ -669,7 +669,7 @@ fbrel:
return ret;
}
-static int __devexit of_platform_mb862xx_remove(struct of_device *ofdev)
+static int __devexit of_platform_mb862xx_remove(struct platform_device *ofdev)
{
struct fb_info *fbi = dev_get_drvdata(&ofdev->dev);
struct mb862xxfb_par *par = fbi->par;
diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c
index c1ff271017a9..7c316c34dfca 100644
--- a/drivers/video/msm/mddi.c
+++ b/drivers/video/msm/mddi.c
@@ -187,10 +187,8 @@ static void mddi_wait_interrupt(struct mddi_info *mddi, uint32_t intmask);
static void mddi_handle_rev_data_avail(struct mddi_info *mddi)
{
- union mddi_rev *rev = mddi->rev_data;
uint32_t rev_data_count;
uint32_t rev_crc_err_count;
- int i;
struct reg_read_info *ri;
size_t prev_offset;
uint16_t length;
@@ -670,7 +668,7 @@ static int __init mddi_rev_data_setup(struct mddi_info *mddi)
return 0;
}
-static int __init mddi_probe(struct platform_device *pdev)
+static int __devinit mddi_probe(struct platform_device *pdev)
{
struct msm_mddi_platform_data *pdata = pdev->dev.platform_data;
struct mddi_info *mddi = &mddi_info[pdev->id];
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 19c01c6208e8..3c28db03ad39 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -258,7 +258,6 @@ int get_img(struct mdp_img *img, struct fb_info *info,
{
int put_needed, ret = 0;
struct file *file;
- unsigned long vstart;
file = fget_light(img->memory_id, &put_needed);
if (file == NULL)
diff --git a/drivers/video/p9100.c b/drivers/video/p9100.c
index 688b055abab2..b6c3fc2db632 100644
--- a/drivers/video/p9100.c
+++ b/drivers/video/p9100.c
@@ -249,7 +249,7 @@ static void p9100_init_fix(struct fb_info *info, int linebytes, struct device_no
info->fix.accel = FB_ACCEL_SUN_CGTHREE;
}
-static int __devinit p9100_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit p9100_probe(struct platform_device *op, const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
struct fb_info *info;
@@ -326,7 +326,7 @@ out_err:
return err;
}
-static int __devexit p9100_remove(struct of_device *op)
+static int __devexit p9100_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct p9100_par *par = info->par;
diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c
index 72a1f4c04732..a50e1977b316 100644
--- a/drivers/video/platinumfb.c
+++ b/drivers/video/platinumfb.c
@@ -533,7 +533,7 @@ static int __init platinumfb_setup(char *options)
#define invalidate_cache(addr)
#endif
-static int __devinit platinumfb_probe(struct of_device* odev,
+static int __devinit platinumfb_probe(struct platform_device* odev,
const struct of_device_id *match)
{
struct device_node *dp = odev->dev.of_node;
@@ -646,7 +646,7 @@ static int __devinit platinumfb_probe(struct of_device* odev,
return rc;
}
-static int __devexit platinumfb_remove(struct of_device* odev)
+static int __devexit platinumfb_remove(struct platform_device* odev)
{
struct fb_info *info = dev_get_drvdata(&odev->dev);
struct fb_info_platinum *pinfo = info->par;
diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c
index 9682ecc60e12..f9aca9d13d1b 100644
--- a/drivers/video/s3c-fb.c
+++ b/drivers/video/s3c-fb.c
@@ -1,7 +1,7 @@
/* linux/drivers/video/s3c-fb.c
*
* Copyright 2008 Openmoko Inc.
- * Copyright 2008 Simtec Electronics
+ * Copyright 2008-2010 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
* http://armlinux.simtec.co.uk/
*
@@ -9,7 +9,7 @@
*
* 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.
+ * published by the Free Software FoundatIon.
*/
#include <linux/kernel.h>
@@ -21,9 +21,11 @@
#include <linux/clk.h>
#include <linux/fb.h>
#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
#include <mach/map.h>
-#include <mach/regs-fb.h>
+#include <plat/regs-fb-v4.h>
#include <plat/fb.h>
/* This driver will export a number of framebuffer interfaces depending
@@ -36,9 +38,9 @@
* output timings and as the control for the output power-down state.
*/
-/* note, some of the functions that get called are derived from including
- * <mach/regs-fb.h> as they are specific to the architecture that the code
- * is being built for.
+/* note, the previous use of <mach/regs-fb.h> to get platform specific data
+ * has been replaced by using the platform device name to pick the correct
+ * configuration data for the system.
*/
#ifdef CONFIG_FB_S3C_DEBUG_REGWRITE
@@ -48,13 +50,108 @@
__raw_writel(v, r); } while(0)
#endif /* FB_S3C_DEBUG_REGWRITE */
+/* irq_flags bits */
+#define S3C_FB_VSYNC_IRQ_EN 0
+
+#define VSYNC_TIMEOUT_MSEC 50
+
struct s3c_fb;
+#define VALID_BPP(x) (1 << ((x) - 1))
+
+#define OSD_BASE(win, variant) ((variant).osd + ((win) * (variant).osd_stride))
+#define VIDOSD_A(win, variant) (OSD_BASE(win, variant) + 0x00)
+#define VIDOSD_B(win, variant) (OSD_BASE(win, variant) + 0x04)
+#define VIDOSD_C(win, variant) (OSD_BASE(win, variant) + 0x08)
+#define VIDOSD_D(win, variant) (OSD_BASE(win, variant) + 0x0C)
+
+/**
+ * struct s3c_fb_variant - fb variant information
+ * @is_2443: Set if S3C2443/S3C2416 style hardware.
+ * @nr_windows: The number of windows.
+ * @vidtcon: The base for the VIDTCONx registers
+ * @wincon: The base for the WINxCON registers.
+ * @winmap: The base for the WINxMAP registers.
+ * @keycon: The abse for the WxKEYCON registers.
+ * @buf_start: Offset of buffer start registers.
+ * @buf_size: Offset of buffer size registers.
+ * @buf_end: Offset of buffer end registers.
+ * @osd: The base for the OSD registers.
+ * @palette: Address of palette memory, or 0 if none.
+ * @has_prtcon: Set if has PRTCON register.
+ * @has_shadowcon: Set if has SHADOWCON register.
+ */
+struct s3c_fb_variant {
+ unsigned int is_2443:1;
+ unsigned short nr_windows;
+ unsigned short vidtcon;
+ unsigned short wincon;
+ unsigned short winmap;
+ unsigned short keycon;
+ unsigned short buf_start;
+ unsigned short buf_end;
+ unsigned short buf_size;
+ unsigned short osd;
+ unsigned short osd_stride;
+ unsigned short palette[S3C_FB_MAX_WIN];
+
+ unsigned int has_prtcon:1;
+ unsigned int has_shadowcon:1;
+};
+
+/**
+ * struct s3c_fb_win_variant
+ * @has_osd_c: Set if has OSD C register.
+ * @has_osd_d: Set if has OSD D register.
+ * @has_osd_alpha: Set if can change alpha transparency for a window.
+ * @palette_sz: Size of palette in entries.
+ * @palette_16bpp: Set if palette is 16bits wide.
+ * @osd_size_off: If != 0, supports setting up OSD for a window; the appropriate
+ * register is located at the given offset from OSD_BASE.
+ * @valid_bpp: 1 bit per BPP setting to show valid bits-per-pixel.
+ *
+ * valid_bpp bit x is set if (x+1)BPP is supported.
+ */
+struct s3c_fb_win_variant {
+ unsigned int has_osd_c:1;
+ unsigned int has_osd_d:1;
+ unsigned int has_osd_alpha:1;
+ unsigned int palette_16bpp:1;
+ unsigned short osd_size_off;
+ unsigned short palette_sz;
+ u32 valid_bpp;
+};
+
+/**
+ * struct s3c_fb_driverdata - per-device type driver data for init time.
+ * @variant: The variant information for this driver.
+ * @win: The window information for each window.
+ */
+struct s3c_fb_driverdata {
+ struct s3c_fb_variant variant;
+ struct s3c_fb_win_variant *win[S3C_FB_MAX_WIN];
+};
+
+/**
+ * struct s3c_fb_palette - palette information
+ * @r: Red bitfield.
+ * @g: Green bitfield.
+ * @b: Blue bitfield.
+ * @a: Alpha bitfield.
+ */
+struct s3c_fb_palette {
+ struct fb_bitfield r;
+ struct fb_bitfield g;
+ struct fb_bitfield b;
+ struct fb_bitfield a;
+};
+
/**
* struct s3c_fb_win - per window private data for each framebuffer.
* @windata: The platform data supplied for the window configuration.
* @parent: The hardware that this window is part of.
* @fbinfo: Pointer pack to the framebuffer info for this window.
+ * @varint: The variant information for this window.
* @palette_buffer: Buffer/cache to hold palette entries.
* @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/
* @index: The window number of this window.
@@ -65,6 +162,7 @@ struct s3c_fb_win {
struct s3c_fb *parent;
struct fb_info *fbinfo;
struct s3c_fb_palette palette;
+ struct s3c_fb_win_variant variant;
u32 *palette_buffer;
u32 pseudo_palette[16];
@@ -72,37 +170,54 @@ struct s3c_fb_win {
};
/**
+ * struct s3c_fb_vsync - vsync information
+ * @wait: a queue for processes waiting for vsync
+ * @count: vsync interrupt count
+ */
+struct s3c_fb_vsync {
+ wait_queue_head_t wait;
+ unsigned int count;
+};
+
+/**
* struct s3c_fb - overall hardware state of the hardware
* @dev: The device that we bound to, for printing, etc.
* @regs_res: The resource we claimed for the IO registers.
* @bus_clk: The clk (hclk) feeding our interface and possibly pixclk.
* @regs: The mapped hardware registers.
+ * @variant: Variant information for this hardware.
* @enabled: A bitmask of enabled hardware windows.
* @pdata: The platform configuration data passed with the device.
* @windows: The hardware windows that have been claimed.
+ * @irq_no: IRQ line number
+ * @irq_flags: irq flags
+ * @vsync_info: VSYNC-related information (count, queues...)
*/
struct s3c_fb {
struct device *dev;
struct resource *regs_res;
struct clk *bus_clk;
void __iomem *regs;
+ struct s3c_fb_variant variant;
unsigned char enabled;
struct s3c_fb_platdata *pdata;
struct s3c_fb_win *windows[S3C_FB_MAX_WIN];
+
+ int irq_no;
+ unsigned long irq_flags;
+ struct s3c_fb_vsync vsync_info;
};
/**
- * s3c_fb_win_has_palette() - determine if a mode has a palette
- * @win: The window number being queried.
- * @bpp: The number of bits per pixel to test.
- *
- * Work out if the given window supports palletised data at the specified bpp.
+ * s3c_fb_validate_win_bpp - validate the bits-per-pixel for this mode.
+ * @win: The device window.
+ * @bpp: The bit depth.
*/
-static int s3c_fb_win_has_palette(unsigned int win, unsigned int bpp)
+static bool s3c_fb_validate_win_bpp(struct s3c_fb_win *win, unsigned int bpp)
{
- return s3c_fb_win_pal_size(win) <= (1 << bpp);
+ return win->variant.valid_bpp & VALID_BPP(bpp);
}
/**
@@ -125,7 +240,7 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var,
var->xres_virtual = max((unsigned int)windata->virtual_x, var->xres);
var->yres_virtual = max((unsigned int)windata->virtual_y, var->yres);
- if (!s3c_fb_validate_win_bpp(win->index, var->bits_per_pixel)) {
+ if (!s3c_fb_validate_win_bpp(win, var->bits_per_pixel)) {
dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n",
win->index, var->bits_per_pixel);
return -EINVAL;
@@ -140,7 +255,7 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var,
case 2:
case 4:
case 8:
- if (!s3c_fb_win_has_palette(win->index, var->bits_per_pixel)) {
+ if (sfb->variant.palette[win->index] != 0) {
/* non palletised, A:1,R:2,G:3,B:2 mode */
var->red.offset = 4;
var->green.offset = 2;
@@ -255,6 +370,66 @@ static int s3c_fb_align_word(unsigned int bpp, unsigned int pix)
}
/**
+ * vidosd_set_size() - set OSD size for a window
+ *
+ * @win: the window to set OSD size for
+ * @size: OSD size register value
+ */
+static void vidosd_set_size(struct s3c_fb_win *win, u32 size)
+{
+ struct s3c_fb *sfb = win->parent;
+
+ /* OSD can be set up if osd_size_off != 0 for this window */
+ if (win->variant.osd_size_off)
+ writel(size, sfb->regs + OSD_BASE(win->index, sfb->variant)
+ + win->variant.osd_size_off);
+}
+
+/**
+ * vidosd_set_alpha() - set alpha transparency for a window
+ *
+ * @win: the window to set OSD size for
+ * @alpha: alpha register value
+ */
+static void vidosd_set_alpha(struct s3c_fb_win *win, u32 alpha)
+{
+ struct s3c_fb *sfb = win->parent;
+
+ if (win->variant.has_osd_alpha)
+ writel(alpha, sfb->regs + VIDOSD_C(win->index, sfb->variant));
+}
+
+/**
+ * shadow_protect_win() - disable updating values from shadow registers at vsync
+ *
+ * @win: window to protect registers for
+ * @protect: 1 to protect (disable updates)
+ */
+static void shadow_protect_win(struct s3c_fb_win *win, bool protect)
+{
+ struct s3c_fb *sfb = win->parent;
+ u32 reg;
+
+ if (protect) {
+ if (sfb->variant.has_prtcon) {
+ writel(PRTCON_PROTECT, sfb->regs + PRTCON);
+ } else if (sfb->variant.has_shadowcon) {
+ reg = readl(sfb->regs + SHADOWCON);
+ writel(reg | SHADOWCON_WINx_PROTECT(win->index),
+ sfb->regs + SHADOWCON);
+ }
+ } else {
+ if (sfb->variant.has_prtcon) {
+ writel(0, sfb->regs + PRTCON);
+ } else if (sfb->variant.has_shadowcon) {
+ reg = readl(sfb->regs + SHADOWCON);
+ writel(reg & ~SHADOWCON_WINx_PROTECT(win->index),
+ sfb->regs + SHADOWCON);
+ }
+ }
+}
+
+/**
* s3c_fb_set_par() - framebuffer request to set new framebuffer state.
* @info: The framebuffer to change.
*
@@ -266,14 +441,17 @@ static int s3c_fb_set_par(struct fb_info *info)
struct s3c_fb_win *win = info->par;
struct s3c_fb *sfb = win->parent;
void __iomem *regs = sfb->regs;
+ void __iomem *buf = regs;
int win_no = win->index;
- u32 osdc_data = 0;
+ u32 alpha = 0;
u32 data;
u32 pagewidth;
int clkdiv;
dev_dbg(sfb->dev, "setting framebuffer parameters\n");
+ shadow_protect_win(win, 1);
+
switch (var->bits_per_pixel) {
case 32:
case 24:
@@ -282,7 +460,7 @@ static int s3c_fb_set_par(struct fb_info *info)
info->fix.visual = FB_VISUAL_TRUECOLOR;
break;
case 8:
- if (s3c_fb_win_has_palette(win_no, 8))
+ if (win->variant.palette_sz >= 256)
info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
else
info->fix.visual = FB_VISUAL_TRUECOLOR;
@@ -297,12 +475,15 @@ static int s3c_fb_set_par(struct fb_info *info)
info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
+ info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0;
+ info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0;
+
/* disable the window whilst we update it */
writel(0, regs + WINCON(win_no));
- /* use window 0 as the basis for the lcd output timings */
+ /* use platform specified window as the basis for the lcd timings */
- if (win_no == 0) {
+ if (win_no == sfb->pdata->default_win) {
clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock);
data = sfb->pdata->vidcon0;
@@ -315,6 +496,9 @@ static int s3c_fb_set_par(struct fb_info *info)
/* write the timing data to the panel */
+ if (sfb->variant.is_2443)
+ data |= (1 << 5);
+
data |= VIDCON0_ENVID | VIDCON0_ENVID_F;
writel(data, regs + VIDCON0);
@@ -322,53 +506,54 @@ static int s3c_fb_set_par(struct fb_info *info)
VIDTCON0_VFPD(var->lower_margin - 1) |
VIDTCON0_VSPW(var->vsync_len - 1);
- writel(data, regs + VIDTCON0);
+ writel(data, regs + sfb->variant.vidtcon);
data = VIDTCON1_HBPD(var->left_margin - 1) |
VIDTCON1_HFPD(var->right_margin - 1) |
VIDTCON1_HSPW(var->hsync_len - 1);
- writel(data, regs + VIDTCON1);
+ /* VIDTCON1 */
+ writel(data, regs + sfb->variant.vidtcon + 4);
data = VIDTCON2_LINEVAL(var->yres - 1) |
VIDTCON2_HOZVAL(var->xres - 1);
- writel(data, regs + VIDTCON2);
+ writel(data, regs +sfb->variant.vidtcon + 8 );
}
/* write the buffer address */
- writel(info->fix.smem_start, regs + VIDW_BUF_START(win_no));
+ /* start and end registers stride is 8 */
+ buf = regs + win_no * 8;
+
+ writel(info->fix.smem_start, buf + sfb->variant.buf_start);
data = info->fix.smem_start + info->fix.line_length * var->yres;
- writel(data, regs + VIDW_BUF_END(win_no));
+ writel(data, buf + sfb->variant.buf_end);
pagewidth = (var->xres * var->bits_per_pixel) >> 3;
data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) |
VIDW_BUF_SIZE_PAGEWIDTH(pagewidth);
- writel(data, regs + VIDW_BUF_SIZE(win_no));
+ writel(data, regs + sfb->variant.buf_size + (win_no * 4));
/* write 'OSD' registers to control position of framebuffer */
data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0);
- writel(data, regs + VIDOSD_A(win_no));
+ writel(data, regs + VIDOSD_A(win_no, sfb->variant));
data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel,
var->xres - 1)) |
VIDOSDxB_BOTRIGHT_Y(var->yres - 1);
- writel(data, regs + VIDOSD_B(win_no));
+ writel(data, regs + VIDOSD_B(win_no, sfb->variant));
data = var->xres * var->yres;
- osdc_data = VIDISD14C_ALPHA1_R(0xf) |
+ alpha = VIDISD14C_ALPHA1_R(0xf) |
VIDISD14C_ALPHA1_G(0xf) |
VIDISD14C_ALPHA1_B(0xf);
- if (s3c_fb_has_osd_d(win_no)) {
- writel(data, regs + VIDOSD_D(win_no));
- writel(osdc_data, regs + VIDOSD_C(win_no));
- } else
- writel(data, regs + VIDOSD_C(win_no));
+ vidosd_set_alpha(win, alpha);
+ vidosd_set_size(win, data);
data = WINCONx_ENWIN;
@@ -424,13 +609,15 @@ static int s3c_fb_set_par(struct fb_info *info)
else
data |= WINCON0_BPPMODE_24BPP_888;
+ data |= WINCONx_WSWP;
data |= WINCONx_BURSTLEN_16WORD;
break;
}
- /* It has no color key control register for window0 */
+ /* Enable the colour keying for the window below this one */
if (win_no > 0) {
u32 keycon0_data = 0, keycon1_data = 0;
+ void __iomem *keycon = regs + sfb->variant.keycon;
keycon0_data = ~(WxKEYCON0_KEYBL_EN |
WxKEYCON0_KEYEN_F |
@@ -438,12 +625,23 @@ static int s3c_fb_set_par(struct fb_info *info)
keycon1_data = WxKEYCON1_COLVAL(0xffffff);
- writel(keycon0_data, regs + WxKEYCONy(win_no-1, 0));
- writel(keycon1_data, regs + WxKEYCONy(win_no-1, 1));
+ keycon += (win_no - 1) * 8;
+
+ writel(keycon0_data, keycon + WKEYCON0);
+ writel(keycon1_data, keycon + WKEYCON1);
+ }
+
+ writel(data, regs + sfb->variant.wincon + (win_no * 4));
+ writel(0x0, regs + sfb->variant.winmap + (win_no * 4));
+
+ /* Enable DMA channel for this window */
+ if (sfb->variant.has_shadowcon) {
+ data = readl(sfb->regs + SHADOWCON);
+ data |= SHADOWCON_CHx_ENABLE(win_no);
+ writel(data, sfb->regs + SHADOWCON);
}
- writel(data, regs + WINCON(win_no));
- writel(0x0, regs + WINxMAP(win_no));
+ shadow_protect_win(win, 0);
return 0;
}
@@ -470,7 +668,7 @@ static void s3c_fb_update_palette(struct s3c_fb *sfb,
void __iomem *palreg;
u32 palcon;
- palreg = sfb->regs + s3c_fb_pal_reg(win->index, reg);
+ palreg = sfb->regs + sfb->variant.palette[win->index];
dev_dbg(sfb->dev, "%s: win %d, reg %d (%p): %08x\n",
__func__, win->index, reg, palreg, value);
@@ -480,10 +678,10 @@ static void s3c_fb_update_palette(struct s3c_fb *sfb,
palcon = readl(sfb->regs + WPALCON);
writel(palcon | WPALCON_PAL_UPDATE, sfb->regs + WPALCON);
- if (s3c_fb_pal_is16(win->index))
- writew(value, palreg);
+ if (win->variant.palette_16bpp)
+ writew(value, palreg + (reg * 2));
else
- writel(value, palreg);
+ writel(value, palreg + (reg * 4));
writel(palcon, sfb->regs + WPALCON);
}
@@ -532,7 +730,7 @@ static int s3c_fb_setcolreg(unsigned regno,
break;
case FB_VISUAL_PSEUDOCOLOR:
- if (regno < s3c_fb_win_pal_size(win->index)) {
+ if (regno < win->variant.palette_sz) {
val = chan_to_field(red, &win->palette.r);
val |= chan_to_field(green, &win->palette.g);
val |= chan_to_field(blue, &win->palette.b);
@@ -591,7 +789,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info)
dev_dbg(sfb->dev, "blank mode %d\n", blank_mode);
- wincon = readl(sfb->regs + WINCON(index));
+ wincon = readl(sfb->regs + sfb->variant.wincon + (index * 4));
switch (blank_mode) {
case FB_BLANK_POWERDOWN:
@@ -602,11 +800,11 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info)
case FB_BLANK_NORMAL:
/* disable the DMA and display 0x0 (black) */
writel(WINxMAP_MAP | WINxMAP_MAP_COLOUR(0x0),
- sfb->regs + WINxMAP(index));
+ sfb->regs + sfb->variant.winmap + (index * 4));
break;
case FB_BLANK_UNBLANK:
- writel(0x0, sfb->regs + WINxMAP(index));
+ writel(0x0, sfb->regs + sfb->variant.winmap + (index * 4));
wincon |= WINCONx_ENWIN;
sfb->enabled |= (1 << index);
break;
@@ -617,7 +815,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info)
return 1;
}
- writel(wincon, sfb->regs + WINCON(index));
+ writel(wincon, sfb->regs + sfb->variant.wincon + (index * 4));
/* Check the enabled state to see if we need to be running the
* main LCD interface, as if there are no active windows then
@@ -636,12 +834,185 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info)
/* we're stuck with this until we can do something about overriding
* the power control using the blanking event for a single fb.
*/
- if (index == 0)
+ if (index == sfb->pdata->default_win)
s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0);
return 0;
}
+/**
+ * s3c_fb_pan_display() - Pan the display.
+ *
+ * Note that the offsets can be written to the device at any time, as their
+ * values are latched at each vsync automatically. This also means that only
+ * the last call to this function will have any effect on next vsync, but
+ * there is no need to sleep waiting for it to prevent tearing.
+ *
+ * @var: The screen information to verify.
+ * @info: The framebuffer device.
+ */
+static int s3c_fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct s3c_fb_win *win = info->par;
+ struct s3c_fb *sfb = win->parent;
+ void __iomem *buf = sfb->regs + win->index * 8;
+ unsigned int start_boff, end_boff;
+
+ /* Offset in bytes to the start of the displayed area */
+ start_boff = var->yoffset * info->fix.line_length;
+ /* X offset depends on the current bpp */
+ if (info->var.bits_per_pixel >= 8) {
+ start_boff += var->xoffset * (info->var.bits_per_pixel >> 3);
+ } else {
+ switch (info->var.bits_per_pixel) {
+ case 4:
+ start_boff += var->xoffset >> 1;
+ break;
+ case 2:
+ start_boff += var->xoffset >> 2;
+ break;
+ case 1:
+ start_boff += var->xoffset >> 3;
+ break;
+ default:
+ dev_err(sfb->dev, "invalid bpp\n");
+ return -EINVAL;
+ }
+ }
+ /* Offset in bytes to the end of the displayed area */
+ end_boff = start_boff + var->yres * info->fix.line_length;
+
+ /* Temporarily turn off per-vsync update from shadow registers until
+ * both start and end addresses are updated to prevent corruption */
+ shadow_protect_win(win, 1);
+
+ writel(info->fix.smem_start + start_boff, buf + sfb->variant.buf_start);
+ writel(info->fix.smem_start + end_boff, buf + sfb->variant.buf_end);
+
+ shadow_protect_win(win, 0);
+
+ return 0;
+}
+
+/**
+ * s3c_fb_enable_irq() - enable framebuffer interrupts
+ * @sfb: main hardware state
+ */
+static void s3c_fb_enable_irq(struct s3c_fb *sfb)
+{
+ void __iomem *regs = sfb->regs;
+ u32 irq_ctrl_reg;
+
+ if (!test_and_set_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) {
+ /* IRQ disabled, enable it */
+ irq_ctrl_reg = readl(regs + VIDINTCON0);
+
+ irq_ctrl_reg |= VIDINTCON0_INT_ENABLE;
+ irq_ctrl_reg |= VIDINTCON0_INT_FRAME;
+
+ irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL0_MASK;
+ irq_ctrl_reg |= VIDINTCON0_FRAMESEL0_VSYNC;
+ irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL1_MASK;
+ irq_ctrl_reg |= VIDINTCON0_FRAMESEL1_NONE;
+
+ writel(irq_ctrl_reg, regs + VIDINTCON0);
+ }
+}
+
+/**
+ * s3c_fb_disable_irq() - disable framebuffer interrupts
+ * @sfb: main hardware state
+ */
+static void s3c_fb_disable_irq(struct s3c_fb *sfb)
+{
+ void __iomem *regs = sfb->regs;
+ u32 irq_ctrl_reg;
+
+ if (test_and_clear_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) {
+ /* IRQ enabled, disable it */
+ irq_ctrl_reg = readl(regs + VIDINTCON0);
+
+ irq_ctrl_reg &= ~VIDINTCON0_INT_FRAME;
+ irq_ctrl_reg &= ~VIDINTCON0_INT_ENABLE;
+
+ writel(irq_ctrl_reg, regs + VIDINTCON0);
+ }
+}
+
+static irqreturn_t s3c_fb_irq(int irq, void *dev_id)
+{
+ struct s3c_fb *sfb = dev_id;
+ void __iomem *regs = sfb->regs;
+ u32 irq_sts_reg;
+
+ irq_sts_reg = readl(regs + VIDINTCON1);
+
+ if (irq_sts_reg & VIDINTCON1_INT_FRAME) {
+
+ /* VSYNC interrupt, accept it */
+ writel(VIDINTCON1_INT_FRAME, regs + VIDINTCON1);
+
+ sfb->vsync_info.count++;
+ wake_up_interruptible(&sfb->vsync_info.wait);
+ }
+
+ /* We only support waiting for VSYNC for now, so it's safe
+ * to always disable irqs here.
+ */
+ s3c_fb_disable_irq(sfb);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * s3c_fb_wait_for_vsync() - sleep until next VSYNC interrupt or timeout
+ * @sfb: main hardware state
+ * @crtc: head index.
+ */
+static int s3c_fb_wait_for_vsync(struct s3c_fb *sfb, u32 crtc)
+{
+ unsigned long count;
+ int ret;
+
+ if (crtc != 0)
+ return -ENODEV;
+
+ count = sfb->vsync_info.count;
+ s3c_fb_enable_irq(sfb);
+ ret = wait_event_interruptible_timeout(sfb->vsync_info.wait,
+ count != sfb->vsync_info.count,
+ msecs_to_jiffies(VSYNC_TIMEOUT_MSEC));
+ if (ret == 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ struct s3c_fb_win *win = info->par;
+ struct s3c_fb *sfb = win->parent;
+ int ret;
+ u32 crtc;
+
+ switch (cmd) {
+ case FBIO_WAITFORVSYNC:
+ if (get_user(crtc, (u32 __user *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = s3c_fb_wait_for_vsync(sfb, crtc);
+ break;
+ default:
+ ret = -ENOTTY;
+ }
+
+ return ret;
+}
+
static struct fb_ops s3c_fb_ops = {
.owner = THIS_MODULE,
.fb_check_var = s3c_fb_check_var,
@@ -651,9 +1022,33 @@ static struct fb_ops s3c_fb_ops = {
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
+ .fb_pan_display = s3c_fb_pan_display,
+ .fb_ioctl = s3c_fb_ioctl,
};
/**
+ * s3c_fb_missing_pixclock() - calculates pixel clock
+ * @mode: The video mode to change.
+ *
+ * Calculate the pixel clock when none has been given through platform data.
+ */
+static void __devinit s3c_fb_missing_pixclock(struct fb_videomode *mode)
+{
+ u64 pixclk = 1000000000000ULL;
+ u32 div;
+
+ div = mode->left_margin + mode->hsync_len + mode->right_margin +
+ mode->xres;
+ div *= mode->upper_margin + mode->vsync_len + mode->lower_margin +
+ mode->yres;
+ div *= mode->refresh ? : 60;
+
+ do_div(pixclk, div);
+
+ mode->pixclock = pixclk;
+}
+
+/**
* s3c_fb_alloc_memory() - allocate display memory for framebuffer window
* @sfb: The base resources for the hardware.
* @win: The window to initialise memory for.
@@ -711,7 +1106,8 @@ static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win)
{
struct fb_info *fbi = win->fbinfo;
- dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len),
+ if (fbi->screen_base)
+ dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len),
fbi->screen_base, fbi->fix.smem_start);
}
@@ -724,9 +1120,18 @@ static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win)
*/
static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win)
{
+ u32 data;
+
if (win->fbinfo) {
+ if (sfb->variant.has_shadowcon) {
+ data = readl(sfb->regs + SHADOWCON);
+ data &= ~SHADOWCON_CHx_ENABLE(win->index);
+ data &= ~SHADOWCON_CHx_LOCAL_ENABLE(win->index);
+ writel(data, sfb->regs + SHADOWCON);
+ }
unregister_framebuffer(win->fbinfo);
- fb_dealloc_cmap(&win->fbinfo->cmap);
+ if (win->fbinfo->cmap.len)
+ fb_dealloc_cmap(&win->fbinfo->cmap);
s3c_fb_free_memory(sfb, win);
framebuffer_release(win->fbinfo);
}
@@ -735,12 +1140,14 @@ static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win)
/**
* s3c_fb_probe_win() - register an hardware window
* @sfb: The base resources for the hardware
+ * @variant: The variant information for this window.
* @res: Pointer to where to place the resultant window.
*
* Allocate and do the basic initialisation for one of the hardware's graphics
* windows.
*/
static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
+ struct s3c_fb_win_variant *variant,
struct s3c_fb_win **res)
{
struct fb_var_screeninfo *var;
@@ -751,9 +1158,11 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
int palette_size;
int ret;
- dev_dbg(sfb->dev, "probing window %d\n", win_no);
+ dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant);
+
+ init_waitqueue_head(&sfb->vsync_info.wait);
- palette_size = s3c_fb_win_pal_size(win_no);
+ palette_size = variant->palette_sz * 4;
fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +
palette_size * sizeof(u32), sfb->dev);
@@ -770,7 +1179,9 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
WARN_ON(windata->win_mode.yres == 0);
win = fbinfo->par;
+ *res = win;
var = &fbinfo->var;
+ win->variant = *variant;
win->fbinfo = fbinfo;
win->parent = sfb;
win->windata = windata;
@@ -784,7 +1195,24 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
}
/* setup the r/b/g positions for the window's palette */
- s3c_fb_init_palette(win_no, &win->palette);
+ if (win->variant.palette_16bpp) {
+ /* Set RGB 5:6:5 as default */
+ win->palette.r.offset = 11;
+ win->palette.r.length = 5;
+ win->palette.g.offset = 5;
+ win->palette.g.length = 6;
+ win->palette.b.offset = 0;
+ win->palette.b.length = 5;
+
+ } else {
+ /* Set 8bpp or 8bpp and 1bit alpha */
+ win->palette.r.offset = 16;
+ win->palette.r.length = 8;
+ win->palette.g.offset = 8;
+ win->palette.g.length = 8;
+ win->palette.b.offset = 0;
+ win->palette.b.length = 8;
+ }
/* setup the initial video mode from the window */
fb_videomode_to_var(&fbinfo->var, initmode);
@@ -808,7 +1236,7 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
/* create initial colour map */
- ret = fb_alloc_cmap(&fbinfo->cmap, s3c_fb_win_pal_size(win_no), 1);
+ ret = fb_alloc_cmap(&fbinfo->cmap, win->variant.palette_sz, 1);
if (ret == 0)
fb_set_cmap(&fbinfo->cmap, fbinfo);
else
@@ -826,7 +1254,6 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
return ret;
}
- *res = win;
dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id);
return 0;
@@ -842,18 +1269,19 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
static void s3c_fb_clear_win(struct s3c_fb *sfb, int win)
{
void __iomem *regs = sfb->regs;
-
- writel(0, regs + WINCON(win));
- writel(0xffffff, regs + WxKEYCONy(win, 0));
- writel(0xffffff, regs + WxKEYCONy(win, 1));
-
- writel(0, regs + VIDOSD_A(win));
- writel(0, regs + VIDOSD_B(win));
- writel(0, regs + VIDOSD_C(win));
+ u32 reg;
+
+ writel(0, regs + sfb->variant.wincon + (win * 4));
+ writel(0, regs + VIDOSD_A(win, sfb->variant));
+ writel(0, regs + VIDOSD_B(win, sfb->variant));
+ writel(0, regs + VIDOSD_C(win, sfb->variant));
+ reg = readl(regs + SHADOWCON);
+ writel(reg & ~SHADOWCON_WINx_PROTECT(win), regs + SHADOWCON);
}
static int __devinit s3c_fb_probe(struct platform_device *pdev)
{
+ struct s3c_fb_driverdata *fbdrv;
struct device *dev = &pdev->dev;
struct s3c_fb_platdata *pd;
struct s3c_fb *sfb;
@@ -861,6 +1289,13 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
int win;
int ret = 0;
+ fbdrv = (struct s3c_fb_driverdata *)platform_get_device_id(pdev)->driver_data;
+
+ if (fbdrv->variant.nr_windows > S3C_FB_MAX_WIN) {
+ dev_err(dev, "too many windows, cannot attach\n");
+ return -EINVAL;
+ }
+
pd = pdev->dev.platform_data;
if (!pd) {
dev_err(dev, "no platform data specified\n");
@@ -873,8 +1308,11 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ dev_dbg(dev, "allocate new framebuffer %p\n", sfb);
+
sfb->dev = dev;
sfb->pdata = pd;
+ sfb->variant = fbdrv->variant;
sfb->bus_clk = clk_get(dev, "lcd");
if (IS_ERR(sfb->bus_clk)) {
@@ -906,6 +1344,20 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
goto err_req_region;
}
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "failed to acquire irq resource\n");
+ ret = -ENOENT;
+ goto err_ioremap;
+ }
+ sfb->irq_no = res->start;
+ ret = request_irq(sfb->irq_no, s3c_fb_irq,
+ 0, "s3c_fb", sfb);
+ if (ret) {
+ dev_err(dev, "irq request failed\n");
+ goto err_ioremap;
+ }
+
dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs);
/* setup gpio and output polarity controls */
@@ -916,21 +1368,34 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
/* zero all windows before we do anything */
- for (win = 0; win < S3C_FB_MAX_WIN; win++)
+ for (win = 0; win < fbdrv->variant.nr_windows; win++)
s3c_fb_clear_win(sfb, win);
+ /* initialise colour key controls */
+ for (win = 0; win < (fbdrv->variant.nr_windows - 1); win++) {
+ void __iomem *regs = sfb->regs + sfb->variant.keycon;
+
+ regs += (win * 8);
+ writel(0xffffff, regs + WKEYCON0);
+ writel(0xffffff, regs + WKEYCON1);
+ }
+
/* we have the register setup, start allocating framebuffers */
- for (win = 0; win < S3C_FB_MAX_WIN; win++) {
+ for (win = 0; win < fbdrv->variant.nr_windows; win++) {
if (!pd->win[win])
continue;
- ret = s3c_fb_probe_win(sfb, win, &sfb->windows[win]);
+ if (!pd->win[win]->win_mode.pixclock)
+ s3c_fb_missing_pixclock(&pd->win[win]->win_mode);
+
+ ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win],
+ &sfb->windows[win]);
if (ret < 0) {
dev_err(dev, "failed to create window %d\n", win);
for (; win >= 0; win--)
s3c_fb_release_win(sfb, sfb->windows[win]);
- goto err_ioremap;
+ goto err_irq;
}
}
@@ -938,6 +1403,9 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
return 0;
+err_irq:
+ free_irq(sfb->irq_no, sfb);
+
err_ioremap:
iounmap(sfb->regs);
@@ -970,6 +1438,8 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev)
if (sfb->windows[win])
s3c_fb_release_win(sfb, sfb->windows[win]);
+ free_irq(sfb->irq_no, sfb);
+
iounmap(sfb->regs);
clk_disable(sfb->bus_clk);
@@ -1016,9 +1486,17 @@ static int s3c_fb_resume(struct platform_device *pdev)
writel(pd->vidcon1, sfb->regs + VIDCON1);
/* zero all windows before we do anything */
- for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++)
+ for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++)
s3c_fb_clear_win(sfb, win_no);
+ for (win_no = 0; win_no < sfb->variant.nr_windows - 1; win_no++) {
+ void __iomem *regs = sfb->regs + sfb->variant.keycon;
+
+ regs += (win_no * 8);
+ writel(0xffffff, regs + WKEYCON0);
+ writel(0xffffff, regs + WKEYCON1);
+ }
+
/* restore framebuffers */
for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) {
win = sfb->windows[win_no];
@@ -1036,11 +1514,208 @@ static int s3c_fb_resume(struct platform_device *pdev)
#define s3c_fb_resume NULL
#endif
+
+#define VALID_BPP124 (VALID_BPP(1) | VALID_BPP(2) | VALID_BPP(4))
+#define VALID_BPP1248 (VALID_BPP124 | VALID_BPP(8))
+
+static struct s3c_fb_win_variant s3c_fb_data_64xx_wins[] = {
+ [0] = {
+ .has_osd_c = 1,
+ .osd_size_off = 0x8,
+ .palette_sz = 256,
+ .valid_bpp = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24),
+ },
+ [1] = {
+ .has_osd_c = 1,
+ .has_osd_d = 1,
+ .osd_size_off = 0x12,
+ .has_osd_alpha = 1,
+ .palette_sz = 256,
+ .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |
+ VALID_BPP(18) | VALID_BPP(19) |
+ VALID_BPP(24) | VALID_BPP(25)),
+ },
+ [2] = {
+ .has_osd_c = 1,
+ .has_osd_d = 1,
+ .osd_size_off = 0x12,
+ .has_osd_alpha = 1,
+ .palette_sz = 16,
+ .palette_16bpp = 1,
+ .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |
+ VALID_BPP(18) | VALID_BPP(19) |
+ VALID_BPP(24) | VALID_BPP(25)),
+ },
+ [3] = {
+ .has_osd_c = 1,
+ .has_osd_alpha = 1,
+ .palette_sz = 16,
+ .palette_16bpp = 1,
+ .valid_bpp = (VALID_BPP124 | VALID_BPP(16) |
+ VALID_BPP(18) | VALID_BPP(19) |
+ VALID_BPP(24) | VALID_BPP(25)),
+ },
+ [4] = {
+ .has_osd_c = 1,
+ .has_osd_alpha = 1,
+ .palette_sz = 4,
+ .palette_16bpp = 1,
+ .valid_bpp = (VALID_BPP(1) | VALID_BPP(2) |
+ VALID_BPP(16) | VALID_BPP(18) |
+ VALID_BPP(24) | VALID_BPP(25)),
+ },
+};
+
+static struct s3c_fb_driverdata s3c_fb_data_64xx = {
+ .variant = {
+ .nr_windows = 5,
+ .vidtcon = VIDTCON0,
+ .wincon = WINCON(0),
+ .winmap = WINxMAP(0),
+ .keycon = WKEYCON,
+ .osd = VIDOSD_BASE,
+ .osd_stride = 16,
+ .buf_start = VIDW_BUF_START(0),
+ .buf_size = VIDW_BUF_SIZE(0),
+ .buf_end = VIDW_BUF_END(0),
+
+ .palette = {
+ [0] = 0x400,
+ [1] = 0x800,
+ [2] = 0x300,
+ [3] = 0x320,
+ [4] = 0x340,
+ },
+
+ .has_prtcon = 1,
+ },
+ .win[0] = &s3c_fb_data_64xx_wins[0],
+ .win[1] = &s3c_fb_data_64xx_wins[1],
+ .win[2] = &s3c_fb_data_64xx_wins[2],
+ .win[3] = &s3c_fb_data_64xx_wins[3],
+ .win[4] = &s3c_fb_data_64xx_wins[4],
+};
+
+static struct s3c_fb_driverdata s3c_fb_data_s5pc100 = {
+ .variant = {
+ .nr_windows = 5,
+ .vidtcon = VIDTCON0,
+ .wincon = WINCON(0),
+ .winmap = WINxMAP(0),
+ .keycon = WKEYCON,
+ .osd = VIDOSD_BASE,
+ .osd_stride = 16,
+ .buf_start = VIDW_BUF_START(0),
+ .buf_size = VIDW_BUF_SIZE(0),
+ .buf_end = VIDW_BUF_END(0),
+
+ .palette = {
+ [0] = 0x2400,
+ [1] = 0x2800,
+ [2] = 0x2c00,
+ [3] = 0x3000,
+ [4] = 0x3400,
+ },
+
+ .has_prtcon = 1,
+ },
+ .win[0] = &s3c_fb_data_64xx_wins[0],
+ .win[1] = &s3c_fb_data_64xx_wins[1],
+ .win[2] = &s3c_fb_data_64xx_wins[2],
+ .win[3] = &s3c_fb_data_64xx_wins[3],
+ .win[4] = &s3c_fb_data_64xx_wins[4],
+};
+
+static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = {
+ .variant = {
+ .nr_windows = 5,
+ .vidtcon = VIDTCON0,
+ .wincon = WINCON(0),
+ .winmap = WINxMAP(0),
+ .keycon = WKEYCON,
+ .osd = VIDOSD_BASE,
+ .osd_stride = 16,
+ .buf_start = VIDW_BUF_START(0),
+ .buf_size = VIDW_BUF_SIZE(0),
+ .buf_end = VIDW_BUF_END(0),
+
+ .palette = {
+ [0] = 0x2400,
+ [1] = 0x2800,
+ [2] = 0x2c00,
+ [3] = 0x3000,
+ [4] = 0x3400,
+ },
+
+ .has_shadowcon = 1,
+ },
+ .win[0] = &s3c_fb_data_64xx_wins[0],
+ .win[1] = &s3c_fb_data_64xx_wins[1],
+ .win[2] = &s3c_fb_data_64xx_wins[2],
+ .win[3] = &s3c_fb_data_64xx_wins[3],
+ .win[4] = &s3c_fb_data_64xx_wins[4],
+};
+
+/* S3C2443/S3C2416 style hardware */
+static struct s3c_fb_driverdata s3c_fb_data_s3c2443 = {
+ .variant = {
+ .nr_windows = 2,
+ .is_2443 = 1,
+
+ .vidtcon = 0x08,
+ .wincon = 0x14,
+ .winmap = 0xd0,
+ .keycon = 0xb0,
+ .osd = 0x28,
+ .osd_stride = 12,
+ .buf_start = 0x64,
+ .buf_size = 0x94,
+ .buf_end = 0x7c,
+
+ .palette = {
+ [0] = 0x400,
+ [1] = 0x800,
+ },
+ },
+ .win[0] = &(struct s3c_fb_win_variant) {
+ .palette_sz = 256,
+ .valid_bpp = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24),
+ },
+ .win[1] = &(struct s3c_fb_win_variant) {
+ .has_osd_c = 1,
+ .has_osd_alpha = 1,
+ .palette_sz = 256,
+ .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |
+ VALID_BPP(18) | VALID_BPP(19) |
+ VALID_BPP(24) | VALID_BPP(25) |
+ VALID_BPP(28)),
+ },
+};
+
+static struct platform_device_id s3c_fb_driver_ids[] = {
+ {
+ .name = "s3c-fb",
+ .driver_data = (unsigned long)&s3c_fb_data_64xx,
+ }, {
+ .name = "s5pc100-fb",
+ .driver_data = (unsigned long)&s3c_fb_data_s5pc100,
+ }, {
+ .name = "s5pv210-fb",
+ .driver_data = (unsigned long)&s3c_fb_data_s5pv210,
+ }, {
+ .name = "s3c2443-fb",
+ .driver_data = (unsigned long)&s3c_fb_data_s3c2443,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids);
+
static struct platform_driver s3c_fb_driver = {
.probe = s3c_fb_probe,
.remove = __devexit_p(s3c_fb_remove),
.suspend = s3c_fb_suspend,
.resume = s3c_fb_resume,
+ .id_table = s3c_fb_driver_ids,
.driver = {
.name = "s3c-fb",
.owner = THIS_MODULE,
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
new file mode 100644
index 000000000000..5699ce0c1780
--- /dev/null
+++ b/drivers/video/sh_mipi_dsi.c
@@ -0,0 +1,505 @@
+/*
+ * Renesas SH-mobile MIPI DSI support
+ *
+ * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <video/mipi_display.h>
+#include <video/sh_mipi_dsi.h>
+#include <video/sh_mobile_lcdc.h>
+
+#define CMTSRTCTR 0x80d0
+#define CMTSRTREQ 0x8070
+
+#define DSIINTE 0x0060
+
+/* E.g., sh7372 has 2 MIPI-DSIs - one for each LCDC */
+#define MAX_SH_MIPI_DSI 2
+
+struct sh_mipi {
+ void __iomem *base;
+ struct clk *dsit_clk;
+ struct clk *dsip_clk;
+};
+
+static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI];
+
+/* Protect the above array */
+static DEFINE_MUTEX(array_lock);
+
+static struct sh_mipi *sh_mipi_by_handle(int handle)
+{
+ if (handle >= ARRAY_SIZE(mipi_dsi) || handle < 0)
+ return NULL;
+
+ return mipi_dsi[handle];
+}
+
+static int sh_mipi_send_short(struct sh_mipi *mipi, u8 dsi_cmd,
+ u8 cmd, u8 param)
+{
+ u32 data = (dsi_cmd << 24) | (cmd << 16) | (param << 8);
+ int cnt = 100;
+
+ /* transmit a short packet to LCD panel */
+ iowrite32(1 | data, mipi->base + 0x80d0); /* CMTSRTCTR */
+ iowrite32(1, mipi->base + 0x8070); /* CMTSRTREQ */
+
+ while ((ioread32(mipi->base + 0x8070) & 1) && --cnt)
+ udelay(1);
+
+ return cnt ? 0 : -ETIMEDOUT;
+}
+
+#define LCD_CHAN2MIPI(c) ((c) < LCDC_CHAN_MAINLCD || (c) > LCDC_CHAN_SUBLCD ? \
+ -EINVAL : (c) - 1)
+
+static int sh_mipi_dcs(int handle, u8 cmd)
+{
+ struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle));
+ if (!mipi)
+ return -ENODEV;
+ return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE, cmd, 0);
+}
+
+static int sh_mipi_dcs_param(int handle, u8 cmd, u8 param)
+{
+ struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle));
+ if (!mipi)
+ return -ENODEV;
+ return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd,
+ param);
+}
+
+static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable)
+{
+ /*
+ * enable LCDC data tx, transition to LPS after completion of each HS
+ * packet
+ */
+ iowrite32(0x00000002 | enable, mipi->base + 0x8000); /* DTCTR */
+}
+
+static void sh_mipi_shutdown(struct platform_device *pdev)
+{
+ struct sh_mipi *mipi = platform_get_drvdata(pdev);
+
+ sh_mipi_dsi_enable(mipi, false);
+}
+
+static void mipi_display_on(void *arg, struct fb_info *info)
+{
+ struct sh_mipi *mipi = arg;
+
+ sh_mipi_dsi_enable(mipi, true);
+}
+
+static void mipi_display_off(void *arg)
+{
+ struct sh_mipi *mipi = arg;
+
+ sh_mipi_dsi_enable(mipi, false);
+}
+
+static int __init sh_mipi_setup(struct sh_mipi *mipi,
+ struct sh_mipi_dsi_info *pdata)
+{
+ void __iomem *base = mipi->base;
+ struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan;
+ u32 pctype, datatype, pixfmt;
+ u32 linelength;
+ bool yuv;
+
+ /* Select data format */
+ switch (pdata->data_format) {
+ case MIPI_RGB888:
+ pctype = 0;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
+ pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
+ linelength = ch->lcd_cfg.xres * 3;
+ yuv = false;
+ break;
+ case MIPI_RGB565:
+ pctype = 1;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
+ pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
+ linelength = ch->lcd_cfg.xres * 2;
+ yuv = false;
+ break;
+ case MIPI_RGB666_LP:
+ pctype = 2;
+ datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
+ pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
+ linelength = ch->lcd_cfg.xres * 3;
+ yuv = false;
+ break;
+ case MIPI_RGB666:
+ pctype = 3;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
+ pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
+ linelength = (ch->lcd_cfg.xres * 18 + 7) / 8;
+ yuv = false;
+ break;
+ case MIPI_BGR888:
+ pctype = 8;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
+ pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
+ linelength = ch->lcd_cfg.xres * 3;
+ yuv = false;
+ break;
+ case MIPI_BGR565:
+ pctype = 9;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
+ pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
+ linelength = ch->lcd_cfg.xres * 2;
+ yuv = false;
+ break;
+ case MIPI_BGR666_LP:
+ pctype = 0xa;
+ datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
+ pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
+ linelength = ch->lcd_cfg.xres * 3;
+ yuv = false;
+ break;
+ case MIPI_BGR666:
+ pctype = 0xb;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
+ pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
+ linelength = (ch->lcd_cfg.xres * 18 + 7) / 8;
+ yuv = false;
+ break;
+ case MIPI_YUYV:
+ pctype = 4;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
+ pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
+ linelength = ch->lcd_cfg.xres * 2;
+ yuv = true;
+ break;
+ case MIPI_UYVY:
+ pctype = 5;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
+ pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
+ linelength = ch->lcd_cfg.xres * 2;
+ yuv = true;
+ break;
+ case MIPI_YUV420_L:
+ pctype = 6;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
+ pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
+ linelength = (ch->lcd_cfg.xres * 12 + 7) / 8;
+ yuv = true;
+ break;
+ case MIPI_YUV420:
+ pctype = 7;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
+ pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
+ /* Length of U/V line */
+ linelength = (ch->lcd_cfg.xres + 1) / 2;
+ yuv = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((yuv && ch->interface_type != YUV422) ||
+ (!yuv && ch->interface_type != RGB24))
+ return -EINVAL;
+
+ /* reset DSI link */
+ iowrite32(0x00000001, base); /* SYSCTRL */
+ /* Hold reset for 100 cycles of the slowest of bus, HS byte and LP clock */
+ udelay(50);
+ iowrite32(0x00000000, base); /* SYSCTRL */
+
+ /* setup DSI link */
+
+ /*
+ * Default = ULPS enable |
+ * Contention detection enabled |
+ * EoT packet transmission enable |
+ * CRC check enable |
+ * ECC check enable
+ * additionally enable first two lanes
+ */
+ iowrite32(0x00003703, base + 0x04); /* SYSCONF */
+ /*
+ * T_wakeup = 0x7000
+ * T_hs-trail = 3
+ * T_hs-prepare = 3
+ * T_clk-trail = 3
+ * T_clk-prepare = 2
+ */
+ iowrite32(0x70003332, base + 0x08); /* TIMSET */
+ /* no responses requested */
+ iowrite32(0x00000000, base + 0x18); /* RESREQSET0 */
+ /* request response to packets of type 0x28 */
+ iowrite32(0x00000100, base + 0x1c); /* RESREQSET1 */
+ /* High-speed transmission timeout, default 0xffffffff */
+ iowrite32(0x0fffffff, base + 0x20); /* HSTTOVSET */
+ /* LP reception timeout, default 0xffffffff */
+ iowrite32(0x0fffffff, base + 0x24); /* LPRTOVSET */
+ /* Turn-around timeout, default 0xffffffff */
+ iowrite32(0x0fffffff, base + 0x28); /* TATOVSET */
+ /* Peripheral reset timeout, default 0xffffffff */
+ iowrite32(0x0fffffff, base + 0x2c); /* PRTOVSET */
+ /* Enable timeout counters */
+ iowrite32(0x00000f00, base + 0x30); /* DSICTRL */
+ /* Interrupts not used, disable all */
+ iowrite32(0, base + DSIINTE);
+ /* DSI-Tx bias on */
+ iowrite32(0x00000001, base + 0x70); /* PHYCTRL */
+ udelay(200);
+ /* Deassert resets, power on, set multiplier */
+ iowrite32(0x03070b01, base + 0x70); /* PHYCTRL */
+
+ /* setup l-bridge */
+
+ /*
+ * Enable transmission of all packets,
+ * transmit LPS after each HS packet completion
+ */
+ iowrite32(0x00000006, base + 0x8000); /* DTCTR */
+ /* VSYNC width = 2 (<< 17) */
+ iowrite32(0x00040000 | (pctype << 12) | datatype, base + 0x8020); /* VMCTR1 */
+ /*
+ * Non-burst mode with sync pulses: VSE and HSE are output,
+ * HSA period allowed, no commands in LP
+ */
+ iowrite32(0x00e00000, base + 0x8024); /* VMCTR2 */
+ /*
+ * 0x660 = 1632 bytes per line (RGB24, 544 pixels: see
+ * sh_mobile_lcdc_info.ch[0].lcd_cfg.xres), HSALEN = 1 - default
+ * (unused, since VMCTR2[HSABM] = 0)
+ */
+ iowrite32(1 | (linelength << 16), base + 0x8028); /* VMLEN1 */
+
+ msleep(5);
+
+ /* setup LCD panel */
+
+ /* cf. drivers/video/omap/lcd_mipid.c */
+ sh_mipi_dcs(ch->chan, MIPI_DCS_EXIT_SLEEP_MODE);
+ msleep(120);
+ /*
+ * [7] - Page Address Mode
+ * [6] - Column Address Mode
+ * [5] - Page / Column Address Mode
+ * [4] - Display Device Line Refresh Order
+ * [3] - RGB/BGR Order
+ * [2] - Display Data Latch Data Order
+ * [1] - Flip Horizontal
+ * [0] - Flip Vertical
+ */
+ sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
+ /* cf. set_data_lines() */
+ sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_PIXEL_FORMAT,
+ pixfmt << 4);
+ sh_mipi_dcs(ch->chan, MIPI_DCS_SET_DISPLAY_ON);
+
+ return 0;
+}
+
+static int __init sh_mipi_probe(struct platform_device *pdev)
+{
+ struct sh_mipi *mipi;
+ struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ unsigned long rate, f_current;
+ int idx = pdev->id, ret;
+ char dsip_clk[] = "dsi.p_clk";
+
+ if (!res || idx >= ARRAY_SIZE(mipi_dsi) || !pdata)
+ return -ENODEV;
+
+ mutex_lock(&array_lock);
+ if (idx < 0)
+ for (idx = 0; idx < ARRAY_SIZE(mipi_dsi) && mipi_dsi[idx]; idx++)
+ ;
+
+ if (idx == ARRAY_SIZE(mipi_dsi)) {
+ ret = -EBUSY;
+ goto efindslot;
+ }
+
+ mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
+ if (!mipi) {
+ ret = -ENOMEM;
+ goto ealloc;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "MIPI register region already claimed\n");
+ ret = -EBUSY;
+ goto ereqreg;
+ }
+
+ mipi->base = ioremap(res->start, resource_size(res));
+ if (!mipi->base) {
+ ret = -ENOMEM;
+ goto emap;
+ }
+
+ mipi->dsit_clk = clk_get(&pdev->dev, "dsit_clk");
+ if (IS_ERR(mipi->dsit_clk)) {
+ ret = PTR_ERR(mipi->dsit_clk);
+ goto eclktget;
+ }
+
+ f_current = clk_get_rate(mipi->dsit_clk);
+ /* 80MHz required by the datasheet */
+ rate = clk_round_rate(mipi->dsit_clk, 80000000);
+ if (rate > 0 && rate != f_current)
+ ret = clk_set_rate(mipi->dsit_clk, rate);
+ else
+ ret = rate;
+ if (ret < 0)
+ goto esettrate;
+
+ dev_dbg(&pdev->dev, "DSI-T clk %lu -> %lu\n", f_current, rate);
+
+ sprintf(dsip_clk, "dsi%1.1dp_clk", idx);
+ mipi->dsip_clk = clk_get(&pdev->dev, dsip_clk);
+ if (IS_ERR(mipi->dsip_clk)) {
+ ret = PTR_ERR(mipi->dsip_clk);
+ goto eclkpget;
+ }
+
+ f_current = clk_get_rate(mipi->dsip_clk);
+ /* Between 10 and 50MHz */
+ rate = clk_round_rate(mipi->dsip_clk, 24000000);
+ if (rate > 0 && rate != f_current)
+ ret = clk_set_rate(mipi->dsip_clk, rate);
+ else
+ ret = rate;
+ if (ret < 0)
+ goto esetprate;
+
+ dev_dbg(&pdev->dev, "DSI-P clk %lu -> %lu\n", f_current, rate);
+
+ msleep(10);
+
+ ret = clk_enable(mipi->dsit_clk);
+ if (ret < 0)
+ goto eclkton;
+
+ ret = clk_enable(mipi->dsip_clk);
+ if (ret < 0)
+ goto eclkpon;
+
+ mipi_dsi[idx] = mipi;
+
+ ret = sh_mipi_setup(mipi, pdata);
+ if (ret < 0)
+ goto emipisetup;
+
+ mutex_unlock(&array_lock);
+ platform_set_drvdata(pdev, mipi);
+
+ /* Set up LCDC callbacks */
+ pdata->lcd_chan->board_cfg.board_data = mipi;
+ pdata->lcd_chan->board_cfg.display_on = mipi_display_on;
+ pdata->lcd_chan->board_cfg.display_off = mipi_display_off;
+
+ return 0;
+
+emipisetup:
+ mipi_dsi[idx] = NULL;
+ clk_disable(mipi->dsip_clk);
+eclkpon:
+ clk_disable(mipi->dsit_clk);
+eclkton:
+esetprate:
+ clk_put(mipi->dsip_clk);
+eclkpget:
+esettrate:
+ clk_put(mipi->dsit_clk);
+eclktget:
+ iounmap(mipi->base);
+emap:
+ release_mem_region(res->start, resource_size(res));
+ereqreg:
+ kfree(mipi);
+ealloc:
+efindslot:
+ mutex_unlock(&array_lock);
+
+ return ret;
+}
+
+static int __exit sh_mipi_remove(struct platform_device *pdev)
+{
+ struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct sh_mipi *mipi = platform_get_drvdata(pdev);
+ int i, ret;
+
+ mutex_lock(&array_lock);
+
+ for (i = 0; i < ARRAY_SIZE(mipi_dsi) && mipi_dsi[i] != mipi; i++)
+ ;
+
+ if (i == ARRAY_SIZE(mipi_dsi)) {
+ ret = -EINVAL;
+ } else {
+ ret = 0;
+ mipi_dsi[i] = NULL;
+ }
+
+ mutex_unlock(&array_lock);
+
+ if (ret < 0)
+ return ret;
+
+ pdata->lcd_chan->board_cfg.display_on = NULL;
+ pdata->lcd_chan->board_cfg.display_off = NULL;
+ pdata->lcd_chan->board_cfg.board_data = NULL;
+
+ clk_disable(mipi->dsip_clk);
+ clk_disable(mipi->dsit_clk);
+ clk_put(mipi->dsit_clk);
+ clk_put(mipi->dsip_clk);
+ iounmap(mipi->base);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+ platform_set_drvdata(pdev, NULL);
+ kfree(mipi);
+
+ return 0;
+}
+
+static struct platform_driver sh_mipi_driver = {
+ .remove = __exit_p(sh_mipi_remove),
+ .shutdown = sh_mipi_shutdown,
+ .driver = {
+ .name = "sh-mipi-dsi",
+ },
+};
+
+static int __init sh_mipi_init(void)
+{
+ return platform_driver_probe(&sh_mipi_driver, sh_mipi_probe);
+}
+module_init(sh_mipi_init);
+
+static void __exit sh_mipi_exit(void)
+{
+ platform_driver_unregister(&sh_mipi_driver);
+}
+module_exit(sh_mipi_exit);
+
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_DESCRIPTION("SuperH / ARM-shmobile MIPI DSI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c
new file mode 100644
index 000000000000..2fde08cc66bf
--- /dev/null
+++ b/drivers/video/sh_mobile_hdmi.c
@@ -0,0 +1,1028 @@
+/*
+ * SH-Mobile High-Definition Multimedia Interface (HDMI) driver
+ * for SLISHDMI13T and SLIPHDMIT IP cores
+ *
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * 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/clk.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include <video/sh_mobile_hdmi.h>
+#include <video/sh_mobile_lcdc.h>
+
+#define HDMI_SYSTEM_CTRL 0x00 /* System control */
+#define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control,
+ bits 19..16 of 20-bit N for Audio Clock Regeneration packet */
+#define HDMI_20_BIT_N_FOR_AUDIO_RPKT_15_8 0x02 /* bits 15..8 of 20-bit N for Audio Clock Regeneration packet */
+#define HDMI_20_BIT_N_FOR_AUDIO_RPKT_7_0 0x03 /* bits 7..0 of 20-bit N for Audio Clock Regeneration packet */
+#define HDMI_SPDIF_AUDIO_SAMP_FREQ_CTS 0x04 /* SPDIF audio sampling frequency,
+ bits 19..16 of Internal CTS */
+#define HDMI_INTERNAL_CTS_15_8 0x05 /* bits 15..8 of Internal CTS */
+#define HDMI_INTERNAL_CTS_7_0 0x06 /* bits 7..0 of Internal CTS */
+#define HDMI_EXTERNAL_CTS_19_16 0x07 /* External CTS */
+#define HDMI_EXTERNAL_CTS_15_8 0x08 /* External CTS */
+#define HDMI_EXTERNAL_CTS_7_0 0x09 /* External CTS */
+#define HDMI_AUDIO_SETTING_1 0x0A /* Audio setting.1 */
+#define HDMI_AUDIO_SETTING_2 0x0B /* Audio setting.2 */
+#define HDMI_I2S_AUDIO_SET 0x0C /* I2S audio setting */
+#define HDMI_DSD_AUDIO_SET 0x0D /* DSD audio setting */
+#define HDMI_DEBUG_MONITOR_1 0x0E /* Debug monitor.1 */
+#define HDMI_DEBUG_MONITOR_2 0x0F /* Debug monitor.2 */
+#define HDMI_I2S_INPUT_PIN_SWAP 0x10 /* I2S input pin swap */
+#define HDMI_AUDIO_STATUS_BITS_SETTING_1 0x11 /* Audio status bits setting.1 */
+#define HDMI_AUDIO_STATUS_BITS_SETTING_2 0x12 /* Audio status bits setting.2 */
+#define HDMI_CATEGORY_CODE 0x13 /* Category code */
+#define HDMI_SOURCE_NUM_AUDIO_WORD_LEN 0x14 /* Source number/Audio word length */
+#define HDMI_AUDIO_VIDEO_SETTING_1 0x15 /* Audio/Video setting.1 */
+#define HDMI_VIDEO_SETTING_1 0x16 /* Video setting.1 */
+#define HDMI_DEEP_COLOR_MODES 0x17 /* Deep Color Modes */
+
+/* 12 16- and 10-bit Color space conversion parameters: 0x18..0x2f */
+#define HDMI_COLOR_SPACE_CONVERSION_PARAMETERS 0x18
+
+#define HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS 0x30 /* External video parameter settings */
+#define HDMI_EXTERNAL_H_TOTAL_7_0 0x31 /* External horizontal total (LSB) */
+#define HDMI_EXTERNAL_H_TOTAL_11_8 0x32 /* External horizontal total (MSB) */
+#define HDMI_EXTERNAL_H_BLANK_7_0 0x33 /* External horizontal blank (LSB) */
+#define HDMI_EXTERNAL_H_BLANK_9_8 0x34 /* External horizontal blank (MSB) */
+#define HDMI_EXTERNAL_H_DELAY_7_0 0x35 /* External horizontal delay (LSB) */
+#define HDMI_EXTERNAL_H_DELAY_9_8 0x36 /* External horizontal delay (MSB) */
+#define HDMI_EXTERNAL_H_DURATION_7_0 0x37 /* External horizontal duration (LSB) */
+#define HDMI_EXTERNAL_H_DURATION_9_8 0x38 /* External horizontal duration (MSB) */
+#define HDMI_EXTERNAL_V_TOTAL_7_0 0x39 /* External vertical total (LSB) */
+#define HDMI_EXTERNAL_V_TOTAL_9_8 0x3A /* External vertical total (MSB) */
+#define HDMI_AUDIO_VIDEO_SETTING_2 0x3B /* Audio/Video setting.2 */
+#define HDMI_EXTERNAL_V_BLANK 0x3D /* External vertical blank */
+#define HDMI_EXTERNAL_V_DELAY 0x3E /* External vertical delay */
+#define HDMI_EXTERNAL_V_DURATION 0x3F /* External vertical duration */
+#define HDMI_CTRL_PKT_MANUAL_SEND_CONTROL 0x40 /* Control packet manual send control */
+#define HDMI_CTRL_PKT_AUTO_SEND 0x41 /* Control packet auto send with VSYNC control */
+#define HDMI_AUTO_CHECKSUM_OPTION 0x42 /* Auto checksum option */
+#define HDMI_VIDEO_SETTING_2 0x45 /* Video setting.2 */
+#define HDMI_OUTPUT_OPTION 0x46 /* Output option */
+#define HDMI_SLIPHDMIT_PARAM_OPTION 0x51 /* SLIPHDMIT parameter option */
+#define HDMI_HSYNC_PMENT_AT_EMB_7_0 0x52 /* HSYNC placement at embedded sync (LSB) */
+#define HDMI_HSYNC_PMENT_AT_EMB_15_8 0x53 /* HSYNC placement at embedded sync (MSB) */
+#define HDMI_VSYNC_PMENT_AT_EMB_7_0 0x54 /* VSYNC placement at embedded sync (LSB) */
+#define HDMI_VSYNC_PMENT_AT_EMB_14_8 0x55 /* VSYNC placement at embedded sync (MSB) */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_1 0x56 /* SLIPHDMIT parameter settings.1 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_2 0x57 /* SLIPHDMIT parameter settings.2 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_3 0x58 /* SLIPHDMIT parameter settings.3 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_5 0x59 /* SLIPHDMIT parameter settings.5 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_6 0x5A /* SLIPHDMIT parameter settings.6 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_7 0x5B /* SLIPHDMIT parameter settings.7 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_8 0x5C /* SLIPHDMIT parameter settings.8 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_9 0x5D /* SLIPHDMIT parameter settings.9 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_10 0x5E /* SLIPHDMIT parameter settings.10 */
+#define HDMI_CTRL_PKT_BUF_INDEX 0x5F /* Control packet buffer index */
+#define HDMI_CTRL_PKT_BUF_ACCESS_HB0 0x60 /* Control packet data buffer access window - HB0 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_HB1 0x61 /* Control packet data buffer access window - HB1 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_HB2 0x62 /* Control packet data buffer access window - HB2 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB0 0x63 /* Control packet data buffer access window - PB0 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB1 0x64 /* Control packet data buffer access window - PB1 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB2 0x65 /* Control packet data buffer access window - PB2 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB3 0x66 /* Control packet data buffer access window - PB3 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB4 0x67 /* Control packet data buffer access window - PB4 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB5 0x68 /* Control packet data buffer access window - PB5 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB6 0x69 /* Control packet data buffer access window - PB6 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB7 0x6A /* Control packet data buffer access window - PB7 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB8 0x6B /* Control packet data buffer access window - PB8 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB9 0x6C /* Control packet data buffer access window - PB9 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB10 0x6D /* Control packet data buffer access window - PB10 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB11 0x6E /* Control packet data buffer access window - PB11 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB12 0x6F /* Control packet data buffer access window - PB12 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB13 0x70 /* Control packet data buffer access window - PB13 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB14 0x71 /* Control packet data buffer access window - PB14 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB15 0x72 /* Control packet data buffer access window - PB15 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB16 0x73 /* Control packet data buffer access window - PB16 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB17 0x74 /* Control packet data buffer access window - PB17 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB18 0x75 /* Control packet data buffer access window - PB18 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB19 0x76 /* Control packet data buffer access window - PB19 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB20 0x77 /* Control packet data buffer access window - PB20 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB21 0x78 /* Control packet data buffer access window - PB21 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB22 0x79 /* Control packet data buffer access window - PB22 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB23 0x7A /* Control packet data buffer access window - PB23 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB24 0x7B /* Control packet data buffer access window - PB24 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB25 0x7C /* Control packet data buffer access window - PB25 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB26 0x7D /* Control packet data buffer access window - PB26 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB27 0x7E /* Control packet data buffer access window - PB27 */
+#define HDMI_EDID_KSV_FIFO_ACCESS_WINDOW 0x80 /* EDID/KSV FIFO access window */
+#define HDMI_DDC_BUS_ACCESS_FREQ_CTRL_7_0 0x81 /* DDC bus access frequency control (LSB) */
+#define HDMI_DDC_BUS_ACCESS_FREQ_CTRL_15_8 0x82 /* DDC bus access frequency control (MSB) */
+#define HDMI_INTERRUPT_MASK_1 0x92 /* Interrupt mask.1 */
+#define HDMI_INTERRUPT_MASK_2 0x93 /* Interrupt mask.2 */
+#define HDMI_INTERRUPT_STATUS_1 0x94 /* Interrupt status.1 */
+#define HDMI_INTERRUPT_STATUS_2 0x95 /* Interrupt status.2 */
+#define HDMI_INTERRUPT_MASK_3 0x96 /* Interrupt mask.3 */
+#define HDMI_INTERRUPT_MASK_4 0x97 /* Interrupt mask.4 */
+#define HDMI_INTERRUPT_STATUS_3 0x98 /* Interrupt status.3 */
+#define HDMI_INTERRUPT_STATUS_4 0x99 /* Interrupt status.4 */
+#define HDMI_SOFTWARE_HDCP_CONTROL_1 0x9A /* Software HDCP control.1 */
+#define HDMI_FRAME_COUNTER 0x9C /* Frame counter */
+#define HDMI_FRAME_COUNTER_FOR_RI_CHECK 0x9D /* Frame counter for Ri check */
+#define HDMI_HDCP_CONTROL 0xAF /* HDCP control */
+#define HDMI_RI_FRAME_COUNT_REGISTER 0xB2 /* Ri frame count register */
+#define HDMI_DDC_BUS_CONTROL 0xB7 /* DDC bus control */
+#define HDMI_HDCP_STATUS 0xB8 /* HDCP status */
+#define HDMI_SHA0 0xB9 /* sha0 */
+#define HDMI_SHA1 0xBA /* sha1 */
+#define HDMI_SHA2 0xBB /* sha2 */
+#define HDMI_SHA3 0xBC /* sha3 */
+#define HDMI_SHA4 0xBD /* sha4 */
+#define HDMI_BCAPS_READ 0xBE /* BCAPS read / debug */
+#define HDMI_AKSV_BKSV_7_0_MONITOR 0xBF /* AKSV/BKSV[7:0] monitor */
+#define HDMI_AKSV_BKSV_15_8_MONITOR 0xC0 /* AKSV/BKSV[15:8] monitor */
+#define HDMI_AKSV_BKSV_23_16_MONITOR 0xC1 /* AKSV/BKSV[23:16] monitor */
+#define HDMI_AKSV_BKSV_31_24_MONITOR 0xC2 /* AKSV/BKSV[31:24] monitor */
+#define HDMI_AKSV_BKSV_39_32_MONITOR 0xC3 /* AKSV/BKSV[39:32] monitor */
+#define HDMI_EDID_SEGMENT_POINTER 0xC4 /* EDID segment pointer */
+#define HDMI_EDID_WORD_ADDRESS 0xC5 /* EDID word address */
+#define HDMI_EDID_DATA_FIFO_ADDRESS 0xC6 /* EDID data FIFO address */
+#define HDMI_NUM_OF_HDMI_DEVICES 0xC7 /* Number of HDMI devices */
+#define HDMI_HDCP_ERROR_CODE 0xC8 /* HDCP error code */
+#define HDMI_100MS_TIMER_SET 0xC9 /* 100ms timer setting */
+#define HDMI_5SEC_TIMER_SET 0xCA /* 5sec timer setting */
+#define HDMI_RI_READ_COUNT 0xCB /* Ri read count */
+#define HDMI_AN_SEED 0xCC /* An seed */
+#define HDMI_MAX_NUM_OF_RCIVRS_ALLOWED 0xCD /* Maximum number of receivers allowed */
+#define HDMI_HDCP_MEMORY_ACCESS_CONTROL_1 0xCE /* HDCP memory access control.1 */
+#define HDMI_HDCP_MEMORY_ACCESS_CONTROL_2 0xCF /* HDCP memory access control.2 */
+#define HDMI_HDCP_CONTROL_2 0xD0 /* HDCP Control 2 */
+#define HDMI_HDCP_KEY_MEMORY_CONTROL 0xD2 /* HDCP Key Memory Control */
+#define HDMI_COLOR_SPACE_CONV_CONFIG_1 0xD3 /* Color space conversion configuration.1 */
+#define HDMI_VIDEO_SETTING_3 0xD4 /* Video setting.3 */
+#define HDMI_RI_7_0 0xD5 /* Ri[7:0] */
+#define HDMI_RI_15_8 0xD6 /* Ri[15:8] */
+#define HDMI_PJ 0xD7 /* Pj */
+#define HDMI_SHA_RD 0xD8 /* sha_rd */
+#define HDMI_RI_7_0_SAVED 0xD9 /* Ri[7:0] saved */
+#define HDMI_RI_15_8_SAVED 0xDA /* Ri[15:8] saved */
+#define HDMI_PJ_SAVED 0xDB /* Pj saved */
+#define HDMI_NUM_OF_DEVICES 0xDC /* Number of devices */
+#define HDMI_HOT_PLUG_MSENS_STATUS 0xDF /* Hot plug/MSENS status */
+#define HDMI_BCAPS_WRITE 0xE0 /* bcaps */
+#define HDMI_BSTAT_7_0 0xE1 /* bstat[7:0] */
+#define HDMI_BSTAT_15_8 0xE2 /* bstat[15:8] */
+#define HDMI_BKSV_7_0 0xE3 /* bksv[7:0] */
+#define HDMI_BKSV_15_8 0xE4 /* bksv[15:8] */
+#define HDMI_BKSV_23_16 0xE5 /* bksv[23:16] */
+#define HDMI_BKSV_31_24 0xE6 /* bksv[31:24] */
+#define HDMI_BKSV_39_32 0xE7 /* bksv[39:32] */
+#define HDMI_AN_7_0 0xE8 /* An[7:0] */
+#define HDMI_AN_15_8 0xE9 /* An [15:8] */
+#define HDMI_AN_23_16 0xEA /* An [23:16] */
+#define HDMI_AN_31_24 0xEB /* An [31:24] */
+#define HDMI_AN_39_32 0xEC /* An [39:32] */
+#define HDMI_AN_47_40 0xED /* An [47:40] */
+#define HDMI_AN_55_48 0xEE /* An [55:48] */
+#define HDMI_AN_63_56 0xEF /* An [63:56] */
+#define HDMI_PRODUCT_ID 0xF0 /* Product ID */
+#define HDMI_REVISION_ID 0xF1 /* Revision ID */
+#define HDMI_TEST_MODE 0xFE /* Test mode */
+
+enum hotplug_state {
+ HDMI_HOTPLUG_DISCONNECTED,
+ HDMI_HOTPLUG_CONNECTED,
+ HDMI_HOTPLUG_EDID_DONE,
+};
+
+struct sh_hdmi {
+ void __iomem *base;
+ enum hotplug_state hp_state;
+ struct clk *hdmi_clk;
+ struct device *dev;
+ struct fb_info *info;
+ struct delayed_work edid_work;
+ struct fb_var_screeninfo var;
+};
+
+static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg)
+{
+ iowrite8(data, hdmi->base + reg);
+}
+
+static u8 hdmi_read(struct sh_hdmi *hdmi, u8 reg)
+{
+ return ioread8(hdmi->base + reg);
+}
+
+/* External video parameter settings */
+static void hdmi_external_video_param(struct sh_hdmi *hdmi)
+{
+ struct fb_var_screeninfo *var = &hdmi->var;
+ u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset;
+ u8 sync = 0;
+
+ htotal = var->xres + var->right_margin + var->left_margin + var->hsync_len;
+
+ hdelay = var->hsync_len + var->left_margin;
+ hblank = var->right_margin + hdelay;
+
+ /*
+ * Vertical timing looks a bit different in Figure 18,
+ * but let's try the same first by setting offset = 0
+ */
+ vtotal = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
+
+ vdelay = var->vsync_len + var->upper_margin;
+ vblank = var->lower_margin + vdelay;
+ voffset = min(var->upper_margin / 2, 6U);
+
+ /*
+ * [3]: VSYNC polarity: Positive
+ * [2]: HSYNC polarity: Positive
+ * [1]: Interlace/Progressive: Progressive
+ * [0]: External video settings enable: used.
+ */
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+ sync |= 4;
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+ sync |= 8;
+
+ pr_debug("H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n",
+ htotal, hblank, hdelay, var->hsync_len,
+ vtotal, vblank, vdelay, var->vsync_len, sync);
+
+ hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS);
+
+ hdmi_write(hdmi, htotal, HDMI_EXTERNAL_H_TOTAL_7_0);
+ hdmi_write(hdmi, htotal >> 8, HDMI_EXTERNAL_H_TOTAL_11_8);
+
+ hdmi_write(hdmi, hblank, HDMI_EXTERNAL_H_BLANK_7_0);
+ hdmi_write(hdmi, hblank >> 8, HDMI_EXTERNAL_H_BLANK_9_8);
+
+ hdmi_write(hdmi, hdelay, HDMI_EXTERNAL_H_DELAY_7_0);
+ hdmi_write(hdmi, hdelay >> 8, HDMI_EXTERNAL_H_DELAY_9_8);
+
+ hdmi_write(hdmi, var->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0);
+ hdmi_write(hdmi, var->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8);
+
+ hdmi_write(hdmi, vtotal, HDMI_EXTERNAL_V_TOTAL_7_0);
+ hdmi_write(hdmi, vtotal >> 8, HDMI_EXTERNAL_V_TOTAL_9_8);
+
+ hdmi_write(hdmi, vblank, HDMI_EXTERNAL_V_BLANK);
+
+ hdmi_write(hdmi, vdelay, HDMI_EXTERNAL_V_DELAY);
+
+ hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION);
+
+ /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for manual mode */
+}
+
+/**
+ * sh_hdmi_video_config()
+ */
+static void sh_hdmi_video_config(struct sh_hdmi *hdmi)
+{
+ /*
+ * [7:4]: Audio sampling frequency: 48kHz
+ * [3:1]: Input video format: RGB and YCbCr 4:4:4 (Y on Green)
+ * [0]: Internal/External DE select: internal
+ */
+ hdmi_write(hdmi, 0x20, HDMI_AUDIO_VIDEO_SETTING_1);
+
+ /*
+ * [7:6]: Video output format: RGB 4:4:4
+ * [5:4]: Input video data width: 8 bit
+ * [3:1]: EAV/SAV location: channel 1
+ * [0]: Video input color space: RGB
+ */
+ hdmi_write(hdmi, 0x34, HDMI_VIDEO_SETTING_1);
+
+ /*
+ * [7:6]: Together with bit [6] of HDMI_AUDIO_VIDEO_SETTING_2, which is
+ * left at 0 by default, this configures 24bpp and sets the Color Depth
+ * (CD) field in the General Control Packet
+ */
+ hdmi_write(hdmi, 0x20, HDMI_DEEP_COLOR_MODES);
+}
+
+/**
+ * sh_hdmi_audio_config()
+ */
+static void sh_hdmi_audio_config(struct sh_hdmi *hdmi)
+{
+ /*
+ * [7:4] L/R data swap control
+ * [3:0] appropriate N[19:16]
+ */
+ hdmi_write(hdmi, 0x00, HDMI_L_R_DATA_SWAP_CTRL_RPKT);
+ /* appropriate N[15:8] */
+ hdmi_write(hdmi, 0x18, HDMI_20_BIT_N_FOR_AUDIO_RPKT_15_8);
+ /* appropriate N[7:0] */
+ hdmi_write(hdmi, 0x00, HDMI_20_BIT_N_FOR_AUDIO_RPKT_7_0);
+
+ /* [7:4] 48 kHz SPDIF not used */
+ hdmi_write(hdmi, 0x20, HDMI_SPDIF_AUDIO_SAMP_FREQ_CTS);
+
+ /*
+ * [6:5] set required down sampling rate if required
+ * [4:3] set required audio source
+ */
+ hdmi_write(hdmi, 0x00, HDMI_AUDIO_SETTING_1);
+
+ /* [3:0] set sending channel number for channel status */
+ hdmi_write(hdmi, 0x40, HDMI_AUDIO_SETTING_2);
+
+ /*
+ * [5:2] set valid I2S source input pin
+ * [1:0] set input I2S source mode
+ */
+ hdmi_write(hdmi, 0x04, HDMI_I2S_AUDIO_SET);
+
+ /* [7:4] set valid DSD source input pin */
+ hdmi_write(hdmi, 0x00, HDMI_DSD_AUDIO_SET);
+
+ /* [7:0] set appropriate I2S input pin swap settings if required */
+ hdmi_write(hdmi, 0x00, HDMI_I2S_INPUT_PIN_SWAP);
+
+ /*
+ * [7] set validity bit for channel status
+ * [3:0] set original sample frequency for channel status
+ */
+ hdmi_write(hdmi, 0x00, HDMI_AUDIO_STATUS_BITS_SETTING_1);
+
+ /*
+ * [7] set value for channel status
+ * [6] set value for channel status
+ * [5] set copyright bit for channel status
+ * [4:2] set additional information for channel status
+ * [1:0] set clock accuracy for channel status
+ */
+ hdmi_write(hdmi, 0x00, HDMI_AUDIO_STATUS_BITS_SETTING_2);
+
+ /* [7:0] set category code for channel status */
+ hdmi_write(hdmi, 0x00, HDMI_CATEGORY_CODE);
+
+ /*
+ * [7:4] set source number for channel status
+ * [3:0] set word length for channel status
+ */
+ hdmi_write(hdmi, 0x00, HDMI_SOURCE_NUM_AUDIO_WORD_LEN);
+
+ /* [7:4] set sample frequency for channel status */
+ hdmi_write(hdmi, 0x20, HDMI_AUDIO_VIDEO_SETTING_1);
+}
+
+/**
+ * sh_hdmi_phy_config()
+ */
+static void sh_hdmi_phy_config(struct sh_hdmi *hdmi)
+{
+ /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */
+ hdmi_write(hdmi, 0x19, HDMI_SLIPHDMIT_PARAM_SETTINGS_1);
+ hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2);
+ hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3);
+ /* PLLA_CONFIG[7:0]: VCO gain, VCO offset, LPF resistance[0] */
+ hdmi_write(hdmi, 0x44, HDMI_SLIPHDMIT_PARAM_SETTINGS_5);
+ hdmi_write(hdmi, 0x32, HDMI_SLIPHDMIT_PARAM_SETTINGS_6);
+ hdmi_write(hdmi, 0x4A, HDMI_SLIPHDMIT_PARAM_SETTINGS_7);
+ hdmi_write(hdmi, 0x0E, HDMI_SLIPHDMIT_PARAM_SETTINGS_8);
+ hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9);
+ hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10);
+}
+
+/**
+ * sh_hdmi_avi_infoframe_setup() - Auxiliary Video Information InfoFrame CONTROL PACKET
+ */
+static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi)
+{
+ /* AVI InfoFrame */
+ hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* Packet Type = 0x82 */
+ hdmi_write(hdmi, 0x82, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+
+ /* Version = 0x02 */
+ hdmi_write(hdmi, 0x02, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+
+ /* Length = 13 (0x0D) */
+ hdmi_write(hdmi, 0x0D, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* N. A. Checksum */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0);
+
+ /*
+ * Y = RGB
+ * A0 = No Data
+ * B = Bar Data not valid
+ * S = No Data
+ */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1);
+
+ /*
+ * C = No Data
+ * M = 16:9 Picture Aspect Ratio
+ * R = Same as picture aspect ratio
+ */
+ hdmi_write(hdmi, 0x28, HDMI_CTRL_PKT_BUF_ACCESS_PB2);
+
+ /*
+ * ITC = No Data
+ * EC = xvYCC601
+ * Q = Default (depends on video format)
+ * SC = No Known non_uniform Scaling
+ */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3);
+
+ /*
+ * VIC = 1280 x 720p: ignored if external config is used
+ * Send 2 for 720 x 480p, 16 for 1080p
+ */
+ hdmi_write(hdmi, 4, HDMI_CTRL_PKT_BUF_ACCESS_PB4);
+
+ /* PR = No Repetition */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5);
+
+ /* Line Number of End of Top Bar (lower 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB6);
+
+ /* Line Number of End of Top Bar (upper 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB7);
+
+ /* Line Number of Start of Bottom Bar (lower 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB8);
+
+ /* Line Number of Start of Bottom Bar (upper 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB9);
+
+ /* Pixel Number of End of Left Bar (lower 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB10);
+
+ /* Pixel Number of End of Left Bar (upper 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB11);
+
+ /* Pixel Number of Start of Right Bar (lower 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB12);
+
+ /* Pixel Number of Start of Right Bar (upper 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB13);
+}
+
+/**
+ * sh_hdmi_audio_infoframe_setup() - Audio InfoFrame of CONTROL PACKET
+ */
+static void sh_hdmi_audio_infoframe_setup(struct sh_hdmi *hdmi)
+{
+ /* Audio InfoFrame */
+ hdmi_write(hdmi, 0x08, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* Packet Type = 0x84 */
+ hdmi_write(hdmi, 0x84, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+
+ /* Version Number = 0x01 */
+ hdmi_write(hdmi, 0x01, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+
+ /* 0 Length = 10 (0x0A) */
+ hdmi_write(hdmi, 0x0A, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* n. a. Checksum */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0);
+
+ /* Audio Channel Count = Refer to Stream Header */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1);
+
+ /* Refer to Stream Header */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB2);
+
+ /* Format depends on coding type (i.e. CT0...CT3) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3);
+
+ /* Speaker Channel Allocation = Front Right + Front Left */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB4);
+
+ /* Level Shift Value = 0 dB, Down - mix is permitted or no information */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5);
+
+ /* Reserved (0) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB6);
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB7);
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB8);
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB9);
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB10);
+}
+
+/**
+ * sh_hdmi_gamut_metadata_setup() - Gamut Metadata Packet of CONTROL PACKET
+ */
+static void sh_hdmi_gamut_metadata_setup(struct sh_hdmi *hdmi)
+{
+ int i;
+
+ /* Gamut Metadata Packet */
+ hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* Packet Type = 0x0A */
+ hdmi_write(hdmi, 0x0A, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+ /* Gamut Packet is not used, so default value */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+ /* Gamut Packet is not used, so default value */
+ hdmi_write(hdmi, 0x10, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* GBD bytes 0 through 27 */
+ for (i = 0; i <= 27; i++)
+ /* HDMI_CTRL_PKT_BUF_ACCESS_PB0_63H - PB27_7EH */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i);
+}
+
+/**
+ * sh_hdmi_acp_setup() - Audio Content Protection Packet (ACP)
+ */
+static void sh_hdmi_acp_setup(struct sh_hdmi *hdmi)
+{
+ int i;
+
+ /* Audio Content Protection Packet (ACP) */
+ hdmi_write(hdmi, 0x01, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* Packet Type = 0x04 */
+ hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+ /* ACP_Type */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+ /* Reserved (0) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* GBD bytes 0 through 27 */
+ for (i = 0; i <= 27; i++)
+ /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i);
+}
+
+/**
+ * sh_hdmi_isrc1_setup() - ISRC1 Packet
+ */
+static void sh_hdmi_isrc1_setup(struct sh_hdmi *hdmi)
+{
+ int i;
+
+ /* ISRC1 Packet */
+ hdmi_write(hdmi, 0x02, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* Packet Type = 0x05 */
+ hdmi_write(hdmi, 0x05, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+ /* ISRC_Cont, ISRC_Valid, Reserved (0), ISRC_Status */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+ /* Reserved (0) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* PB0 UPC_EAN_ISRC_0-15 */
+ /* Bytes PB16-PB27 shall be set to a value of 0. */
+ for (i = 0; i <= 27; i++)
+ /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i);
+}
+
+/**
+ * sh_hdmi_isrc2_setup() - ISRC2 Packet
+ */
+static void sh_hdmi_isrc2_setup(struct sh_hdmi *hdmi)
+{
+ int i;
+
+ /* ISRC2 Packet */
+ hdmi_write(hdmi, 0x03, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* HB0 Packet Type = 0x06 */
+ hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+ /* Reserved (0) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+ /* Reserved (0) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* PB0 UPC_EAN_ISRC_16-31 */
+ /* Bytes PB16-PB27 shall be set to a value of 0. */
+ for (i = 0; i <= 27; i++)
+ /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i);
+}
+
+/**
+ * sh_hdmi_configure() - Initialise HDMI for output
+ */
+static void sh_hdmi_configure(struct sh_hdmi *hdmi)
+{
+ /* Configure video format */
+ sh_hdmi_video_config(hdmi);
+
+ /* Configure audio format */
+ sh_hdmi_audio_config(hdmi);
+
+ /* Configure PHY */
+ sh_hdmi_phy_config(hdmi);
+
+ /* Auxiliary Video Information (AVI) InfoFrame */
+ sh_hdmi_avi_infoframe_setup(hdmi);
+
+ /* Audio InfoFrame */
+ sh_hdmi_audio_infoframe_setup(hdmi);
+
+ /* Gamut Metadata packet */
+ sh_hdmi_gamut_metadata_setup(hdmi);
+
+ /* Audio Content Protection (ACP) Packet */
+ sh_hdmi_acp_setup(hdmi);
+
+ /* ISRC1 Packet */
+ sh_hdmi_isrc1_setup(hdmi);
+
+ /* ISRC2 Packet */
+ sh_hdmi_isrc2_setup(hdmi);
+
+ /*
+ * Control packet auto send with VSYNC control: auto send
+ * General control, Gamut metadata, ISRC, and ACP packets
+ */
+ hdmi_write(hdmi, 0x8E, HDMI_CTRL_PKT_AUTO_SEND);
+
+ /* FIXME */
+ msleep(10);
+
+ /* PS mode b->d, reset PLLA and PLLB */
+ hdmi_write(hdmi, 0x4C, HDMI_SYSTEM_CTRL);
+
+ udelay(10);
+
+ hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL);
+}
+
+static void sh_hdmi_read_edid(struct sh_hdmi *hdmi)
+{
+ struct fb_var_screeninfo *var = &hdmi->var;
+ struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+ struct fb_videomode *lcd_cfg = &pdata->lcd_chan->lcd_cfg;
+ unsigned long height = var->height, width = var->width;
+ int i;
+ u8 edid[128];
+
+ /* Read EDID */
+ pr_debug("Read back EDID code:");
+ for (i = 0; i < 128; i++) {
+ edid[i] = hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW);
+#ifdef DEBUG
+ if ((i % 16) == 0) {
+ printk(KERN_CONT "\n");
+ printk(KERN_DEBUG "%02X | %02X", i, edid[i]);
+ } else {
+ printk(KERN_CONT " %02X", edid[i]);
+ }
+#endif
+ }
+#ifdef DEBUG
+ printk(KERN_CONT "\n");
+#endif
+ fb_parse_edid(edid, var);
+ pr_debug("%u-%u-%u-%u x %u-%u-%u-%u @ %lu kHz monitor detected\n",
+ var->left_margin, var->xres, var->right_margin, var->hsync_len,
+ var->upper_margin, var->yres, var->lower_margin, var->vsync_len,
+ PICOS2KHZ(var->pixclock));
+
+ /* FIXME: Use user-provided configuration instead of EDID */
+ var->width = width;
+ var->xres = lcd_cfg->xres;
+ var->xres_virtual = lcd_cfg->xres;
+ var->left_margin = lcd_cfg->left_margin;
+ var->right_margin = lcd_cfg->right_margin;
+ var->hsync_len = lcd_cfg->hsync_len;
+ var->height = height;
+ var->yres = lcd_cfg->yres;
+ var->yres_virtual = lcd_cfg->yres * 2;
+ var->upper_margin = lcd_cfg->upper_margin;
+ var->lower_margin = lcd_cfg->lower_margin;
+ var->vsync_len = lcd_cfg->vsync_len;
+ var->sync = lcd_cfg->sync;
+ var->pixclock = lcd_cfg->pixclock;
+
+ hdmi_external_video_param(hdmi);
+}
+
+static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id)
+{
+ struct sh_hdmi *hdmi = dev_id;
+ u8 status1, status2, mask1, mask2;
+
+ /* mode_b and PLLA and PLLB reset */
+ hdmi_write(hdmi, 0x2C, HDMI_SYSTEM_CTRL);
+
+ /* How long shall reset be held? */
+ udelay(10);
+
+ /* mode_b and PLLA and PLLB reset release */
+ hdmi_write(hdmi, 0x20, HDMI_SYSTEM_CTRL);
+
+ status1 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_1);
+ status2 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_2);
+
+ mask1 = hdmi_read(hdmi, HDMI_INTERRUPT_MASK_1);
+ mask2 = hdmi_read(hdmi, HDMI_INTERRUPT_MASK_2);
+
+ /* Correct would be to ack only set bits, but the datasheet requires 0xff */
+ hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_1);
+ hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_2);
+
+ if (printk_ratelimit())
+ pr_debug("IRQ #%d: Status #1: 0x%x & 0x%x, #2: 0x%x & 0x%x\n",
+ irq, status1, mask1, status2, mask2);
+
+ if (!((status1 & mask1) | (status2 & mask2))) {
+ return IRQ_NONE;
+ } else if (status1 & 0xc0) {
+ u8 msens;
+
+ /* Datasheet specifies 10ms... */
+ udelay(500);
+
+ msens = hdmi_read(hdmi, HDMI_HOT_PLUG_MSENS_STATUS);
+ pr_debug("MSENS 0x%x\n", msens);
+ /* Check, if hot plug & MSENS pin status are both high */
+ if ((msens & 0xC0) == 0xC0) {
+ /* Display plug in */
+ hdmi->hp_state = HDMI_HOTPLUG_CONNECTED;
+
+ /* Set EDID word address */
+ hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS);
+ /* Set EDID segment pointer */
+ hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER);
+ /* Enable EDID interrupt */
+ hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1);
+ } else if (!(status1 & 0x80)) {
+ /* Display unplug, beware multiple interrupts */
+ if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED)
+ schedule_delayed_work(&hdmi->edid_work, 0);
+
+ hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED;
+ /* display_off will switch back to mode_a */
+ }
+ } else if (status1 & 2) {
+ /* EDID error interrupt: retry */
+ /* Set EDID word address */
+ hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS);
+ /* Set EDID segment pointer */
+ hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER);
+ } else if (status1 & 4) {
+ /* Disable EDID interrupt */
+ hdmi_write(hdmi, 0xC0, HDMI_INTERRUPT_MASK_1);
+ hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE;
+ schedule_delayed_work(&hdmi->edid_work, msecs_to_jiffies(10));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void hdmi_display_on(void *arg, struct fb_info *info)
+{
+ struct sh_hdmi *hdmi = arg;
+ struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+
+ if (info->var.xres != 1280 || info->var.yres != 720) {
+ dev_warn(info->device, "Unsupported framebuffer geometry %ux%u\n",
+ info->var.xres, info->var.yres);
+ return;
+ }
+
+ pr_debug("%s(%p): state %x\n", __func__, pdata->lcd_dev, info->state);
+ /*
+ * FIXME: not a good place to store fb_info. And we cannot nullify it
+ * even on monitor disconnect. What should the lifecycle be?
+ */
+ hdmi->info = info;
+ switch (hdmi->hp_state) {
+ case HDMI_HOTPLUG_EDID_DONE:
+ /* PS mode d->e. All functions are active */
+ hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL);
+ pr_debug("HDMI running\n");
+ break;
+ case HDMI_HOTPLUG_DISCONNECTED:
+ info->state = FBINFO_STATE_SUSPENDED;
+ default:
+ hdmi->var = info->var;
+ }
+}
+
+static void hdmi_display_off(void *arg)
+{
+ struct sh_hdmi *hdmi = arg;
+ struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+
+ pr_debug("%s(%p)\n", __func__, pdata->lcd_dev);
+ /* PS mode e->a */
+ hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL);
+}
+
+/* Hotplug interrupt occurred, read EDID */
+static void edid_work_fn(struct work_struct *work)
+{
+ struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work);
+ struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+
+ pr_debug("%s(%p): begin, hotplug status %d\n", __func__,
+ pdata->lcd_dev, hdmi->hp_state);
+
+ if (!pdata->lcd_dev)
+ return;
+
+ if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) {
+ pm_runtime_get_sync(hdmi->dev);
+ /* A device has been plugged in */
+ sh_hdmi_read_edid(hdmi);
+ msleep(10);
+ sh_hdmi_configure(hdmi);
+ /* Switched to another (d) power-save mode */
+ msleep(10);
+
+ if (!hdmi->info)
+ return;
+
+ acquire_console_sem();
+
+ /* HDMI plug in */
+ hdmi->info->var = hdmi->var;
+ if (hdmi->info->state != FBINFO_STATE_RUNNING)
+ fb_set_suspend(hdmi->info, 0);
+ else
+ hdmi_display_on(hdmi, hdmi->info);
+
+ release_console_sem();
+ } else {
+ if (!hdmi->info)
+ return;
+
+ acquire_console_sem();
+
+ /* HDMI disconnect */
+ fb_set_suspend(hdmi->info, 1);
+
+ release_console_sem();
+ pm_runtime_put(hdmi->dev);
+ }
+
+ pr_debug("%s(%p): end\n", __func__, pdata->lcd_dev);
+}
+
+static int __init sh_hdmi_probe(struct platform_device *pdev)
+{
+ struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int irq = platform_get_irq(pdev, 0), ret;
+ struct sh_hdmi *hdmi;
+ long rate;
+
+ if (!res || !pdata || irq < 0)
+ return -ENODEV;
+
+ hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL);
+ if (!hdmi) {
+ dev_err(&pdev->dev, "Cannot allocate device data\n");
+ return -ENOMEM;
+ }
+
+ hdmi->dev = &pdev->dev;
+
+ hdmi->hdmi_clk = clk_get(&pdev->dev, "ick");
+ if (IS_ERR(hdmi->hdmi_clk)) {
+ ret = PTR_ERR(hdmi->hdmi_clk);
+ dev_err(&pdev->dev, "Unable to get clock: %d\n", ret);
+ goto egetclk;
+ }
+
+ rate = PICOS2KHZ(pdata->lcd_chan->lcd_cfg.pixclock) * 1000;
+
+ rate = clk_round_rate(hdmi->hdmi_clk, rate);
+ if (rate < 0) {
+ ret = rate;
+ dev_err(&pdev->dev, "Cannot get suitable rate: %ld\n", rate);
+ goto erate;
+ }
+
+ ret = clk_set_rate(hdmi->hdmi_clk, rate);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot set rate %ld: %d\n", rate, ret);
+ goto erate;
+ }
+
+ pr_debug("HDMI set frequency %lu\n", rate);
+
+ ret = clk_enable(hdmi->hdmi_clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot enable clock: %d\n", ret);
+ goto eclkenable;
+ }
+
+ dev_info(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate);
+
+ if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) {
+ dev_err(&pdev->dev, "HDMI register region already claimed\n");
+ ret = -EBUSY;
+ goto ereqreg;
+ }
+
+ hdmi->base = ioremap(res->start, resource_size(res));
+ if (!hdmi->base) {
+ dev_err(&pdev->dev, "HDMI register region already claimed\n");
+ ret = -ENOMEM;
+ goto emap;
+ }
+
+ platform_set_drvdata(pdev, hdmi);
+
+#if 1
+ /* Product and revision IDs are 0 in sh-mobile version */
+ dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n",
+ hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID));
+#endif
+
+ /* Set up LCDC callbacks */
+ pdata->lcd_chan->board_cfg.board_data = hdmi;
+ pdata->lcd_chan->board_cfg.display_on = hdmi_display_on;
+ pdata->lcd_chan->board_cfg.display_off = hdmi_display_off;
+
+ INIT_DELAYED_WORK(&hdmi->edid_work, edid_work_fn);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_resume(&pdev->dev);
+
+ ret = request_irq(irq, sh_hdmi_hotplug, 0,
+ dev_name(&pdev->dev), hdmi);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Unable to request irq: %d\n", ret);
+ goto ereqirq;
+ }
+
+ return 0;
+
+ereqirq:
+ pm_runtime_disable(&pdev->dev);
+ iounmap(hdmi->base);
+emap:
+ release_mem_region(res->start, resource_size(res));
+ereqreg:
+ clk_disable(hdmi->hdmi_clk);
+eclkenable:
+erate:
+ clk_put(hdmi->hdmi_clk);
+egetclk:
+ kfree(hdmi);
+
+ return ret;
+}
+
+static int __exit sh_hdmi_remove(struct platform_device *pdev)
+{
+ struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data;
+ struct sh_hdmi *hdmi = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int irq = platform_get_irq(pdev, 0);
+
+ pdata->lcd_chan->board_cfg.display_on = NULL;
+ pdata->lcd_chan->board_cfg.display_off = NULL;
+ pdata->lcd_chan->board_cfg.board_data = NULL;
+
+ free_irq(irq, hdmi);
+ pm_runtime_disable(&pdev->dev);
+ cancel_delayed_work_sync(&hdmi->edid_work);
+ clk_disable(hdmi->hdmi_clk);
+ clk_put(hdmi->hdmi_clk);
+ iounmap(hdmi->base);
+ release_mem_region(res->start, resource_size(res));
+ kfree(hdmi);
+
+ return 0;
+}
+
+static struct platform_driver sh_hdmi_driver = {
+ .remove = __exit_p(sh_hdmi_remove),
+ .driver = {
+ .name = "sh-mobile-hdmi",
+ },
+};
+
+static int __init sh_hdmi_init(void)
+{
+ return platform_driver_probe(&sh_hdmi_driver, sh_hdmi_probe);
+}
+module_init(sh_hdmi_init);
+
+static void __exit sh_hdmi_exit(void)
+{
+ platform_driver_unregister(&sh_hdmi_driver);
+}
+module_exit(sh_hdmi_exit);
+
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_DESCRIPTION("SuperH / ARM-shmobile HDMI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 12c451a711e9..d72075a9f01c 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -56,6 +56,7 @@ static int lcdc_shared_regs[] = {
/* per-channel registers */
enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR,
+ LDHAJR,
NR_CH_REGS };
static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
@@ -74,6 +75,7 @@ static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
[LDVLNR] = 0x450,
[LDVSYNR] = 0x454,
[LDPMR] = 0x460,
+ [LDHAJR] = 0x4a0,
};
static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
@@ -137,6 +139,7 @@ struct sh_mobile_lcdc_priv {
struct clk *dot_clk;
unsigned long lddckr;
struct sh_mobile_lcdc_chan ch[2];
+ struct notifier_block notifier;
unsigned long saved_shared_regs[NR_SHARED_REGS];
int started;
};
@@ -404,6 +407,56 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */
}
+static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
+{
+ struct fb_var_screeninfo *var = &ch->info->var;
+ unsigned long h_total, hsync_pos;
+ u32 tmp;
+
+ tmp = ch->ldmt1r_value;
+ tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28;
+ tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0;
+ lcdc_write_chan(ch, LDMT1R, tmp);
+
+ /* setup SYS bus */
+ lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r);
+ lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);
+
+ /* horizontal configuration */
+ h_total = var->xres + var->hsync_len +
+ var->left_margin + var->right_margin;
+ tmp = h_total / 8; /* HTCN */
+ tmp |= (var->xres / 8) << 16; /* HDCN */
+ lcdc_write_chan(ch, LDHCNR, tmp);
+
+ hsync_pos = var->xres + var->right_margin;
+ tmp = hsync_pos / 8; /* HSYNP */
+ tmp |= (var->hsync_len / 8) << 16; /* HSYNW */
+ lcdc_write_chan(ch, LDHSYNR, tmp);
+
+ /* vertical configuration */
+ tmp = var->yres + var->vsync_len +
+ var->upper_margin + var->lower_margin; /* VTLN */
+ tmp |= var->yres << 16; /* VDLN */
+ lcdc_write_chan(ch, LDVLNR, tmp);
+
+ tmp = var->yres + var->lower_margin; /* VSYNP */
+ tmp |= var->vsync_len << 16; /* VSYNW */
+ lcdc_write_chan(ch, LDVSYNR, tmp);
+
+ /* Adjust horizontal synchronisation for HDMI */
+ tmp = ((var->xres & 7) << 24) |
+ ((h_total & 7) << 16) |
+ ((var->hsync_len & 7) << 8) |
+ hsync_pos;
+ lcdc_write_chan(ch, LDHAJR, tmp);
+}
+
static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
{
struct sh_mobile_lcdc_chan *ch;
@@ -470,49 +523,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
if (!ch->enabled)
continue;
- tmp = ch->ldmt1r_value;
- tmp |= (lcd_cfg->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28;
- tmp |= (lcd_cfg->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0;
- lcdc_write_chan(ch, LDMT1R, tmp);
-
- /* setup SYS bus */
- lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r);
- lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);
-
- /* horizontal configuration */
- tmp = lcd_cfg->xres + lcd_cfg->hsync_len;
- tmp += lcd_cfg->left_margin;
- tmp += lcd_cfg->right_margin;
- tmp /= 8; /* HTCN */
- tmp |= (lcd_cfg->xres / 8) << 16; /* HDCN */
- lcdc_write_chan(ch, LDHCNR, tmp);
-
- tmp = lcd_cfg->xres;
- tmp += lcd_cfg->right_margin;
- tmp /= 8; /* HSYNP */
- tmp |= (lcd_cfg->hsync_len / 8) << 16; /* HSYNW */
- lcdc_write_chan(ch, LDHSYNR, tmp);
+ sh_mobile_lcdc_geometry(ch);
/* power supply */
lcdc_write_chan(ch, LDPMR, 0);
- /* vertical configuration */
- tmp = lcd_cfg->yres + lcd_cfg->vsync_len;
- tmp += lcd_cfg->upper_margin;
- tmp += lcd_cfg->lower_margin; /* VTLN */
- tmp |= lcd_cfg->yres << 16; /* VDLN */
- lcdc_write_chan(ch, LDVLNR, tmp);
-
- tmp = lcd_cfg->yres;
- tmp += lcd_cfg->lower_margin; /* VSYNP */
- tmp |= lcd_cfg->vsync_len << 16; /* VSYNW */
- lcdc_write_chan(ch, LDVSYNR, tmp);
-
board_cfg = &ch->cfg.board_cfg;
if (board_cfg->setup_sys)
ret = board_cfg->setup_sys(board_cfg->board_data, ch,
@@ -577,7 +592,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
board_cfg = &ch->cfg.board_cfg;
if (board_cfg->display_on)
- board_cfg->display_on(board_cfg->board_data);
+ board_cfg->display_on(board_cfg->board_data, ch->info);
}
return 0;
@@ -943,6 +958,62 @@ static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
.runtime_resume = sh_mobile_lcdc_runtime_resume,
};
+static int sh_mobile_lcdc_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct fb_event *event = data;
+ struct fb_info *info = event->info;
+ struct sh_mobile_lcdc_chan *ch = info->par;
+ struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg;
+ struct fb_var_screeninfo *var;
+
+ if (&ch->lcdc->notifier != nb)
+ return 0;
+
+ dev_dbg(info->dev, "%s(): action = %lu, data = %p\n",
+ __func__, action, event->data);
+
+ switch(action) {
+ case FB_EVENT_SUSPEND:
+ if (board_cfg->display_off)
+ board_cfg->display_off(board_cfg->board_data);
+ pm_runtime_put(info->device);
+ break;
+ case FB_EVENT_RESUME:
+ var = &info->var;
+
+ /* HDMI must be enabled before LCDC configuration */
+ if (board_cfg->display_on)
+ board_cfg->display_on(board_cfg->board_data, ch->info);
+
+ /* Check if the new display is not in our modelist */
+ if (ch->info->modelist.next &&
+ !fb_match_mode(var, &ch->info->modelist)) {
+ struct fb_videomode mode;
+ int ret;
+
+ /* Can we handle this display? */
+ if (var->xres > ch->cfg.lcd_cfg.xres ||
+ var->yres > ch->cfg.lcd_cfg.yres)
+ return -ENOMEM;
+
+ /* Add to the modelist */
+ fb_var_to_videomode(&mode, var);
+ ret = fb_add_videomode(&mode, &ch->info->modelist);
+ if (ret < 0)
+ return ret;
+ }
+
+ pm_runtime_get_sync(info->device);
+
+ sh_mobile_lcdc_geometry(ch);
+
+ break;
+ }
+
+ return 0;
+}
+
static int sh_mobile_lcdc_remove(struct platform_device *pdev);
static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
@@ -1020,15 +1091,19 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err1;
}
+ priv->base = ioremap_nocache(res->start, resource_size(res));
+ if (!priv->base)
+ goto err1;
+
error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv);
if (error) {
dev_err(&pdev->dev, "unable to setup clocks\n");
goto err1;
}
- priv->base = ioremap_nocache(res->start, (res->end - res->start) + 1);
-
for (i = 0; i < j; i++) {
+ struct fb_var_screeninfo *var;
+ struct fb_videomode *lcd_cfg;
cfg = &priv->ch[i].cfg;
priv->ch[i].info = framebuffer_alloc(0, &pdev->dev);
@@ -1039,22 +1114,33 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
}
info = priv->ch[i].info;
+ var = &info->var;
+ lcd_cfg = &cfg->lcd_cfg;
info->fbops = &sh_mobile_lcdc_ops;
- info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres;
- info->var.yres = cfg->lcd_cfg.yres;
+ var->xres = var->xres_virtual = lcd_cfg->xres;
+ var->yres = lcd_cfg->yres;
/* Default Y virtual resolution is 2x panel size */
- info->var.yres_virtual = info->var.yres * 2;
- info->var.width = cfg->lcd_size_cfg.width;
- info->var.height = cfg->lcd_size_cfg.height;
- info->var.activate = FB_ACTIVATE_NOW;
- error = sh_mobile_lcdc_set_bpp(&info->var, cfg->bpp);
+ var->yres_virtual = var->yres * 2;
+ var->width = cfg->lcd_size_cfg.width;
+ var->height = cfg->lcd_size_cfg.height;
+ var->activate = FB_ACTIVATE_NOW;
+ var->left_margin = lcd_cfg->left_margin;
+ var->right_margin = lcd_cfg->right_margin;
+ var->upper_margin = lcd_cfg->upper_margin;
+ var->lower_margin = lcd_cfg->lower_margin;
+ var->hsync_len = lcd_cfg->hsync_len;
+ var->vsync_len = lcd_cfg->vsync_len;
+ var->sync = lcd_cfg->sync;
+ var->pixclock = lcd_cfg->pixclock;
+
+ error = sh_mobile_lcdc_set_bpp(var, cfg->bpp);
if (error)
break;
info->fix = sh_mobile_lcdc_fix;
- info->fix.line_length = cfg->lcd_cfg.xres * (cfg->bpp / 8);
+ info->fix.line_length = lcd_cfg->xres * (cfg->bpp / 8);
info->fix.smem_len = info->fix.line_length *
- info->var.yres_virtual;
+ var->yres_virtual;
buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len,
&priv->ch[i].dma_handle, GFP_KERNEL);
@@ -1119,10 +1205,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
ch->cfg.bpp);
/* deferred io mode: disable clock to save power */
- if (info->fbdefio)
+ if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED)
sh_mobile_lcdc_clk_off(priv);
}
+ /* Failure ignored */
+ priv->notifier.notifier_call = sh_mobile_lcdc_notify;
+ fb_register_client(&priv->notifier);
+
return 0;
err1:
sh_mobile_lcdc_remove(pdev);
@@ -1136,6 +1226,8 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
struct fb_info *info;
int i;
+ fb_unregister_client(&priv->notifier);
+
for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
if (priv->ch[i].info && priv->ch[i].info->dev)
unregister_framebuffer(priv->ch[i].info);
diff --git a/drivers/video/sunxvr1000.c b/drivers/video/sunxvr1000.c
index 7288934c0d49..5dbe06af2226 100644
--- a/drivers/video/sunxvr1000.c
+++ b/drivers/video/sunxvr1000.c
@@ -111,7 +111,7 @@ static int __devinit gfb_set_fbinfo(struct gfb_info *gp)
return 0;
}
-static int __devinit gfb_probe(struct of_device *op,
+static int __devinit gfb_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -172,7 +172,7 @@ err_out:
return err;
}
-static int __devexit gfb_remove(struct of_device *op)
+static int __devexit gfb_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct gfb_info *gp = info->par;
diff --git a/drivers/video/tcx.c b/drivers/video/tcx.c
index f375e0db6776..77ad27955cf0 100644
--- a/drivers/video/tcx.c
+++ b/drivers/video/tcx.c
@@ -342,7 +342,7 @@ tcx_init_fix(struct fb_info *info, int linebytes)
info->fix.accel = FB_ACCEL_SUN_TCX;
}
-static void tcx_unmap_regs(struct of_device *op, struct fb_info *info,
+static void tcx_unmap_regs(struct platform_device *op, struct fb_info *info,
struct tcx_par *par)
{
if (par->tec)
@@ -362,7 +362,7 @@ static void tcx_unmap_regs(struct of_device *op, struct fb_info *info,
info->screen_base, info->fix.smem_len);
}
-static int __devinit tcx_probe(struct of_device *op,
+static int __devinit tcx_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *dp = op->dev.of_node;
@@ -486,7 +486,7 @@ out_err:
return err;
}
-static int __devexit tcx_remove(struct of_device *op)
+static int __devexit tcx_remove(struct platform_device *op)
{
struct fb_info *info = dev_get_drvdata(&op->dev);
struct tcx_par *par = info->par;
diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c
index 29b5daacc217..0c9ce88e95e8 100644
--- a/drivers/video/xilinxfb.c
+++ b/drivers/video/xilinxfb.c
@@ -397,7 +397,7 @@ static int xilinxfb_release(struct device *dev)
*/
static int __devinit
-xilinxfb_of_probe(struct of_device *op, const struct of_device_id *match)
+xilinxfb_of_probe(struct platform_device *op, const struct of_device_id *match)
{
const u32 *prop;
u32 *p;
@@ -477,7 +477,7 @@ xilinxfb_of_probe(struct of_device *op, const struct of_device_id *match)
return -ENODEV;
}
-static int __devexit xilinxfb_of_remove(struct of_device *op)
+static int __devexit xilinxfb_of_remove(struct platform_device *op)
{
return xilinxfb_release(&op->dev);
}
diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c
index 30a2512fd52e..566343b3c131 100644
--- a/drivers/watchdog/cpwd.c
+++ b/drivers/watchdog/cpwd.c
@@ -526,7 +526,7 @@ static const struct file_operations cpwd_fops = {
.release = cpwd_release,
};
-static int __devinit cpwd_probe(struct of_device *op,
+static int __devinit cpwd_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct device_node *options;
@@ -639,7 +639,7 @@ out_free:
goto out;
}
-static int __devexit cpwd_remove(struct of_device *op)
+static int __devexit cpwd_remove(struct platform_device *op)
{
struct cpwd *p = dev_get_drvdata(&op->dev);
int i;
diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c
index 1df284f9c2a1..9c21d19043a6 100644
--- a/drivers/watchdog/gef_wdt.c
+++ b/drivers/watchdog/gef_wdt.c
@@ -260,7 +260,7 @@ static struct miscdevice gef_wdt_miscdev = {
};
-static int __devinit gef_wdt_probe(struct of_device *dev,
+static int __devinit gef_wdt_probe(struct platform_device *dev,
const struct of_device_id *match)
{
int timeout = 10;
diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c
index 4cda64dd309c..8fa213cdb499 100644
--- a/drivers/watchdog/mpc8xxx_wdt.c
+++ b/drivers/watchdog/mpc8xxx_wdt.c
@@ -185,7 +185,7 @@ static struct miscdevice mpc8xxx_wdt_miscdev = {
.fops = &mpc8xxx_wdt_fops,
};
-static int __devinit mpc8xxx_wdt_probe(struct of_device *ofdev,
+static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
int ret;
@@ -238,7 +238,7 @@ err_unmap:
return ret;
}
-static int __devexit mpc8xxx_wdt_remove(struct of_device *ofdev)
+static int __devexit mpc8xxx_wdt_remove(struct platform_device *ofdev)
{
mpc8xxx_wdt_pr_warn("watchdog removed");
del_timer_sync(&wdt_timer);
diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c
index 4082b4ace1fc..3faee1ae64bd 100644
--- a/drivers/watchdog/riowd.c
+++ b/drivers/watchdog/riowd.c
@@ -172,7 +172,7 @@ static struct miscdevice riowd_miscdev = {
.fops = &riowd_fops
};
-static int __devinit riowd_probe(struct of_device *op,
+static int __devinit riowd_probe(struct platform_device *op,
const struct of_device_id *match)
{
struct riowd *p;
@@ -219,7 +219,7 @@ out:
return err;
}
-static int __devexit riowd_remove(struct of_device *op)
+static int __devexit riowd_remove(struct platform_device *op)
{
struct riowd *p = dev_get_drvdata(&op->dev);
diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig
index 0a8826936639..60d71e9abe9f 100644
--- a/drivers/xen/Kconfig
+++ b/drivers/xen/Kconfig
@@ -71,4 +71,9 @@ config XEN_PLATFORM_PCI
initializing xenbus and grant_table when running in a Xen HVM
domain. As a consequence this driver is required to run any Xen PV
frontend on Xen HVM.
+
+config SWIOTLB_XEN
+ def_bool y
+ depends on SWIOTLB
+
endmenu
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index e392fb776af3..fcaf838f54be 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_XEN_DEV_EVTCHN) += evtchn.o
obj-$(CONFIG_XENFS) += xenfs/
obj-$(CONFIG_XEN_SYS_HYPERVISOR) += sys-hypervisor.o
obj-$(CONFIG_XEN_PLATFORM_PCI) += platform-pci.o
+obj-$(CONFIG_SWIOTLB_XEN) += swiotlb-xen.o
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index 1a0d8c2a0354..500290b150bb 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -85,13 +85,6 @@ static struct sys_device balloon_sysdev;
static int register_balloon(struct sys_device *sysdev);
-/*
- * Protects atomic reservation decrease/increase against concurrent increases.
- * Also protects non-atomic updates of current_pages and driver_pages, and
- * balloon lists.
- */
-static DEFINE_SPINLOCK(balloon_lock);
-
static struct balloon_stats balloon_stats;
/* We increase/decrease in batches which fit in a page */
@@ -210,7 +203,7 @@ static int increase_reservation(unsigned long nr_pages)
if (nr_pages > ARRAY_SIZE(frame_list))
nr_pages = ARRAY_SIZE(frame_list);
- spin_lock_irqsave(&balloon_lock, flags);
+ spin_lock_irqsave(&xen_reservation_lock, flags);
page = balloon_first_page();
for (i = 0; i < nr_pages; i++) {
@@ -254,7 +247,7 @@ static int increase_reservation(unsigned long nr_pages)
balloon_stats.current_pages += rc;
out:
- spin_unlock_irqrestore(&balloon_lock, flags);
+ spin_unlock_irqrestore(&xen_reservation_lock, flags);
return rc < 0 ? rc : rc != nr_pages;
}
@@ -299,7 +292,7 @@ static int decrease_reservation(unsigned long nr_pages)
kmap_flush_unused();
flush_tlb_all();
- spin_lock_irqsave(&balloon_lock, flags);
+ spin_lock_irqsave(&xen_reservation_lock, flags);
/* No more mappings: invalidate P2M and add to balloon. */
for (i = 0; i < nr_pages; i++) {
@@ -315,7 +308,7 @@ static int decrease_reservation(unsigned long nr_pages)
balloon_stats.current_pages -= nr_pages;
- spin_unlock_irqrestore(&balloon_lock, flags);
+ spin_unlock_irqrestore(&xen_reservation_lock, flags);
return need_sleep;
}
diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c
new file mode 100644
index 000000000000..54469c3eeacd
--- /dev/null
+++ b/drivers/xen/swiotlb-xen.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright 2010
+ * by Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+ *
+ * This code provides a IOMMU for Xen PV guests with PCI passthrough.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 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.
+ *
+ * PV guests under Xen are running in an non-contiguous memory architecture.
+ *
+ * When PCI pass-through is utilized, this necessitates an IOMMU for
+ * translating bus (DMA) to virtual and vice-versa and also providing a
+ * mechanism to have contiguous pages for device drivers operations (say DMA
+ * operations).
+ *
+ * Specifically, under Xen the Linux idea of pages is an illusion. It
+ * assumes that pages start at zero and go up to the available memory. To
+ * help with that, the Linux Xen MMU provides a lookup mechanism to
+ * translate the page frame numbers (PFN) to machine frame numbers (MFN)
+ * and vice-versa. The MFN are the "real" frame numbers. Furthermore
+ * memory is not contiguous. Xen hypervisor stitches memory for guests
+ * from different pools, which means there is no guarantee that PFN==MFN
+ * and PFN+1==MFN+1. Lastly with Xen 4.0, pages (in debug mode) are
+ * allocated in descending order (high to low), meaning the guest might
+ * never get any MFN's under the 4GB mark.
+ *
+ */
+
+#include <linux/bootmem.h>
+#include <linux/dma-mapping.h>
+#include <xen/swiotlb-xen.h>
+#include <xen/page.h>
+#include <xen/xen-ops.h>
+/*
+ * Used to do a quick range check in swiotlb_tbl_unmap_single and
+ * swiotlb_tbl_sync_single_*, to see if the memory was in fact allocated by this
+ * API.
+ */
+
+static char *xen_io_tlb_start, *xen_io_tlb_end;
+static unsigned long xen_io_tlb_nslabs;
+/*
+ * Quick lookup value of the bus address of the IOTLB.
+ */
+
+u64 start_dma_addr;
+
+static dma_addr_t xen_phys_to_bus(phys_addr_t paddr)
+{
+ return phys_to_machine(XPADDR(paddr)).maddr;;
+}
+
+static phys_addr_t xen_bus_to_phys(dma_addr_t baddr)
+{
+ return machine_to_phys(XMADDR(baddr)).paddr;
+}
+
+static dma_addr_t xen_virt_to_bus(void *address)
+{
+ return xen_phys_to_bus(virt_to_phys(address));
+}
+
+static int check_pages_physically_contiguous(unsigned long pfn,
+ unsigned int offset,
+ size_t length)
+{
+ unsigned long next_mfn;
+ int i;
+ int nr_pages;
+
+ next_mfn = pfn_to_mfn(pfn);
+ nr_pages = (offset + length + PAGE_SIZE-1) >> PAGE_SHIFT;
+
+ for (i = 1; i < nr_pages; i++) {
+ if (pfn_to_mfn(++pfn) != ++next_mfn)
+ return 0;
+ }
+ return 1;
+}
+
+static int range_straddles_page_boundary(phys_addr_t p, size_t size)
+{
+ unsigned long pfn = PFN_DOWN(p);
+ unsigned int offset = p & ~PAGE_MASK;
+
+ if (offset + size <= PAGE_SIZE)
+ return 0;
+ if (check_pages_physically_contiguous(pfn, offset, size))
+ return 0;
+ return 1;
+}
+
+static int is_xen_swiotlb_buffer(dma_addr_t dma_addr)
+{
+ unsigned long mfn = PFN_DOWN(dma_addr);
+ unsigned long pfn = mfn_to_local_pfn(mfn);
+ phys_addr_t paddr;
+
+ /* If the address is outside our domain, it CAN
+ * have the same virtual address as another address
+ * in our domain. Therefore _only_ check address within our domain.
+ */
+ if (pfn_valid(pfn)) {
+ paddr = PFN_PHYS(pfn);
+ return paddr >= virt_to_phys(xen_io_tlb_start) &&
+ paddr < virt_to_phys(xen_io_tlb_end);
+ }
+ return 0;
+}
+
+static int max_dma_bits = 32;
+
+static int
+xen_swiotlb_fixup(void *buf, size_t size, unsigned long nslabs)
+{
+ int i, rc;
+ int dma_bits;
+
+ dma_bits = get_order(IO_TLB_SEGSIZE << IO_TLB_SHIFT) + PAGE_SHIFT;
+
+ i = 0;
+ do {
+ int slabs = min(nslabs - i, (unsigned long)IO_TLB_SEGSIZE);
+
+ do {
+ rc = xen_create_contiguous_region(
+ (unsigned long)buf + (i << IO_TLB_SHIFT),
+ get_order(slabs << IO_TLB_SHIFT),
+ dma_bits);
+ } while (rc && dma_bits++ < max_dma_bits);
+ if (rc)
+ return rc;
+
+ i += slabs;
+ } while (i < nslabs);
+ return 0;
+}
+
+void __init xen_swiotlb_init(int verbose)
+{
+ unsigned long bytes;
+ int rc;
+
+ xen_io_tlb_nslabs = (64 * 1024 * 1024 >> IO_TLB_SHIFT);
+ xen_io_tlb_nslabs = ALIGN(xen_io_tlb_nslabs, IO_TLB_SEGSIZE);
+
+ bytes = xen_io_tlb_nslabs << IO_TLB_SHIFT;
+
+ /*
+ * Get IO TLB memory from any location.
+ */
+ xen_io_tlb_start = alloc_bootmem(bytes);
+ if (!xen_io_tlb_start)
+ panic("Cannot allocate SWIOTLB buffer");
+
+ xen_io_tlb_end = xen_io_tlb_start + bytes;
+ /*
+ * And replace that memory with pages under 4GB.
+ */
+ rc = xen_swiotlb_fixup(xen_io_tlb_start,
+ bytes,
+ xen_io_tlb_nslabs);
+ if (rc)
+ goto error;
+
+ start_dma_addr = xen_virt_to_bus(xen_io_tlb_start);
+ swiotlb_init_with_tbl(xen_io_tlb_start, xen_io_tlb_nslabs, verbose);
+
+ return;
+error:
+ panic("DMA(%d): Failed to exchange pages allocated for DMA with Xen! "\
+ "We either don't have the permission or you do not have enough"\
+ "free memory under 4GB!\n", rc);
+}
+
+void *
+xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flags)
+{
+ void *ret;
+ int order = get_order(size);
+ u64 dma_mask = DMA_BIT_MASK(32);
+ unsigned long vstart;
+
+ /*
+ * Ignore region specifiers - the kernel's ideas of
+ * pseudo-phys memory layout has nothing to do with the
+ * machine physical layout. We can't allocate highmem
+ * because we can't return a pointer to it.
+ */
+ flags &= ~(__GFP_DMA | __GFP_HIGHMEM);
+
+ if (dma_alloc_from_coherent(hwdev, size, dma_handle, &ret))
+ return ret;
+
+ vstart = __get_free_pages(flags, order);
+ ret = (void *)vstart;
+
+ if (hwdev && hwdev->coherent_dma_mask)
+ dma_mask = dma_alloc_coherent_mask(hwdev, flags);
+
+ if (ret) {
+ if (xen_create_contiguous_region(vstart, order,
+ fls64(dma_mask)) != 0) {
+ free_pages(vstart, order);
+ return NULL;
+ }
+ memset(ret, 0, size);
+ *dma_handle = virt_to_machine(ret).maddr;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_alloc_coherent);
+
+void
+xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr,
+ dma_addr_t dev_addr)
+{
+ int order = get_order(size);
+
+ if (dma_release_from_coherent(hwdev, order, vaddr))
+ return;
+
+ xen_destroy_contiguous_region((unsigned long)vaddr, order);
+ free_pages((unsigned long)vaddr, order);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_free_coherent);
+
+
+/*
+ * Map a single buffer of the indicated size for DMA in streaming mode. The
+ * physical address to use is returned.
+ *
+ * Once the device is given the dma address, the device owns this memory until
+ * either xen_swiotlb_unmap_page or xen_swiotlb_dma_sync_single is performed.
+ */
+dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size,
+ enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ phys_addr_t phys = page_to_phys(page) + offset;
+ dma_addr_t dev_addr = xen_phys_to_bus(phys);
+ void *map;
+
+ BUG_ON(dir == DMA_NONE);
+ /*
+ * If the address happens to be in the device's DMA window,
+ * we can safely return the device addr and not worry about bounce
+ * buffering it.
+ */
+ if (dma_capable(dev, dev_addr, size) &&
+ !range_straddles_page_boundary(phys, size) && !swiotlb_force)
+ return dev_addr;
+
+ /*
+ * Oh well, have to allocate and map a bounce buffer.
+ */
+ map = swiotlb_tbl_map_single(dev, start_dma_addr, phys, size, dir);
+ if (!map)
+ return DMA_ERROR_CODE;
+
+ dev_addr = xen_virt_to_bus(map);
+
+ /*
+ * Ensure that the address returned is DMA'ble
+ */
+ if (!dma_capable(dev, dev_addr, size))
+ panic("map_single: bounce buffer is not DMA'ble");
+
+ return dev_addr;
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_map_page);
+
+/*
+ * Unmap a single streaming mode DMA translation. The dma_addr and size must
+ * match what was provided for in a previous xen_swiotlb_map_page call. All
+ * other usages are undefined.
+ *
+ * After this call, reads by the cpu to the buffer are guaranteed to see
+ * whatever the device wrote there.
+ */
+static void xen_unmap_single(struct device *hwdev, dma_addr_t dev_addr,
+ size_t size, enum dma_data_direction dir)
+{
+ phys_addr_t paddr = xen_bus_to_phys(dev_addr);
+
+ BUG_ON(dir == DMA_NONE);
+
+ /* NOTE: We use dev_addr here, not paddr! */
+ if (is_xen_swiotlb_buffer(dev_addr)) {
+ swiotlb_tbl_unmap_single(hwdev, phys_to_virt(paddr), size, dir);
+ return;
+ }
+
+ if (dir != DMA_FROM_DEVICE)
+ return;
+
+ /*
+ * phys_to_virt doesn't work with hihgmem page but we could
+ * call dma_mark_clean() with hihgmem page here. However, we
+ * are fine since dma_mark_clean() is null on POWERPC. We can
+ * make dma_mark_clean() take a physical address if necessary.
+ */
+ dma_mark_clean(phys_to_virt(paddr), size);
+}
+
+void xen_swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr,
+ size_t size, enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ xen_unmap_single(hwdev, dev_addr, size, dir);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_unmap_page);
+
+/*
+ * Make physical memory consistent for a single streaming mode DMA translation
+ * after a transfer.
+ *
+ * If you perform a xen_swiotlb_map_page() but wish to interrogate the buffer
+ * using the cpu, yet do not wish to teardown the dma mapping, you must
+ * call this function before doing so. At the next point you give the dma
+ * address back to the card, you must first perform a
+ * xen_swiotlb_dma_sync_for_device, and then the device again owns the buffer
+ */
+static void
+xen_swiotlb_sync_single(struct device *hwdev, dma_addr_t dev_addr,
+ size_t size, enum dma_data_direction dir,
+ enum dma_sync_target target)
+{
+ phys_addr_t paddr = xen_bus_to_phys(dev_addr);
+
+ BUG_ON(dir == DMA_NONE);
+
+ /* NOTE: We use dev_addr here, not paddr! */
+ if (is_xen_swiotlb_buffer(dev_addr)) {
+ swiotlb_tbl_sync_single(hwdev, phys_to_virt(paddr), size, dir,
+ target);
+ return;
+ }
+
+ if (dir != DMA_FROM_DEVICE)
+ return;
+
+ dma_mark_clean(phys_to_virt(paddr), size);
+}
+
+void
+xen_swiotlb_sync_single_for_cpu(struct device *hwdev, dma_addr_t dev_addr,
+ size_t size, enum dma_data_direction dir)
+{
+ xen_swiotlb_sync_single(hwdev, dev_addr, size, dir, SYNC_FOR_CPU);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_sync_single_for_cpu);
+
+void
+xen_swiotlb_sync_single_for_device(struct device *hwdev, dma_addr_t dev_addr,
+ size_t size, enum dma_data_direction dir)
+{
+ xen_swiotlb_sync_single(hwdev, dev_addr, size, dir, SYNC_FOR_DEVICE);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_sync_single_for_device);
+
+/*
+ * Map a set of buffers described by scatterlist in streaming mode for DMA.
+ * This is the scatter-gather version of the above xen_swiotlb_map_page
+ * interface. Here the scatter gather list elements are each tagged with the
+ * appropriate dma address and length. They are obtained via
+ * sg_dma_{address,length}(SG).
+ *
+ * NOTE: An implementation may be able to use a smaller number of
+ * DMA address/length pairs than there are SG table elements.
+ * (for example via virtual mapping capabilities)
+ * The routine returns the number of addr/length pairs actually
+ * used, at most nents.
+ *
+ * Device ownership issues as mentioned above for xen_swiotlb_map_page are the
+ * same here.
+ */
+int
+xen_swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl,
+ int nelems, enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ struct scatterlist *sg;
+ int i;
+
+ BUG_ON(dir == DMA_NONE);
+
+ for_each_sg(sgl, sg, nelems, i) {
+ phys_addr_t paddr = sg_phys(sg);
+ dma_addr_t dev_addr = xen_phys_to_bus(paddr);
+
+ if (swiotlb_force ||
+ !dma_capable(hwdev, dev_addr, sg->length) ||
+ range_straddles_page_boundary(paddr, sg->length)) {
+ void *map = swiotlb_tbl_map_single(hwdev,
+ start_dma_addr,
+ sg_phys(sg),
+ sg->length, dir);
+ if (!map) {
+ /* Don't panic here, we expect map_sg users
+ to do proper error handling. */
+ xen_swiotlb_unmap_sg_attrs(hwdev, sgl, i, dir,
+ attrs);
+ sgl[0].dma_length = 0;
+ return DMA_ERROR_CODE;
+ }
+ sg->dma_address = xen_virt_to_bus(map);
+ } else
+ sg->dma_address = dev_addr;
+ sg->dma_length = sg->length;
+ }
+ return nelems;
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_map_sg_attrs);
+
+int
+xen_swiotlb_map_sg(struct device *hwdev, struct scatterlist *sgl, int nelems,
+ enum dma_data_direction dir)
+{
+ return xen_swiotlb_map_sg_attrs(hwdev, sgl, nelems, dir, NULL);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_map_sg);
+
+/*
+ * Unmap a set of streaming mode DMA translations. Again, cpu read rules
+ * concerning calls here are the same as for swiotlb_unmap_page() above.
+ */
+void
+xen_swiotlb_unmap_sg_attrs(struct device *hwdev, struct scatterlist *sgl,
+ int nelems, enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ struct scatterlist *sg;
+ int i;
+
+ BUG_ON(dir == DMA_NONE);
+
+ for_each_sg(sgl, sg, nelems, i)
+ xen_unmap_single(hwdev, sg->dma_address, sg->dma_length, dir);
+
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_unmap_sg_attrs);
+
+void
+xen_swiotlb_unmap_sg(struct device *hwdev, struct scatterlist *sgl, int nelems,
+ enum dma_data_direction dir)
+{
+ return xen_swiotlb_unmap_sg_attrs(hwdev, sgl, nelems, dir, NULL);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_unmap_sg);
+
+/*
+ * Make physical memory consistent for a set of streaming mode DMA translations
+ * after a transfer.
+ *
+ * The same as swiotlb_sync_single_* but for a scatter-gather list, same rules
+ * and usage.
+ */
+static void
+xen_swiotlb_sync_sg(struct device *hwdev, struct scatterlist *sgl,
+ int nelems, enum dma_data_direction dir,
+ enum dma_sync_target target)
+{
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgl, sg, nelems, i)
+ xen_swiotlb_sync_single(hwdev, sg->dma_address,
+ sg->dma_length, dir, target);
+}
+
+void
+xen_swiotlb_sync_sg_for_cpu(struct device *hwdev, struct scatterlist *sg,
+ int nelems, enum dma_data_direction dir)
+{
+ xen_swiotlb_sync_sg(hwdev, sg, nelems, dir, SYNC_FOR_CPU);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_sync_sg_for_cpu);
+
+void
+xen_swiotlb_sync_sg_for_device(struct device *hwdev, struct scatterlist *sg,
+ int nelems, enum dma_data_direction dir)
+{
+ xen_swiotlb_sync_sg(hwdev, sg, nelems, dir, SYNC_FOR_DEVICE);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_sync_sg_for_device);
+
+int
+xen_swiotlb_dma_mapping_error(struct device *hwdev, dma_addr_t dma_addr)
+{
+ return !dma_addr;
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_dma_mapping_error);
+
+/*
+ * Return whether the given device DMA address mask can be supported
+ * properly. For example, if your device can only drive the low 24-bits
+ * during bus mastering, then you would pass 0x00ffffff as the mask to
+ * this function.
+ */
+int
+xen_swiotlb_dma_supported(struct device *hwdev, u64 mask)
+{
+ return xen_virt_to_bus(xen_io_tlb_end - 1) <= mask;
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_dma_supported);